How can I be calling a script on a gameobject that doesn't exist?

If I call this code, I get an error that turretPrimary has been destroyed on the print statement.

    public void buttonPressShoot(){
        print("Shoot "+turretPrimary.name);
        turretPrimary.shoot();
        ctrlDownShoot = true;
    }

But if I called the method first, the method turretPrimary.shoot(); gets successfully called.

    public void buttonPressShoot(){
        turretPrimary.shoot();
        print("Shoot "+turretPrimary.name);
        ctrlDownShoot = true;
    }

And that method fails to have an associated gameObject.

    public void shoot(){
        GameObject go = gameObject;
        print("Shooting "+name);
        shooting = true;
    }

If you want to call a script on a deactivated gameobject then you have to reference it from another script on a different gameobject.

Its because you’ve destroyed the GameObject with Destroy(). Unity has two environments the C# side that you write all your code in, and the c++ where unity’s data is actually managed. because they are two separate environments, Destroying an object doesn’t happen immediately. the gameobject you’ve “destroyed” was only flagged to be destroyed and its memory is waiting to be released on the c++ side.

on the Unity side the object is represented as a fake null object so when you try to access built-in properties on the object unity will respond back that the object was (flagged) destroyed but you’re still trying to access it. Shoot() may still appear to work but the object itself is just waiting in line in the afterlife.

this is just a basic rule, but try and check if the object is null before trying to use it (and return instantly if it is)

public void buttonPressShoot()
{

        if(turretPrimary == null) return;
   
        turretPrimary.shoot();
        ctrlDownShoot = true;
    }

)

1 Like

This. The C++/C# interface is a strange place. On the C++ side, memory is unmanaged. As soon as you call destroy, the C++ object is banished into oblivion. On the C# side memory is managed. You can’t banish an object to oblivion. As long as you have a reference to an object, it hangs around in memory. It’s only if you remove all references that the GC kicks in and actually removes the object.

Using a null check is a good way to avoid this. I would go one step further and use the implicit book operator. This doesn’t rely on ‘fake null’ and so is less likely to cause you weird bugs in the future.

Thanks for the replies!

Okay, this was the problem, although I thought I had accounted for it. I was anticipating the object to be delay-destroyed but was referencing its replacement.

if(turretObj != null){
   Destroy(turretObject);
}

turretObject = Instantiate(turretSource) as GameObject;
turretPrimary = turretObject.GetComponent<class_turret>();

So by my thinking, the old turretObject was scheduled to be removed but there was a shiny new turretObject replacing it. Turns out it doesn’t work that way. For whatever reason the script reference was to the old object. Using DestroyImmediate(turretObject); works and proved the fault, and I can restructure my removal earlier.

But weird!