Search Unity

I don't understand the difference in logic between these two codes?

Discussion in 'Getting Started' started by ThrowbackZac, Apr 23, 2021.

  1. ThrowbackZac

    ThrowbackZac

    Joined:
    Aug 9, 2019
    Posts:
    4
    So I was having an issue while following a tutorial for an endless runner that I wanted to make the platforms get destroyed and respawn. However everytime a new platform would spawn it wouldn't move with this code:
    Code (CSharp):
    1. {
    2.     [SerializeField] float resetPosition;
    3.     [SerializeField] GameObject ground;
    4.     [SerializeField] float speed;
    5.     private Vector3 respawnPosition = new Vector3(0, 0, 442.5f);
    6.     Rigidbody rb;
    7.     // Start is called before the first frame update
    8.     void Start()
    9.     {
    10.         rb = GetComponent<Rigidbody>();
    11.     }
    12.  
    13.     // Update is called once per frame
    14.     void Update()
    15.     {
    16.         if(transform.localPosition.z <= resetPosition)
    17.         {
    18.             Destroy(gameObject);
    19.             Instantiate(ground, respawnPosition, transform.rotation);
    20.            
    21.         }
    22.     }
    23.     private void FixedUpdate()
    24.     {
    25.         MoveGround();
    26.     }
    27.     void MoveGround()
    28.     {
    29.         gameObject.transform.Translate(0, 0, -speed * Time.deltaTime);
    30.      
    31.     }
    32. }
    But then when I flipped the destroy and instantiate like so:
    Code (CSharp):
    1. if(transform.localPosition.z <= resetPosition)
    2.         {
    3.             Instantiate(ground, respawnPosition, transform.rotation);
    4.             Destroy(gameObject);
    5.            
    6.         }
    it works no problem, I'm looking for an explanation on why the logic doesn't work both ways? Thanks in advance!
     
  2. Schneider21

    Schneider21

    Joined:
    Feb 6, 2014
    Posts:
    3,512
    Destroy(gameObject) is going to destroy the GameObject that this script is running on. Once destroyed, the code attached to it can no longer run.

    So by calling Destroy before instantiating a new platform, you're effectively not calling the Instantiate. It might be that the way the first set of code runs, the object gets instantiated before the garbage collector can come by and clean up the current object, so it does start the instantiate for the new object, but then is getting cleared out before it can fully initialize? Seems like something that would perform inconsistently.

    In any case, if you're ever trying to destroy the GameObject you're currently working from, make sure you've already done everything else you intend to do with it first.
     
    ThrowbackZac likes this.
  3. ThrowbackZac

    ThrowbackZac

    Joined:
    Aug 9, 2019
    Posts:
    4
    Hey again! Thanks for the answer. I still don't fully wrap my head around it working the way it does (although that's probably more just me being new to coding), but I get the idea of making sure Destroy(gameObject) should be called after everything else is done. I'll be honest coding is a little rough to get into, but I'm hoping it gets easier as time goes on.
     
  4. Schneider21

    Schneider21

    Joined:
    Feb 6, 2014
    Posts:
    3,512
    It absolutely does. Every day I realize I had no idea what I was doing previously. If you keep at it and force yourself to understand stuff as you're doing it, you'll look back at this script in a month and laugh at how bad you think it is.
     
  5. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I disagree. When you call Destroy, it merely schedules the destroy to occur. The destroy actually occurs at the end of the frame. The current frame, and all its code on the object, will execute as normal with the object not yet destroyed until the end of the frame.

    https://docs.unity3d.com/ScriptReference/Object.Destroy.html
    To the OP, I don't see anything to explain the difference in seen behavior.
     
  6. Let's be precise here. Destroy marks the object for destruction what happens after the current update loop.
    But, Instantiate essentially (deep)copies the game object. It is undefined and undocumented (AFAIK) behavior to call instantiate on an already destroyed object, but we can assume it will copy this mark for death (or at least parts of it) as well. It is undocumented though therefore unsafe to call Instantiate on a destroyed game object.

    edit and ps: among other things, this is the reason Unity used to advocated setting the game object variable null right after the destroy command
     
    Joe-Censored likes this.
  7. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    But the OP is instantiating "ground" instead of "gameObject". Is "ground" the same object as, or a child of, "gameObject"?
     
  8. ground is a member variable on (edit: ...a component on...) the current game object, we don't know what happens with the references during Destroy

    All I'm saying is, it is unsafe, undocumented and advocated against using any part of a game object you called Destroy on.
     
    Joe-Censored likes this.
  9. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,187
    My programmer senses are tingling. I believe this might be a bug in the engine. For testing purposes I created a new project in Unity 2021.1.3, added a sphere to the default scene, and then attached the following scripts.

    Script A
    Code (csharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Foo : MonoBehaviour
    6. {
    7.     void Update()
    8.     {
    9.         Destroy(this);
    10.         Debug.Log("Foo!");
    11.         Instantiate(this);
    12.     }
    13. }
    Script B
    Code (csharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Bar : MonoBehaviour
    6. {
    7. }
    From there I proceeded to run two tests. Once with the script in its original form and once with the Destroy method commented out to stop it from executing. With it commented out the script proceeded to clone the object infinitely which is the expected behaviour without the Destroy.

    With Destroy enabled it cloned the object which that too is expected however the cloned object had the script that called Destroy disabled preventing it from repeating this behaviour. Again this may simply be a bug in Unity since I see nothing describing this behaviour in the documentation.
     
    Last edited: Apr 24, 2021
    BrandyStarbrite and Joe-Censored like this.