How to access and disable a GameObject from a different script - Unity2D - c#

I am currently working on a game, and while designing a Main Menu for the game, I need to be able to access different GameObjects and enable/disable them for the user to access different parts of the menu. The different parts are held under their respective parent GameObjects, for example, all the GameObjects related to the "Options Menu" will be under a single empty GameObject called "Options".
Here is the code pertaining to a script that holds the references to all the different parent objects and the methods to enable them:
public class MenuSwitch : MonoBehaviour
{
public GameObject Main;
public GameObject Options;
public GameObject About;
public GameObject Load;
public void MainMenu()
{
Main.SetActive(true);
Options.SetActive(false);
About.SetActive(false);
Load.SetActive(false);
}
public void OptionsMenu()
{
Main.SetActive(false);
Options.SetActive(true);
About.SetActive(false);
Load.SetActive(false);
}
However. the problem is the navigation of the Menu has to be done by the player by typing 'commands'. For example, they would type '/options' to go to the options menu. This functionality will remain throughout the game, so I made another script to handle player commands from the in-game console. Now how do I call the methods from MenuSwitch, because making a MenuSwitch object inside my console script does not seem to work, it gives an error: "Object reference not set to an instance of an object".
Here is the script handling the commands specified in the main menu:
// Commands
string[] MainMenuCommands = { "./start", "./load", "./about", "./options", "./end" };
private void Update()
{
if (Input.GetKeyDown(KeyCode.Return))
{
// Reset the console
playerInput = console.text;
console.text = "";
// Main Menu Input
if (CheckCommand(playerInput) == true)
{
RunCommand(playerInput, scene);
}
}
}
void RunCommand(string _command, int _scene)
{
// Main Menu commands
if (_scene == 0)
{
if (_command == MainMenuCommands[0]) { StartCoroutine(MM_Start()); }
else if (_command == MainMenuCommands[1]) { MM_Load(); }
else if (_command == MainMenuCommands[2]) { MM_About(); }
else if (_command == MainMenuCommands[3]) { MM_Options(); }
else if (_command == MainMenuCommands[4]) { MM_End(); }
}
}
The MM methods are empty currently, that is where the enabling/disabling will take place. How do I enable or disable the GameObjects inside the MenuSwitch script? The objects are referenced in that script via the Unity inspector.

You could create an empty object and call it MenuManager, attach the MenuScript to it. Then in a script you want to call MenuSwitch functions from declare a class member like so:
public MenuSwitch menuManager;
Drag and drop the MenuManager empty gameObject in the inspector and then you can easily use all the public functions from MenuSwitch script by just calling them from the menuManager variable like so:
menuManager.MainMenu();
menuManager.OptionsMenu();
I hope it was helpful :)

Related

How to assign GameObject to child for serialization

I want to assign each item I create with a GameObject of some sort and then when I create a new object (called Item) it will have the assigned GameObject as a child.
For that I have a class of a scriptable object which holds a public GameObject called "gameObj" within it:
public abstract class ItemObject : ScriptableObject
{
public int id;
public GameObject gameObj;
}
Then, in another class I want to have something of this sort:
public class GroundItem : MonoBehaviour, ISerializationCallbackReceiver
{
public ItemObject item;
public void OnBeforeSerialize()
{
this.gameObject.transform.GetChild(0) = item.gameObj; //WRONG CODE, NEED HELP HERE
}
}
The purpose is to set the gameObj from the given ItemObject.item as the GameObject for the GroundItem.
The purpose is in the end to have lots of scriptable items of all sorts (like bread, sword, stone etc) and each one will have a GameObject assigned to it, and once I create a new GroundItem game object I will simply assign the scriptable object item and its child will have the game object itself (which includes all the visuals, special scripts etc).
For reference, in the following link the person is doing this from minute 4 to minute 6, but with a sprite instead of a game object.
Anyone knows how it should be written? Is it even possible?
You would probably mean
public class GroundItem : MonoBehaviour, ISerializationCallbackReceiver
{
public ItemObject item;
public void OnBeforeSerialize()
{
if(!item) return;
if(!item.gameObj) return;
// make according object a child of this object
item.gameObj.transform.parent = transform;
// make it the firs child
item.gameObj.transform.SetSiblingIndex(0);
}
}
However, in general ScriptableObjects are assets and GameObjects are instances in the scene hierarchy -> you can't simply reference scene objects in assets, doing so anyway might lead to unexpected behavior.
Is there a good reason why this has to be done in ISerializationCallbackReceiver?
I think what you actually rather want to achieve would be e.g.
public class GroundItem : MonoBehaviour
{
public ItemObject item;
#if UNITY_EDITOR
[HideInInspector]
[SerializeField]
private ItemObject _lastItem;
// This is called whenever something is changed in this objects Inspector
// like e.g. item ;)
private void OnValidate()
{
// Delay the call in order to not get warnings for SendMessage
UnityEditor.EditorApplication.delayCall += DelayedOnValidate;
}
private void DelayedOnValidate()
{
// remove the callback since we want to be sure it is always added only once
UnityEditor.EditorApplication.delayCall -= DelayedOnValidate;
// if item hasn't changed nothing to do
if (item == _lastItem) return;
// otherwise first destroy current child if any
if (_lastItem && transform.childCount > 0)
{
if (Application.isPlaying) Destroy(transform.GetChild(0).gameObject);
else DestroyImmediate(transform.GetChild(0).gameObject);
}
// is an item referenced and does it even have a gameObject ?
if (item && item.gameObj)
{
// instantiate the new one as child of this object
var obj = Instantiate(item.gameObj, transform);
// set as first child (if needed)
obj.transform.SetSiblingIndex(0);
if (!Application.isPlaying)
{
// if in edit mode mark this object as dirty so it needs to be saved
UnityEditor.EditorUtility.SetDirty(gameObject);
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
}
}
_lastItem = item;
}
#endif
}
Which would now look like

Can't find objects with Find() in hierarchy

When game starts, script will close the shop.(SetActive(false))
void Start()
{
_shopMenu = GameObject.Find("Shop");
_shopMenu.SetActive(false);
if (_shopSlotContainer == null || _shopItemSlotTemplate == null)
FindItemShopContainerAndItemTemplate();
}
And try to find two objects on scene
private void FindItemShopContainerAndItemTemplate()
{
_shopSlotContainer = transform.Find("itemShopContainer");
if (_shopSlotContainer == null)
Debug.LogError("Container doesn't found");
_shopItemSlotTemplate = transform.Find("shopItem_template");
if (_shopItemSlotTemplate == null)
Debug.LogError("Item template doesn't found");
}
Any other ways to find objects or fix this problem?
(Script attached to "Shop" object)
Transform.Find will only search the immediate children, it will not look further down in the hierarchy. It is also not very performant. You can assign the game objects you want to variables in your script
public class MyScript : MonoBehavior
{
[SerializeField]
private GameObject _itemShopContainer {get; set;}
}
After compile you should be able to see Item Shop Container property on MyScript game object in the editor and be able to drag the proper game object there.
In the code _itemShopContainer will now have your game object.
If you want to assign via code like you are doing switch to using a variant of Transform.GetComponentsInChildren<>(), rather than Transform.Find

How to set a GameObject active on one scene form a button click function on second scene that is loaded additive C#, Unity

I'm new to coding and Unity, I'm working on a simple click style game to learn the basics of both. I've created several scenes: MainMenu, UI, 1st level and 2nd level. After pressing 'Start' in main menu i'm loading UI and 1st level additively.
In UI layer I have a shop UI and other bits that I never want to unload. On the 1st and 2nd level i have the bits that i want to have only on those scenes.
So, what I'm trying to do is when i purchase an item or upgrade for the 1st level i want a GameObject (sprite) to be set as active.
What I've tried to do is to call a function in one script that is attached to GameObject in 1st level from script that is attached to a purchase button in UI scene, but from what I was able to understand from messing with it will set that game object active if it's assigned in the UI scene - so the whole thing is basicly pointless and i can do it much easier.
Code in script attached to GameObject in 1st level scene
public class Work_Button : MonoBehaviour
{
public GameObject hubert12;
public void Huberd()
{
hubert12.SetActive(true);
}
}
Code in script attached to GameObject in UI scene
public class Shop : MonoBehaviour
{
public GameObject buyHubertOnebutton;
public GameObject test;
public void UnlockHubert1()
{
if (Global_Cash.CashCount >= 20)
{
Global_Cash.CashCount -= 20;
buyHubertOnebutton.GetComponent<UnityEngine.UI.Button>().interactable = false;
Work_Button sn = test.GetComponent<Work_Button>();
sn.Huberd();
}
}
}
If you have any remarks anout how i've spit scenes or anything else they will be more than welcome!
Thanks!
There's something you need to be aware of, and that is that you can't reference an object from one scene, in another. You CAN reference the same Prefab though, but that's a slightly different issue.
One way I find effective is to do something like this:
public class Work_Button : MonoBehaviour
{
public GameObject hubert12;
public void Awake ( )
{
Shop.Register ( this );
}
public void Huberd ( )
{
hubert12.SetActive ( true );
}
}
Then you can have a Shop manager that has instance and static components.
public class Shop : MonoBehaviour
{
// Static variables
private static Work_Button _workButton;
// Instance variables
public GameObject buyHubertOnebutton;
public static void Register ( Work_Button workButton )
{
_workButton = workButton;
}
public void UnlockHubert1 ( )
{
if ( Global_Cash.CashCount >= 20 )
{
Global_Cash.CashCount -= 20;
buyHubertOnebutton.GetComponent<UnityEngine.UI.Button> ( ).interactable = false;
if ( _workButton != null )
_workButton.Huberd ( );
}
}
}
This works by having your button register with the shop. And because we're using the static variable here, we don't need to "find" the Shop object. The instance method UnlockHubert1 will check to make sure there is a button registered.
This represents just one of many ways to accomplish this. It's also the most basic of basic implementations. But as of right now I'm fond of this method. Extending this, you could store the registered items in a Dictionary collection, and you could do some cleanup/deregister when a button is destroyed (i.e. OnDestroy ).
The assumption here is that there is only one Work_Button in the scene, otherwise you'll need a way to differentiate them (i.e. a Dictionary with am identifier as the key).

Gameobject.find only returning null even when gameobject is active in heirarchy

I am trying to tranfer from one scene to another and trigger a function once this happens.
So when i press my play game button on my main menu page it loads a function to begin building the world. I have got the function to build a world but once ive attatched it to a button it has stopped working.
So far I believe this is down to me not fully understanding the method of calling a function from another class for it to run normally.
I begin to define my GameObject as:
private static GameOjbect Play;
This doesnt allow me to assign a GameObject to it within the unity editor. Therefore, i went down the method of using:
GameObject Play = GameObject.Find("PlayScreen");
My GameObeject is active in the heirarchy when this function begins but the program still does not function correctly. To test where the program is encountering an issue I used:
Debug.Log(Play);
Which i believed would just output "PlayScreen" to the debug log as this is the gameobject I am searching for, but this only returns "Null" and my program does not progress any further which is creating a wall.
Below is my main menu code:
public class MainMenu : MonoBehaviour
{
public GameObject PlayScene;
public GameObject SettingsScreen;
public void PlayGame()
{
SceneManager.LoadScene("InGame");
Debug.Log("Loading startup...");
WorldBuilder.Awake();
}
}
Below is my WorldBuilding function:
public class WorldBuilder:MonoBehaviour
{
public static GameObject Play;
public static void Awake()
{
Debug.Log("Finding Scene...");
GameObject Play = GameObject.Find("PlayScreen");
Debug.Log(Play);
}
}
How come my program is not finding the GameObject?
I am still new to C# so any sort of help is appreciated. Thankyou.
Don't make the Awake function static. If you do Unity won't call it.
Also, you are creating a local variable when you do GameObject Play = GameObject.Find("PlayScreen");. If you want to keep it in a static variable, you shouldn't do that. See below:
public class WorldBuilder : MonoBehaviour {
public static GameObject Play;
public static void Awake()
{
Debug.Log("Finding Scene...");
WorldBuilder.Play = GameObject.Find("PlayScreen");
Debug.Log(Play);
}
}
Also, remove the call in PlayGame:
public void PlayGame()
{
SceneManager.LoadScene("InGame");
Debug.Log("Loading startup...");
}

Cannot access class variable in member function

In Unity C# script I have a singleton game controller object in which the game variables are stored but I am getting odd behaviour when accessing them in a member function. It prints the initialized value, not the current one. In the update function however it prints the correct value each frame. I summarized the class below. The controller class has a static reference to itself. If you need to know additional details you can ask. I am new to C# and Unity so I might be lacking some obvious answer.
Thanks
public class controller : MonoBehaviour {
public int[] star = new int[64];
void Start(){ /* calls another function to set 0 for each star index */ }
void Update(){ // during gameplay star[0] gets a value of 1
print(star[0]); // prints correct value which is 1
}
public void checkValue(){
print(star[0]); // prints 0 incorrectly which should be 1
}
}
I've set up a little example. I created a button and an empty gameobject GameController. I added this code to the GameController:
using UnityEngine;
using System.Collections;
public class GameController : MonoBehaviour {
public static GameController instance;
[System.NonSerialized] public int[] star = new int[64];
private void Awake()
{
if(instance == null)
instance = this;
else if(instance != this)
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
}
private void Start()
{
StartCoroutine(SetAllTo(1));
}
// for simulating some changes during the game
private IEnumerator SetAllTo(int value)
{
yield return new WaitForSeconds(2.0f);
for(int i = 0; i < star.Length; i++)
star[i] = value;
Debug.Log("Setting done");
}
public void PrintFirst()
{
Debug.Log(star[0]);
}
}
Now I added an OnClick event to the button, dragged the GameController gameobject into the slot and picked PrintFirst.
I start the game and click the button once before the Coroutine log and once after and the console gives the following:
0
Setting Done
1
Edit:
The gameobject for the OnClick event must be in the scene, it can't be a prefab in the assets folder.
It seems that's the call to the start function may also occurs in your external calls before the call for ckeckValue
Try debugging by putting breakpoint in each of these functions and you can understand what is going on.
The start function runs once in the beginning of the game and not again.
For that you have pattern - Singleton
The second thing why to call external function to initialize class member? do it in your default contractor.
- C# default constractor already initialize 0 in int[].
if you still what to run Start from other scope in your code make it public (c# makes variable/methods as private by default)

Categories

Resources