Search code examples
unity-game-engineif-statementdestroygameobject

How to destroy gameObjects without causing issues in Unity?


I am currently working on a simple 3d game, and I've come across a problem more than once - I did find a way around the ones in the past though this time seems to be a little complicated. I text which appears in a the screen when I'm close to the drawer - the text is 'O to open' the drawer, after I hit 'O' the drawer opens but the text remains on the screen. So, I used destroy gameobject to destroy the text after opening the drawer (so that it never appears again when I point at it) though as I continue playing, the console keeps counting up with the error as I keep pointing at either the same object or other objects (when else is running) A pic of the script Or here as text:

[SerializeField] private Animator OpenDrawer;
[SerializeField] private Animator OpenKey;
public GameObject openDrawer;
 public float interactionDistance = 10f;

 void Update ()
 {
     RaycastHit hit;

     Debug.DrawRay(transform.position, transform.forward * interactionDistance, Color.black);

     if(Physics.Raycast(transform.position, transform.forward, out hit, interactionDistance))
    {
        if (hit.transform.tag == "DESK")
        {
            openDrawer.SetActive(true);
            if (Input.GetKey(KeyCode.O))
            {
            Destroy(openDrawer, 1f);// here's the problem
            // openDrawer.SetActive(false);
            OpenDrawer.SetBool("OpenDrawer", true);
            OpenKey.SetBool("OpenKey", true);
            }
        }
    }
    else
    {
     openDrawer.SetActive(false);   
    }
        
 }   

I was expecting it to destroy the GameObject forever without causing any problems or errors.


Solution

  • This will resolve the errors:

    void Update ()
    {
        Debug.DrawRay(transform.position, transform.forward * interactionDistance, Color.black);
    
        if( Physics.Raycast(transform.position, transform.forward, out var hit, interactionDistance) )
        {
            if ( hit.transform.CompareTag ("DESK") )
            {
                if ( openDrawer != null ) openDrawer.SetActive(true);
                if (Input.GetKey(KeyCode.O))
                {
                    if ( openDrawer != null )  Destroy(openDrawer, 1f);// here's the problem
                    OpenDrawer.SetBool("OpenDrawer", true);
                    OpenKey.SetBool("OpenKey", true);
                }
            }
        }
        else
        {
            if ( openDrawer != null )  openDrawer.SetActive(false);   
        }
    }   
    

    The reason we have to do this check is because after you call Destroy ( openDrawer, 1f ), openDrawer is left pointing to an object that has been destroyed and is treated as null. When you next try and do something like openDrawer.SetActive(false), you’re trying to use a reference that is null, so the code breaks.

    Having said that though, it’s likely that your game is going to want to have many of these types of objects throughout it. This approach is not great, as in every Update event, you’re trying to turn off objects just in case they were on. The engine code will take care of that for you, but there’s a lot of unnecessary function calls.

    A possible update might be to include a state for your notification text:

    public enum NotificationState
    {
        None = 0,
        NotShown,
        Shown,
        Deactivated
    }
    

    Then do a check against the state:

    void Update ()
    {
        Debug.DrawRay(transform.position, transform.forward * interactionDistance, Color.black);
    
        if( Physics.Raycast(transform.position, transform.forward, out var hit, interactionDistance) )
        {
            if ( hit.transform.CompareTag ("DESK") )
            {
                if ( notificationState == NotificationState.NotShown )
                {
                    notificationState = NotificationState.Shown;
                    openDrawer.SetActive(true);
                }
    
                if (Input.GetKey(KeyCode.O))
                {
                    if ( notificationState == NotificationState.Shown )
                    {
                        notificationState = NotificationState.Deactivated;
                        Destroy(openDrawer, 1f);
                    }
    
                    OpenDrawer.SetBool( "OpenDrawer", true);
                    OpenKey.SetBool("OpenKey", true);
                }
            }
        }
        else
        {
            if ( notificationState == NotificationState.Shown )  
                openDrawer.SetActive(false);   
        }
    }   
    

    Keep in mind that this is just a suggestion and not a requirement. This small optimisation might make no difference to how your game runs, but might make your code intent a little clearer.

    Note: Written on mobile phone, not tested.