In my project I have a resource called power (an alternative to mana).
I figured out how to make the current amount of power and power-gain connected through scenes, by making the value static, but every time I jump to another scene it seems to reset the process of gaining power, making the player able to abuse the power-gain by swapping scenes repeatedly.
I assume this is the line that makes the glitch above happen.
The power is set to increase by 1 each second, but when swapping scene the WaitForSeconds(1) is somehow seems to reset, so that if you click on the “change scene” buttons fast enough you basicly get 1 power for every click.
Is the MonoBehaviour on which this coroutine runs persistent? Meaning, if you just have a script on a GameObject which runs that function, it will be destroyed and recreated whenever you change to a new scene. Only when you mark it as DontDestroyOnLoad, will it persist between scenes.
The Coroutine belong to the script that started it so if that one is destroyed on scene load the Coroutine is terminated and probably restarted by you. So where are you starting the Coroutine and is that Monobehavior not kept alive?
Most people advise to avoid statics as much as possible. Using DontDestroyOnLoad is relatively straightforward. You still have a concrete instance in your scene, it’s marked, which you can see (and therefore easily debug) in the hierarchy and you can potentially remove it by calling Destroy manually. This makes managing such an object (and it’s associated memory footprint) straightforward.
Statics on the other hand can lead to hidden “leaks”, because you might have manually destroyed your GameObject now you think, that it should be gone, but if there were any static variables referencing this destroyed object, it will never be unloaded from memory, therefore causing you RAM consumption to grow over time, if you have many such leaks. This kind of problem is very hard to debug, you will need specialized tools, once your project reaches a certain size, and a lot of those tools don’t play nicely with Unity (lots of crashes or not compatible); at the end you’ll be hunting down all static variables in your code manually, wishing you’d never started to use them.
Of course, if you know how to use everything correctly, it can be quicker to keep a static reference, which will stay until your game shuts down. In such a case, it shouldn’t cause any problems.
Correction:
The mentioned issue with statics doesn’t happen with references to GameObjects, because as StarManta explained, they are managed on the C++ side. However, it does apply for things like keeping a static reference to another object which has a static list, which contains a lot of animation assets. When you delete all your GameObjects with MonoBehaviours, those animations will not be unloaded, because they are referenced statically. Indeed, the solution is to always null statics, when you don’t need them anymore, but it’s one more thing to forget, especially when there are many variables.
If you set the static variable to null on destroy it removes the problem right?. As long as you don’t abuse statics but use them when appropriate I don’t see why they shouldn’t be used.
This is a misrepresentation of the way Unity and its objects handle memory. When you say “public GameObject foo;”, foo doesn’t point to a real GameObject, but rather, a virtual wrapper class which, itself, contains reference to a GameObject. In most cases, this distinction is minor, but this is important in the area of 1) null checks, and 2) memory management.
If you had a direct reference to the GameObject proper, you’d be correct: everything on that GameObject would still be held in memory. But because you have a fake object, when you call Destroy on a GameObject, it’s dead and gone; the engine takes care of making sure that all relevant references to it are nullified. While foo is not technically null, it might as well be (see that null check blog post linked above). It now points to a tiny wrapper-class object that takes up a negligible amount of memory.
You could only end up with any notable amount of memory being used by statics if you linked directly to the data in question, e.g. public static Texture2D someTexture; or if you have a static object of a class that contains this data (and I think that this class would have to not inherit from UnityEngine.Object in order for the data to survive a Destroy, but don’t quote me on that one).
I still don’t think I understand how this work.
I tried it out in the following scenario:
In the first scene i have a manabar. It has a maximum of mana value of 1000, start at 0 and gain 1 mana per second.
I made an empty object, put the mana-related scripts on that, put the “DontDestroyOnLoad” script on it and made a visual representation on the canvas, displaying the “public unityengine.ui.text”'s from the scripts at the text boxes there.
I then added another scene, and created two buttons to swap between them.
When I swap scene I can see that the empty object with the scripts is transfered, but when I then return to my first scene the mana-value has reset to 0 again. Should the value not remain in this case?
I get errors that the object-reference (the ui.text lines) are not set to an instance of an object, but what do I then do, if I dont want the mana displayed, but just want the function to keep running behind the curtain?
The data-holding part of your script, which is persistent, and knows how much mana you have, and has no conception of the UI
The UI
This sort of structure is very common, and in general it’s good practice to separate out the UI this way - your game logic shouldn’t be aware that your UI even exists, effectively, only providing the available “hooks” so that the UI can handle itself.
So for your first one, use a singleton. This is a static reference to an object in your scene, which allows you to reference it from anywhere while letting you use MonoBehaviour things like Update and coroutines, plus editing the variables in the inspector. You can add in code to this singleton that ensures there is never more than one (which is probably what your most recent comment alludes to - when you re-load the first scene, it’s creating a new copy of the object, which is hiding the old copy):
public class ManaHolder : MonoBehaviour {
public static ManaHolder instance;
void Awake() {
if (instance != null) {
instance = this;
DontDestroyOnLoad(gameObject);
}
else {
Destroy(gameObject);
}
}
public float mana = 1000f;
public float manaRechargePerSecond = 1f;
void Update() {
mana += manaRechargePerSecond * Time.deltaTime;
}
}
// anywhere else
Debug.Log("Mana is currently at "+ManaHolder.instance.mana);
Your UI objects, on the other hand, will be just normal objects that can live and die by the scene as you see fit, and they will access all the data they need via ManaHolder.instance. They don’t need to persist, because the data they’re looking at lives elsewhere.