1
\$\begingroup\$

Hope you bear with me, I'm a beginner! Maybe I should've paid better attention to the tutorials but they all seem to use public variables/game objects that we can then drag into using the Unity Editor. However, if I have a private Game Object, how can I reference that programatically? I have the following:

private GameObject menuCanvas;

void Start()
{
menuCanvas = GameObject.Find("menuCanvas");
menuCanvas.SetActive(false);
}

Where the menuCanvas is named such in the Unity editor. However, I usually get the following error:

"The variable menuCanvas of myScript has not been assigned."

myScript is attached to a different GameObject in the scene, but not attached to menuCanvas. What am I doing wrong? Thanks for the help!

Edit: another question - which is generally better to use?

Edit 2: Hi guys, I figured it out. I had to move some code around to the Awake() function, so it looked like this:

private GameObject menuCanvas;

void Awake()
{
menuCanvas = GameObject.Find("menuCanvas");
}

void Start()
{
menuCanvas.SetActive(false);
}

And that worked. I realized that myScript was attached to multiple objects, which would cause the menuCanvas to be set inactive more than once (but it could not be referenced inactive).

S. Tarık Çetin's answer worked just as well! Thanks for the help

Edit 3: As Jesse Williams answered, an internal definition keeps the variable hidden from the editor, but it can still be assigned via code.

\$\endgroup\$
  • \$\begingroup\$ In C#, variables are private by default, so you don't actually need to explicitly type it. Also, With GameObject.Find, there are two things to keep in mind. The first is that it will search for exactly the name you have put into it. If your object is called anything other than "menuCanvas" (case sensitive), it will return null, which would cause your issue. The other is that GameObject.Find only finds objects that are active. I assume it is active to begin with, since you are setting it to false after finding, but if it is not active when the scene loads, it would also fail to find the object. \$\endgroup\$ – DisturbedNeo Jul 7 '16 at 11:35
  • \$\begingroup\$ Thank you for your suggestions! I made sure of both of these but GameObject.Find is still returning a null reference ("Object Reference not set to an instance of an object"). \$\endgroup\$ – squeegene Jul 8 '16 at 3:16
1
\$\begingroup\$

Normally Unity serializes only public fields, to prevent confusion in inspector. But you can force Unity to serialize private fields by adding [SerializeField] before the decleration.

So this should do the trick:

[SerializeField] private GameObject menuCanvas;

Then you can assign it in inspector.


For the question in your edit: It is always better to use private variables unless you have a reason to do otherwise. If you want to expose something to public use, best option is to use properties.

\$\endgroup\$
0
\$\begingroup\$

If you want to have Unity serialize your data you can use the approach in S. Tarık Çetin answer and mark private members with [SerializeField].

EDIT: Actually this really exposes the variable as S. Tarık Çetin said. So you are good with this approach. But I think this was not your main concern.

What you are trying can not work that way because the command GameObject.Find(string name) finds a game object with this name and returns it (see Unity Documentation for GameObject.Find). So you have to set the name via the inspector or the "name" variable that every GameObject has.

EDIT: Not relevant anymore, but this approach gives you the ability to use properties instead of variables in the inspector. A long with a lot more like custom user interfaces (buttons etc.).

If you want to access a private variable via the inspector you have to write a separate editor class: Unity Documentation for Custom Editors.

For your example:

using UnityEngine;

public class ExampleClass : MonoBehaviour
{
     [SerializeField]
     private GameObject menuCanvas;

     public GameObject MenuCanvas
     {
         get { return menuCanvas; }
         set { menuCanvas = value; }
     }

     ...
}

And then your second class:

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(ExampleClass))]
public class ExampleClassEditor : Editor
{
    public override void OnInspectorGUI()
    {
        // render the rest of the GUI
        base.OnInspectorGUI();

        // cast the internal reference
        var obj = (ExampleClass)target;

        // create an entry in the inspector
        obj.MenuCanvas = (GameObject)EditorGUILayout.ObjectField("Menu Canvas", obj.MenuCanvas, typeof(GameObject), true);
}

The editor class has to be in a subfolder namend "Editor".

In tutorials you usually just want to get something going so this dual-class approach is dropped for just a public.

\$\endgroup\$
  • \$\begingroup\$ If you want to access a private variable via the inspector you have to write a separate editor class No you don't. [SerializeField] already exposes them. The purpose of property is to access it from other classes. \$\endgroup\$ – S. Tarık Çetin Jul 7 '16 at 7:41
  • \$\begingroup\$ No it does not, at least not in Unity 5. I just tested it because your answer seemed odd to me. \$\endgroup\$ – Tristan Kreuziger Jul 7 '16 at 7:43
  • \$\begingroup\$ I suggest you to test again, I just tested with Unity 5.3.4f1 and it works as I said. \$\endgroup\$ – S. Tarık Çetin Jul 7 '16 at 8:18
  • \$\begingroup\$ You are right, I'm sorry. Maybe that is not relevant anymore since Unity 5. I will edit the answer. \$\endgroup\$ – Tristan Kreuziger Jul 7 '16 at 10:01
0
\$\begingroup\$

I may be missing the point of the question, but if scope is an issue, and you need to access the variable from another script or another scope, but don't want the inspector objects visible, you can initialize it as internal rather than public. It will behave as a public variable, but not show in the inspector.

\$\endgroup\$
  • \$\begingroup\$ Scope is part of the issue! I'm trying to get a reference to the GameObject without the use of the inspector. GameObject.Find returns a null reference, and I've checked that the spelling is correct and that the object is active. Sorry for the confusion! \$\endgroup\$ – squeegene Jul 8 '16 at 3:15
  • \$\begingroup\$ You'll want it public or internal then. Even if it's set to public, you aren't required to set anything via the inspector - it can still be done via code. Internal is the way to go if you just want to hide it. \$\endgroup\$ – Jesse Williams Jul 8 '16 at 22:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.