2
\$\begingroup\$

Unity C#. This is a hard question to phrase, an example explains it the best:

Setup

  1. Create a GameObject called Thing.
  2. Create a script ThingScript and attach it to Thing.
public class ThingScript : MonoBehaviour
{
    bool initialized = false;

    void Start()
    {
        Debug.Log("Thing Started");
        initialized = true;
    }

    public void Run()
    {
        Debug.Log("Before Start: initialized= " + initialized);
        if (!initialized) Start();
        Debug.Log("After Start: initialized= " + initialized);
    }
}
  1. Create a Prefab of Thing and delete the instance from your scene.
  2. Create another GameObject called Holder.
  3. Create a script HolderScript and place it on Holder.
public class HolderScript : MonoBehaviour
{
    public GameObject thingPrefab;

    void Start()
    {
        thingPrefab.GetComponent<ThingScript>().Run();
    }
}
  1. In the Unity editor, drag your Prefab of Thing into the slot of Holder-HolderScript-thingPrefab
  2. Run the game.

Expected Result

Game prints come out as:

Before Start: initialized=False

Thing Started

After Start: initialized=True

My assumption here being that thingPrefab was never initialized right, so the Start never ran. So we basically run it manually and therefore the result should be like above.

Actual Result

Game prints come out as:

Before Start: initialized=True

After Start: initialized=True


An interesting note about this problem:

  • When you close and open your Unity project again, the first run will print the Expected Result
  • But if you stop your run, and hit play again, then it will switch to the Actual Result. And any runs after that will be the same.

So for some reason, restarting the editor is changing this result. Unity is somehow caching something.

What is going on here? I feel like I am going crazy.

\$\endgroup\$
7
  • 1
    \$\begingroup\$ Not a full answer but you are doing a few things you are not really supposed to do. Calling Start manually is bad practice. thingPrefab never gets correctly created (assigning a prefab over the editor is useful as a template for copies of it, not to call functions of that prefab. If you want to do that, have that prefab directly in your hierarchy and assign the reference. Calling Start manually is not initializing the prefab nor the first function executed in the life cycle (there are three more functions called before Start) and more behind the hood \$\endgroup\$
    – Zibelas
    Commented Feb 16 at 19:47
  • \$\begingroup\$ Makes sense. But when you make it in you hierarchy, it lives in the game world (say at 0,0,0). Is it bad practice to then move these "reference objects" like to 9999,9999,9999? Is this a common thing in game design? Because jeeze I can't understand how to get references to things. Some people say using Find is bad, but now assigning to an editor is also bad. I don't understand what I'm missing. That said, regardless of my game design, I would still like to understand why Unity is behaving in this way. \$\endgroup\$
    – Frank
    Commented Feb 16 at 20:23
  • \$\begingroup\$ You take a reference to a prefab like this: [SerializeField] private GameObject myPrefab;, and then assign the prefab to the field in the inspector. \$\endgroup\$ Commented Feb 16 at 21:04
  • \$\begingroup\$ @starikcetin Using [SerializeField] private GameObject myPrefab; or public GameObject myPrefab; doesn't make a difference in the question hey. I know it's not the same thing but the result in this case is the same. \$\endgroup\$
    – Frank
    Commented Feb 16 at 21:17
  • \$\begingroup\$ It is not an answer to the question, if it was, it would be an answer, not a comment. You said you can't figure out how to get references, and I told you how you can get references. You don't need to put those objects in the hierarchy, you just reference them through serialized fields. \$\endgroup\$ Commented Feb 17 at 11:12

1 Answer 1

2
\$\begingroup\$

First to answer the question, why it is behaving like this.

Because if you just assign a GameObject in the Editor, it does not exist yet. If you compare it with other languages, you are basically calling functions of a null object and you get undefined behaviour. MonoBehaviours can't be called with new, always use Instantiate.

Now for some examples on the differences.

public class Weapon: MonoBehaviour {

    public GameObject bulletPrefab;

    void Shoot() {
        Instantiate(bulletPrefab);
    }
}

The bullet does not need to be in your scene at the game start, you would create one each time you are shooting. You do not call functions on bulletPrefab, it is the original. In the example you don't have any reference left to the bullet you shot.

void Shoot() {
    GameObject bullet = Instantiate(bulletPrefab);
} 

Now we have a reference to our created bullet. This would be the same as if you dragged your prefab in the editor in the scene and afterwards assigned it to the variable. The big difference is, since it is used as a template, any changes are reflecting in the copy at the time when you make it. If you drag an enemy in the scene, hurt it and use that as a source for later, the copies are as well hurt (or have the other state changes to it).

Is it bad practice to then move these "reference objects" like to 9999,9999,9999?

No and yes. You can create reference objects and move them out of the players view. But not that far, this can cause problems on its own. If you want to read more about that, check out object pooling. Short version, Instantiate is performance intensive, if you need to create a lot of the same object fast, it is better to create them beforehand, deactivate them and use them when you actually need them. Examples would be bullet hell games.

You assigning a gameobject to a variable directly that exists in the editor already for fixed references. Take a spawn point. You would design it in the game at a location, your spawn manager would use this as a reference to know where to spawn something. (The spawnpoint could be more complex than just a Vector as the location).

As a last mentioning special mentioning, you can have Singletons. They usually do not have any visual component (normally a collection of just scripts) and thus are not visuable to the player. 0/0/0 makes not much a difference compared to 999999999. They are normally static and you can access them directly without having a reference to them or looking them up.

Now you can probably answer your statement

Some people say using Find is bad, but now assigning to an editor is also bad.

If it livs in the scene and you need to know where, assigning in the editor is good. Find is expensive. The difference to your posted code is with that statement you compare apples to pears. Just assigning the prefab to a variable doesnt make it magically appear, Find won't even work for those (it will for all created instances of them).

Do not call Start yourself, this is bad practice, there should never a need to do this. If you want to have code that handles some init of your gameobject, have a seperate init function. Call this in Start. Call this init again if you need to reset the gameobject (often done in object pools). But don't call Start.

\$\endgroup\$
2
  • \$\begingroup\$ Hey @Zibelas I really appreciate the in depth answer. Thank you. This does help in my game design problem. But I feel like this doesn't yet explain the debug printing expected vs result part. Also doesn't explain the "An interesting note about this problem" part. I appreciate the info though! \$\endgroup\$
    – Frank
    Commented Feb 17 at 17:01
  • \$\begingroup\$ @ Frank chat.stackexchange.com/rooms/151533/assign-go if you want a bit more info in general, same mistake you do in the other question you have. It is undefined behaviour, basically the first block. In other languages you can compare it to a class that is null and you try to access a variable in it or function. It might work, it might not. You might get the assigned value or something totally else. \$\endgroup\$
    – Zibelas
    Commented Feb 17 at 17:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .