Home > Software engineering >  How to destroy gameObjects without causing issues in Unity?
How to destroy gameObjects without causing issues in Unity?

Time:01-04

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.

CodePudding user response:

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.

CodePudding user response:

Rather than just destroying it, a better approach would be to add a variable. Like this:

bool OpenDrawMessageShown = false

add this outside of your methods.

Then, put all of your logic with your Raycast and showing the text into an if statement that looks like this:

if (!OpenDrawMessageShown) {   }

And, whenever you make the object appear, simply set OpenDrawMessageShown to true.

What this does is just check if the message has been shown already, and not show it again if it has been. Hope this helps!

Also, your previous solution with SetActive(false) should work if you use this solution, in case you would rather set it to inactive than destroy it.

CodePudding user response:

Instead of destroying the text, why not just hide/show it instead? It will save you on performance and it's easier. Just do drawer.SetActive(false).

  • Related