Is the way OnEnable() and OnDisable() work understood correctly

I’m a little unsure if I have understood the way OnEnable() and OnDisable() works correctly.

Most important part first:

Is it correctly that if you have four different gameobjects for example four different enemies and at the start of the game there is no enemies at the screen and then in the next for example 4 minutes the game spawns 40 gameobjects enemies with a 25 % chance for each, and each gameobject enemy can be destroyed either by being shot by the player or by leaving the screen. The important part here is that at any given time there is zero or more of each type of enemy. Is it correctly that in this situation if the initialization of the references is placed in Start() it only affects the first gameobject normally giving a nullreferenceexception because it is not exactly one of the given gameobject, while placing the references in OnEnable() will give each gameobject enemy of a given type a unique copy of all the components on that gameobject enemy which is independent of all the other gameobject enemies of the same type and then somehow removing the references for that particular unique gameobject enemy in OnDisable() when that particular unique gameobject enemy is destroyed no matter how many other gameobject enemies of that particular type exists at that time. In code this becomes:

private EnemyHealth enemyHealth;

private void OnEnable()

{

    GameObject enemyHealthObject = GameObject.FindWithTag("Enemy");//enemy is the tag
    if (enemyHealthObject != null)
    {
        enemyHealth = enemyHealthObject.GetComponent<EnemyHealth>();//initializing references, the important part.
    }
    if(enemyHealthObject == null)//debugging.
    {
        Debug.Log("Finner ikke EnemyHealth scriptet");
    }

}

private void OnDisable()

{

??? (Somehow removing the reference created in OnEnable when that particular unique version of a gameobject enemy is destroyed).

}

If only the OnDisable() part is wrong please tell me what should be wrote there, if I have misunderstood the way OnEnable() and OnDisable() works please tell me how how I can have zero or more gameobjects of a particular type in the game at the same time without getting nullreferenceexeption and at the same time referencing between scripts as is done in the example with EnemyHealth being the script on the enemy which all enemies share and enemyHealth is the reference. Thanks.

OnEnable may be called multiple times for a given Monobehavior object. If disabled, and re-enabled it will be called again.

So, if you are say… DESTORYING the enemy gameobjects, OnEnable is probably only being called once for each.

But, if rather than destroying them, you are disabling them, for reuse later, THEN OnEnable will indeed be called multiple times.

The point being: if you are using the second case you probably want to initialize your references in Start(), rather than OnEnable.

The differences between these functions can be a bit tricky; make sure you understand and become familiar with this page: Unity - Manual: Order of execution for event functions

GameObject.FindWithTag("Enemy");

I’m not sure why you have this in here; it looks like the gameObejct calling OnEnable is the one you want, so why do this Find operation? Also, how do you know that this function returns the CORRECT enemy gameobject? I recommend you instead use the monobehavior member gameObject. (Unity - Scripting API: Component.gameObject)

Edit: Summary: I suspect the issue is caused by a combination of using FindWithTag, and multiple calls to OnEnable.

Every MonoBehaviour you attach to a GameObject receives the following callbacks in order when the game is started:

Awake()
OnEnable()
Start()
FixedUpdate()
Update()
LateUpdate()

  • Awake() happens on first initialization of the object.
  • Start() happens only when the game is started.
  • OnDisable() happens when you use SetActive(false) on the object.
  • OnEnable() is called again on disabled objects when SetActive(true) is called.
  • OnDestroy() is called if you happen to destroy the object with Destroy(gameObject).
  • FixedUpdate() precedes Update() and LateUpdate() and is used for physics calculations.
  • Update() is used to update object state and other non-physics tasks.
  • LateUpdate() supercedes all other Update() methods.

Just because an object isn’t on screen doesn’t mean OnDisable() is invoked. It’s possible that the GC might clean up inactive objects, but I haven’t experienced this. If the GC does get involved, it will destroy the object, thus forcing the object to receive OnDestroy().


Find methods such as GameObject.Find, Object.FindWithTag, FindObjectOfType, etc are generally unreliable unless you know what you’re doing. You are checking to make sure the object in question isn’t null, which is good, but if you want a particular object, it will likely fail to find it correctly. All the find methods which only return a single object instead of a collection will only return the first available object in the hierarchy where the find condition is true. You’ll have to revise your design if you want to get a particular object.

ok I have tried some different designs where none of them worked but I will write down my best try which I think is closest to being correct. It may be unneccesary with two references to the same script but that is a small extra question. I think I understand how find functions work they take the first object they can find and stops there. I have skipped the code for the Player and GameController in Start() which works as they should. If I understood it correctly there is no need to put this in front of gameObject, the correct gameObject will be chosen automatically. Can someone please tell me what changes I must do to the code for the code to work correctly which means no null pointers, it still seems like the if test for null values is true or false at random. And all the relevant gameObjects in the scene have the Mover script attached, while all enemies and not non-enemies have the EnemyHealth attached, even though all the relevant gameObjects have the Mover script attached I still get all three error messages.

And your answer @Glurth is great, one of the best answers I have seen on the Unity forums, unfortunately it was not enough to solve my problem, but I think I am very close to solving the problem now thanks.

private EnemyHealth enemyHealth;
private EnemyHealth enemyShipHealth;
private Mover mover;

private void OnEnable()
{
    if (gameObject.GetComponent<Mover>() != null)
    {
        Debug.Log("if test mover suksess");
        mover = gameObject.GetComponent<Mover>();
    }

    if(mover == null)
    {
        Debug.Log(gameObject.name + " mover er null");
    }

    if (gameObject.GetComponent<EnemyHealth>() != null)
    {
        Debug.Log("if test enemyHealth suksess");
        enemyHealth = gameObject.GetComponent<EnemyHealth>();
    }

    if(enemyHealth == null)
    {
        Debug.Log(gameObject.name + " enemyHealth er null");
    }

    if (gameObject.GetComponent<EnemyHealth>() != null)
    {
        Debug.Log("if test enemyShipHealth suksess");
        enemyShipHealth = gameObject.GetComponent<EnemyHealth>();
    }

    if(enemyShipHealth == null)
    {
        Debug.Log(gameObject.name + " enemyShipHealt er null");
    }