Search Unity

Questions on Object Management

Discussion in 'Scripting' started by joe_04_04, Oct 6, 2017.

  1. joe_04_04

    joe_04_04

    Joined:
    Sep 15, 2017
    Posts:
    14
    https://unity3d.com/learn/tutorials/temas/multiplayer-networking/shooting-single-player

    I'm studying this tutorial on how to create a shooter. What has me confused is how these bullet objects are being tracked. What I mean is, we are instantiating a new bullet prefab to the bullet var GameObject, but if we fire two in a row, aren't we calling the fire function twice, which would destroy the bullet var GameObject? I could be misunderstanding this, since I'm still new to C#, but if the bullet var is being destroyed by the end of the fire function, what is keeping track of those bullets?

    I come from a C++ background, so I'm used to objects being destroyed when a function ends and having to manually delete objects when I need to reassign a pointer to a new object. I know that in C#, there is garbage collection that does this, but that doesn't really clear up my main question.

    Any help would be appreciated.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    3,787
    With Unity objects can sorta take care of themselves. The fire function Instantiates the bullet from the given prefab, then immediately marks it for destruction in 2.0 seconds. At that point it forgets about it.

    Cue the magic of colliders and other methods that can be used to decide if that bullet actually dies earlier, such as if it hits something. I assume that's in a later part of the tutorial. The bullet you made is already marked to be destroyed 2.0 seconds from its creation, and then if it gets destroyed earlier, it's gone earlier.

    It's definitely a different way of thinking about game problems that really enables a lot of really interesting behaviors that can be trivially set up with clever use of components (MonoBehaviour scripts) on GameObjects. It's pretty amazing how well Unity pulls it off.
     
    Lysander likes this.
  3. joe_04_04

    joe_04_04

    Joined:
    Sep 15, 2017
    Posts:
    14
    I guess what I'm having trouble understanding is what is managing the bullet after another bullet is fired? For instance, if I fire two bullets in a row, the second bullet is instantiated and assigned to the bullet variable, overwriting the contents that it had before, which was the first bullet... in my head, its like take the head pointer in a linked list and assigning it to another value and losing the linked list. It probably just comes down to me not understand C# that well... I assume what I'm thinking is incorrect, but if it is correct, how does that bullet no become a memory leak?
     
  4. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,401
    It's just what @Kurt-Dekker has said. The gameobjects are sorta tracked by the engine itself.

    It won't do anything to the gameobject if you lose all of your own references to it, because the engine keeps it in some kind of collection that you can access later. That's why there are methods such as GameObject.Find and so on.

    You could just make a loop, instantiate 100 objects and they'll be happily sitting around in your scene, without the need to keep references to them.

    Speaking of the scope, if you do that with a value type (e.g. all integral types or C#-structs), it'll be gone when it leaves the scope, just like in C++.
    If you deal with reference types and lose all references, they're ready to be collected sooner or later after the last reference has been lost - but it'll usually not happen right away unless the GC allows you to make a call to collect immediately (not recommended in most cases).

    And when you deal with UnityEngine.Objects, there's a little more to that. As mentioned, the engine itself tracks several types of objects and many of the built-in types have a C++ pendant as well, which you get access to through a C#-object.

    The only way to really destroy a GO is any of the destroy methods (or anything that enforces equivalent calls, like a scene change). It then destroys / disposes the native C++ object, the C# object is still around and will be around as long as you keep references to it.
    But be aware, that object does no longer represent valid state, or at least only partly*. That is, anything that would need to be forwarded to the C++ side (or also anything C#-sided that internally keeps track of destruction) will cause an exception to be thrown.

    *Partly, because you could still access members that do not operate on the destroyed "parts" or your object.

    It's worth (and also interesting) digging a little bit into it when you've got the time for that, I used to code C++ as well and when i switched to C# ~ 3 or 4 years ago, there was a lot of stuff to get used to. :)
     
    Lysander likes this.
  5. Lysander

    Lysander

    Joined:
    Feb 24, 2013
    Posts:
    1,596
    What they said. The scene itself is a collection that the object can be accessed from (this is how GameObject.Find works), so if it's in the scene hierarchy, which it automatically is if you use Instantiate to create it, then you won't ever lose all of the references to it without destroying it yourself or changing scenes.

    It's the nature of bullet objects that they really only have one job, and they should handle it themselves, so personally I don't see the point of keeping a specific reference to the last bullet fired anyways. If you're going to do that, then a FIFO collection and a coroutine that removes references after X seconds would make more sense, or a pool to keep a given number of bullet objects in so that you don't need to keep instantiating and destroying them (if this were a real game project, you should be doing this anyways).
     
    Last edited: Oct 8, 2017
  6. joe_04_04

    joe_04_04

    Joined:
    Sep 15, 2017
    Posts:
    14
    Thank you everyone for the clarification. It all makes sense. I was just having trouble understanding how the object didn't simply become a memory leak when the bullet variable holding it is reassigned on a new instantiation. I kind of had a hunch that Unity was keeping track of this. In C++, this would most certainly be a memory leak to use new on a pointer that already has a value, without deleting it, but I get it now for C# and Unity, thanks.

    For the game I wrote in class, instead of using the method used in the tutorial to destroy it, I just inserted the OnBecameInvisible callback into my code and used Destroy(gameObject), so that the bullet is destroyed when it is out of camera view.
     
    Last edited: Oct 9, 2017
  7. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,401
    Note that it's not specific to Unity. You can do that in normal C# applications as well, but there's nothing out of the box that kinda registers all objects for you.
    Like mentioned, doing this with your own plain C# types, there's nothing to register those in the first place.

    Code (csharp):
    1. // some scope
    2. {
    3.     var localVariable = new Sample();
    4. }
    5.  
    If that object (a reference to it to be exact) does not leave the scope in any possible way, nor registeres itself somewhere, it'll be candidate to be garbage collectedwhen the scope ends . There's literally no need to take care about that.

    Worth noting: in pure C#, there's technically no real memory leak - given that the runtime environment and the GC work flawlessly. Objects that you lose all references to are simply cleaned up sooner or later. It becomes more interesting when you start to deal with resources and / or native code that you want to use (just like Unity does). There are common patterns to clean up those resources / manually allocated memory.
    If you ever happen to come across things like the interface System.IDisposable, don't hesitate to read it up.

    Of course there's something similar though. Sometimes you happen to run into situations in which reference are created that you might not immediately be aware of.

    For instance, if you build lambda-expressions / delegates and/or subscribe to events. But that's considered a bug or unwary programming instead, still not a memory leak as the objects are technically still being referenced.

    There's alot more to give an insight to, but I'll leave that up for yourself to explore as it takes time to wrap your head around, especially when you are new to C# (or managed languages in general) and/or come from a native programming background, and all of that cannot be put into one or a few posts anyway.
     
    Last edited: Oct 9, 2017
    Kurt-Dekker likes this.