Should I declare and create an instance of a class inside loop? - c#

I wrote a simple console game (TicTacToe to be specific) for practice. After game over I need to reset the game so I am creating a new instance of the game class.
Is the old instance of class destroyed before creating the new one?
If not then it is getting stacked up in the memory which is not good. So what is the better practice here?
static void Main(string[] args)
{
bool restart = true; //stores decision whether to restart the game
do
{
Game game = new Game(); //a simple console game
game.start(); //start -> play -> game over
restart = playAgain(); //playAgain returns a boolean
} while (restart);
}
The code is written in c# if that is relevant.

The short version:
Should I declare and create an instance of a class inside loop?
Yes, if you only need that instance inside the loop.
When we create an object
Game game = new Game();
...it "exists" in memory as long as there are references to it. When there are no longer any references to an object it becomes available for garbage collection.
So the question is, what references are there to the created Game object? It appears that there is only one reference, the game variable that you assign it to.
So now the question is, how long does that variable live? It lives as long as the scope within which it is defined. In this case you're defining it within a do loop. As soon as that execution of the loop is over (a few lines later) the variable goes out of scope. In other words, the scope within which it was defined no longer exists. In simpler terms, that variable no longer exists.
The variable is the only reference to that exact Game object. When the variable goes out of scope, there are no longer any references to that Game object. It will be garbage collected. (That doesn't happen instantly, but we don't have to worry about exactly when it happens. We don't usually care. That's something the framework worries about for us.)
What if the loop repeats? Think of the inside of that loop as a method that gets called over and over. When it gets called again, the game variable is new, because it's declared inside the loop. It doesn't "know" anything about the previous execution of the loop or the previous Game object. That variable went out of scope at the end of that iteration of the loop.
How do we tell what the scope of a variable is? One easy way is to see where we're allowed to use it.
If you tried to write this, using the game variable outside of the loop:
static void Main(string[] args)
{
bool restart = true; //stores decision whether to restart the game
do
{
Game game = new Game(); //a simple console game
game.start(); //start -> play -> game over
restart = playAgain(); //playAgain returns a boolean
} while (restart);
}
game.Start(); // <-- Outside the loop where it was declared
...it wouldn't compile. That's because the variable is declared inside the loop, so it's not available outside the loop. Just like if we declare a local variable inside one method it's not visible or accessible inside another method.

As far as I know (because I'm relatively new to this) all variables that you declare in the do scope are local, and when the program is out of that scope those variables are destroyed. So, the instance of the game class that you declare in the do scope is local and will therefore be destroyed when the while loop checks the condition again.

When you create a new instance of the Game class called game all you are doing is setting the variable game to a new instance. game is saved in memory so the next time you start a game it will take the variable game and regardless of what state it's in, will set it to a new instance. This means that there is never more than once instance of that class regardless of it being in a do/while loop because you are using the same variable.

Related

Script is getting the prefab's default data?

Using Unity. When instantiating a new gameObjectA with ScriptA, I access gameObjectB's script (ScriptB) while doing so. The thing is, I can only use the prefab of gameObjectB, instead of an already instantiated gameObjectB. For example -
public class ScriptA : MonoBehaviour
{
public ScriptB scrptB;
void Start()
{
float X = scrptB.IntegerThatIncreasesEveryFrame;
Debug.Log(X);
}
In every frame that a gameObjectB has been instantiated, I have integer IntegerThatIncreasesEveryFrame (I'll call it ITIEF now) that, of course, adds 1 to itself every frame.
When gameObjectA and ScriptA gets instantiated, I want to use gameObjectB's ScriptB's ITIEF (e.g. 100 after 100 frames after being instantiated).
I have to use a prefab of gameObjectB, though. When gameObjectA gets instantiated, it uses the DEFAULT value of ITIEF (which is zero).
If this makes sense please help!
In order to access the information from an instanced object, you need to have a reference to the object. In your case, you are referencing the prefab, not the instanced one in game.
The reason why you cannot drag the script itself into the Inspector is because a reference needs to reference a script that is instanced. ScriptA cannot make a reference to ScriptB unless ScriptB is instanced into a gameobject. A prefab acts as an instanced object that doesn't 'exist' in the game. If you want a reference to the instanced script that IS in the game, then you need to set the ScriptB variable to be that in-game script.
ScriptB instance = GameObject.FindObjectObType<ScriptB>();
This line finds the first instanced form of ScriptB.
This only works if there is ONE instance of ScriptB in the game. If there are more, you need to do a more complicated bit of code.
Are there the same amount of ScriptA and ScriptB? Then when they are both first instanced, store ScriptB somewhere, and then update ScriptA with it.
Your question leaves a lot more questions, but I think I've covered most of the solutions. Pretty much, if you have more than one ScriptB instance, then store the one you want to use and then set it on the ScriptA instance. Otherwise, use FindObjectOfType to get a single instance.

Hold list of currently active SoundEffectInstances

I'm currently trying to make a SoundManager class for my game. It's supposed to hold a list of all currently playing SoundEffectInstances, so I can loop this list to pause (and resume) all Sounds, for example when the game is paused.
My Play function is pretty straight forward:
public static void PlaySound(string name)
{
sTestSoundInstance = sTestSound.CreateInstance();
sAllSounds.Add(sTestSoundInstance);
sTestSoundInstance.Play();
}
However, I can't wrap my head around how I can check if a sound has finished playing, and when I can remove it from sAllSounds. Please consider there will be different Sounds of different leghts. Any thought on this is apprechiated.
I avoided this problem by making my own sound class: It gets initialized with a SoundEffect, stores the duration property, creates a SoundEffectInstance and drops the SoundEffect. The SoundManager class holds a list of these new SoundEffect class. Each Instance gets updated every Frame, and sets a flag it the duration has been reached. The SoundManager delets the Instance from the list within the next frame.

How does C# handle variables in memory when their method is called multiple times?

First time asking here. My experience is only "advanced beginner". The question is about the variable storage in the memory due to an object instantiation in Unity. Need a bit to explain the setup:
In my scene, I instantiate an entire starsystem. The script can create up to 8 planets, each with up to 3 moons and 4 anchors (empty gameObjects). I do not change scenes, I only destroy the starsystem and instantiate the new one, so I need to ensure all old references are gone from memory. I want to be sure that I do not create a memory leak here by stuffing the memory with gameObjects that never get removed.
Below is the part of my script that instantiates the moons, can be called many times within a single system instantiation. The moon gameObject will be "returned" to the caller after instantiation and added to another variable created in the caller, which is it's official variable called planetXMoonY, and can only exist once per system and will be nulled before a new system instantiation.
In Version 1, I create the temporary variables within the method, and in version 2, I created them outside and first reset them before instantiating stuff.
How is this handled in memory ? Am I seeing it correct that in version 1 after a while there could be technically dozens or more newMoon GameObjects in memory ? Same name, but of course with separate instance ID, and I will filling up the memory.
What happens when the object planetXMoonY is destroyed ? When I understand it correctly, there is no more connection to the newMoon variable , but the planetXMoonY reference will be removed in time by the GC(). But will the newMoon variable ever be deleted from memory without a direct call setting it to null ?
So I thought maybe Version 2 is an improvement. Does it help to reset it to null first to ensure the garbageCollector removes all newMoons in time, since they aren't needed after returning the gameObject, there are just here for instantiation purpose. But am I still creating dozens in this way, or just one ?
I hope the question is clear enough. I seem to miss a bit of basic understanding in memory usage here. I think I'm missing a really important knowledge piece here on how to correctly and safely create variables, and I want to ensure my future workflow is improved. Thank you in advance for helping.
B/R
Starman
Version 1:
private GameObject InstantiateMoon(GameObject moon, GameObject planetSphere, float moonSphereRadius, float moonOrbit, float MoonOrbit_Y)
{
GameObject newMoon = Instantiate(moon, planetSphere.transform.position, Quaternion.identity, spheresContainer.transform) as GameObject;
GameObject newMoonSphere = newMoon.transform.Find("MoonSphere").gameObject;
newMoonSphere.transform.localScale = new Vector3(moonSphereRadius, moonSphereRadius, moonSphereRadius);
newMoonSphere.transform.localPosition = new Vector3(0,0,moonOrbit);
newMoon.transform.localRotation = Quaternion.AngleAxis(MoonOrbit_Y, Vector3.up);
DrawCircle orbitLine = newMoon.transform.Find("OrbitLine").gameObject.GetComponent<DrawCircle>();
orbitLine.InitializeLine(moonOrbit);
newMoon.transform.SetParent(planetSphere.transform);
return newMoon;
}
Version 2
private GameObject newMoon;
private GameObject newMoonSphere;
private DrawCircle orbitLine;
private GameObject InstantiateMoon2(GameObject moon, GameObject planetSphere, float moonSphereRadius, float moonOrbit, float MoonOrbit_Y)
{
newMoon = null;
newMoonSphere = null;
orbitLine = null;
newMoon = Instantiate(moon, planetSphere.transform.position, Quaternion.identity, spheresContainer.transform) as GameObject;
newMoonSphere = newMoon.transform.Find("MoonSphere").gameObject;
newMoonSphere.transform.localScale = new Vector3(moonSphereRadius, moonSphereRadius, moonSphereRadius);
newMoonSphere.transform.localPosition = new Vector3(0,0,moonOrbit);
newMoon.transform.localRotation = Quaternion.AngleAxis(MoonOrbit_Y, Vector3.up);
orbitLine = newMoon.transform.Find("OrbitLine").gameObject.GetComponent<DrawCircle>();
orbitLine.InitializeLine(moonOrbit);
newMoon.transform.SetParent(planetSphere.transform);
return newMoon;
}
Long story short, the 2nd function isn't an improvement.
When you do this:
newMoon = Instantiate(moon, planetSphere.transform.position, Quaternion.identity, spheresContainer.transform) as GameObject;
The previous value is dissassossiated from the private newMoon member and will be picked up by the garbage collector if it's not associated with another variable in your code.
The GC will pick an object when it is no logger used. If the object is not assigned to a variable then it is considered unused.
The variable itself in your first function, does not exist any more the moment the function ends. It returns a reference to the object and if that's not assigned anywhere, the object itself will be also picked up by the GC for disposal.
It mostly depends on what you do outside of the functions, instead of what happens inside.
If memory usage is a concern if yours, check the well known GoF flyweight pattern https://en.m.wikipedia.org/wiki/Flyweight_pattern which in your case, I think will help.

Singleton not getting Referenced in Start() Function after Scene Change

I am trying to understand why I am getting a missing reference exception after a scene change when using singletons.
So, I have two GameObjects in the first scene. A main camera with a GameManager script attached and another shop object with a Purchaser script attached. Both scripts are also singletons created like this for example:
public static Purchaser Instance
void Awake(){
Instance = this
}
They then both reference each other in the Start() function, again like this for example:
void Start(){
game = GameManager.Instance
}
Before a scene change, both scripts use each others singleton references to call methods from one another and everything seems to be working fine. Once I change scenes neither of those objects is in the next scene so they both get destroyed. However, once I go back to the main scene I receive a missing reference exception when the purchaser script attempts to call a method from GameManager using the singleton reference it gets from Start(), that changes the text of a text object attached to the main camera. This function is called after a button is pressed that is attached to the shop object that calls this function in the Purchaser script:
UpdateMoney(){
game.UpdateMoney(100);
}
I read around and was seeing that this may be because Start() will only being called once throughout the whole game run. Which meant the GameManger singleton instance of the Purchaser script was still the old one from before the scenes changed and that instance was destroyed. But, I just tested to see if this was true by putting a debug log in each scripts Start() function, and saw that after each scene change back to the main scene the debug log would go through from both scripts Start(). So, would it be right to say Start() is only ever called once for the life of script but not for the whole game run right? Shouldn't this also mean that once the game changes back to the main scene and both GameObjects are created again, the Purchaser script should now have an updated reference to the newly created GameManager script since Start() was called again?
What I also found was that this worked instead of using the game reference in UpdateMoney():
UpdateMoney(){
GameManager.Instance.UpdateMoney(100);
}
So, why does this work instead of using the game reference retrieved in Start()? Does this mean when Start() is called GameManager.Instance is still the old GameManager.Instance which is why game = GameManager.Instance does not work? Sorry this is very wordy. Any help is much appreciated.
Based on what I understand, you have a singleton made in the main scene. The game object where the singleton is attached to is destroyed after switching scenes.
First of all, is there a reason why you need the singleton to be a MonoBehaviour and attached to a game object? Because you can just make a class with
private static MyClass instance = null
public static MyClass Instance
{
get {
if(instance == null)
instance = new MyClass();
return instance;
}
}
This way, your singleton will always have a value and can be passed thru scenes unless instance is set to null.
Second, Start() is called once only. However, I encountered issues before that if the MonoBehaviour is attached to a disabled game object, the Start() will not be called. You can check if this is what's happening to you when you switch back to the main scene.
Third, if you really need the singleton to be a MonoBehaviour, you can use DontDestroyOnLoad(instance.gameObject) so the game object of the singleton will not be destroyed even after a scene switch. However. I assume the game objects are set on the scene. If it is not from a prefab, you can just do something like this
private static MyClass instance = null;
public static MyClass Instance
{
get {
if(instance == null){
GameObject inst = new GameObject("MyClass Singleton");
instance = inst.AddComponent<MyClass>();
DontDestroyOnLoad(inst);
}
return instance;
}
}
If you do this, then you can remove the preset game objects from the main scene and let the first call to MyClass.Instance make the game object for you.
Lastly, if you do not want to do that, you should set instance = null; on the game object's OnDestroy() so that when you enter the main scene, the new instances will be set to the singleton. This would mean the singleton will not have a value after you switch scenes outside of the main scene.

Unity MissingReferenceException when loading same scene for second time

I'm trying to create Arkanoid 3d game using Unity with C#. I've created simple Menu (Scene 0), where I can start my game, my main scene where actual game takes place(Scene 1) and Scoreboard (Scene 2), which is shown after losing all 3 balls Player has at start. After pressing any key i go back to Menu and can start game again. And this is where problem begins.
During second game after loosing 1st ball, my game goes crazy. I get loads of "MissingReferenceException"s like one below (but some linked to other objects (like GUIText's etc):
MissingReferenceException: The object of type 'Player' has been destroyed but
you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
Player.BallLost () (at Assets/Player/Player.cs:164)
GameEventManager.TriggerBallLost () (at Assets/Menagers/GameEventManager.cs:30)
Ball.Update () (at Assets/Ball/Ball.cs:47)
I noticed loads of MissingReferenceExceptions that are casued by not assigning variables. But this feels kinda diffrent for me since it all works perfectly during "1st play". What can cause this problem? I cheked in inspector after launching game for the second game and all variables are assigned to objects.
I'm not sure if shoudl insert game code since it has grown rather big and is split into >10 scripts.
In my case, the problem were two static events. One assigned to call a method whenever it was raised (created by another class), and one created in this class to inform other classes for the occurance of something.
So I just added the following two in the OnDestroy() method:
OtherClass.onNewX_event -= X_eventHandler;
for the fist one (where OtherClass was the other class which was raising the onNewX_event and the current class was handlening it)
onThisClassEvent = null;
for the event created and raised in this class.
I'm guessing you used Application.loadLevel(xx). This is what I found out about it:
Reloading the scene should reset all the variables unless you are using static variables because logically creating a new instance of every object would reset its values to their initial state.
Static variables on the other hand are not destroyed because they are part of a class, not an instance. You have to reset these manually.
DontDestroyOnLoad() is a little different. It tells Unity not to destroy an object when you load a new scene. So these objects won't be reset either because they aren't being destroyed and recreated.
The only way to reset them is just to manually go through and turn the variables back to some initial state. It is your choice how you do that. You can either save all the initial values, or copy the values over from a newly instantiated class.
As an addition I'd like to say if you use static variables, it might be more useful to put them all in a Singleton or change them into non-static variables.
Include below function in your GameEventManager class
public static void Nullify(){
GameStart = null;
GameOver = null;
LevelWon = null;
GamePause = null;
GameResume = null;
BallLost = null;
}
and call this function (GameEventManager.Nullify();) in Menu(scene0) before loading other scenes ;
GameEventManager.Nullify();
Application.LoadLevel("Scene1);
Hope this help...... :-)

Categories

Resources