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...... :-)
Related
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.
I created a football score calculator. I have a button that changes the scene, but when I change the scene and reopen it the score value is reset to 0. Here is the code:
public class Main : MonoBehaviour {
public Text plusUp;
public int value = 0;
public void button(int sc)
{
SceneManager.LoadScene(sc);
}
public void plus()
{
value++;
plusUp.text = value.ToString();
}
}
1. You can use the method Object.DontDestroyOnLoad on the object that hold the variable you want to save. It will keep the GameObject this script is attached too alive when another scene is loaded:
void Awake()
{
DontDestroyOnLoad(this.gameObject);
}
See the documentation for more informations
You can also make a Singleton but this design pattern is a little more complex as Unity will destroy it when you load another scene. You still have to use DontDestroyOnLoad see how to implement this pattern on their GitHub page
2. Or you can save the value on the disk before loading another scene and then load the value with PlayerPrefs helpers methods:
public int value = 0;
void Awake()
{
//Load the saved score (this value will be saved even if you restart the app)
value = PlayerPrefs.GetInt("Score");
}
public void button1(int sc)
{
//Save your value before you load the scene
PlayerPrefs.SetInt("Score", value);
SceneManager.LoadScene(sc);
}
See the documentation for more informations on the types.
There's a few core concepts that would prove useful to understand this issue.
Scope: Each scene operates in it's own scope. Any variables, objects, or changes that occur in one scene do not automatically transfer to another scene. When you start a scene, all objects in the scene are instantiated and initalized, and their Awake()/Start() methods are called if they are Monobehaviours.
Initialization - When an object is instantiated, it is initialized with constructors or default values. Monobehaviours do not have constructors, so any variables will defer back to default values.
Data persistence - When you change scenes, all game objects in the previous scene are destroyed, while all objects in the new scene are instantiated and initialized. Because all objects in the previous scene are destroyed, any values set on those objects disappear. You can prevent a GameObject from being destroyed with DoNotDestroyOnLoad(), but that does not overwrite the objects defined in the new scene. It is usually not advised to use DoNotDestroyOnLoad() as a core part of your game logic, as it often results in scenes being dependent on one another ("scene 1 has to define the values of a GameObject and pass it to scene 2 to be usable" = bad practice).
Solving your problem
It looks like you want score to persist as a value regardless of scene. Since all GameObjects and Monobehaviours are scoped within the scene, you can:
Force the object to be scene-analagous using the Singleton pattern.
Store score data to a file whenever it changes, and read from that file in the Start() method.
My recommended approach: Use a ScriptableObject to hold the score, and refrence that object when changing the score and updating your gameObjects. ScriptableObjects are scoped at the project level, so they automatically persist between scenes.
I'm creating a game where the player creates objects (blocks) in a "set up" scene and then when the timer ends, I'd like those objects, including their transform values to be loaded into a new scene. How would I go about loading objects created during runtime into a new scene?
DontDestroyOnLoad is a valuable way to achieve this.
What you could also do is the following:
Create an Empty object called "Cross-Scene-Objects" and add a script to it, make it so it doesn't destroy on load. Then simply child any objects to that object, and remove objects as you see fit.
You could also make the CrossSceneObjects script a singleton class and have some basic AddObject and RemoveObject methods which handle putting the game objects under the object.
If you only want certain objects in certain scenes, you could use the method above but add some further logic to set game objects active if you're in the scene you want them to show up in.
I have no example code, but if this is not enough for you to work off I can happily edit to provide code examples :) My style is to first provide the solution steps rather than the code to give you a starting point.
I'm not sure how to switch scenes and bring all of my resources with me. I do understand that upon load of new scene, the previous scene gets destroyed on load. I've explored some with DontDestroyOnLoad() but had no luck with what I'm trying to do.
I tried to make my player controller a prefab and simply put him into the next scene; however, I returned a heck of a lot of errors because of the many scripts I have. Mostly stuff like checkpoint, HP bars, and even the weapon.
What I need to know is how to import everything into the next scene. How do I do this without having to recode or even re-make all of the stuff that I need?
You're looking for LoadSceneMode.Additive. This is the second parameter of the LoadScene method and will load the new scene into the current one.
If you need to import every object from the previous scene, then what's the point in creating a new one?
What you could do is saving the objects positions into a file and then loading that file back on the next scene or try (again) with DontDestroyOnLoad();
I recommend to check the documentation of Unity about this function.
If you want an individual object not to be destroyed on the scene change then on the void Awake() function of Unity make a DontDestroyOnLoad(this.gameObject);
Could you provide more information? I ask because it seems from your description that
DontDestoryOnLoad
should accomplish what you want but you say it does not. As long as the object holding the components whose states you want to save is persisted into the next scene using that method, then all of those components' states should be persisted as well. Please elaborate and we can possibly provide a better answer. As for how to use it to save the state of every game object:
GameObject[] allObjects = UnityEngine.Object.FindObjectsOfType<GameObject>();
foreach(GameObject go in allObjects) {
if (go.activeInHierarchy) { /* and any other logic you want. Maybe like !isTerrain */
Object.DontDestroyOnLoad(go);
}
}
For the above code, I ripped it from https://answers.unity.com/questions/329395/how-to-get-all-gameobjects-in-scene.html
I have a persistent game object that I use to initialize basically everything. The script Persistence as I call it has some public references I just dragged on them via the inspector.
I'm trying to make it persistent like I found online:
public static Persistence instance;
void Awake()
{
if (instance != null)
{
DestroyImmediate(gameObject);
}
else
{
DontDestroyOnLoad(gameObject);
instance = this;
}
}
The thing is, when I load a level from main menu, it's fine. When I load back the main menu from that level, it says
MissingReferenceException: The object of type 'Persistence' has been destroyed but you are still trying to access it.
So I decided to let it create other instances when loading the main menu, but that messes up all the scripts on the game levels that rely on this data.
My question is, how to correctly implement this ~singleton persistent pattern in Unity, given I have inspector-added references?
NOTE that I initialize only from my Awake function in my persistent class and from nowhere else. Literally, my InitializeMe scripts are called from the Persistence class, one after the other.
What should I do differently to make this work? Initialize from a different, non-persistent gameobject? Forget dragging to the inspector? Any advice to make this work is appreciated.
I got it to work. So here's the thing one should know (I should have known) before messing around with persistence in Unity:
You don't drag references to a persistent GO via the inspector as they are gone the instant another scene is loaded except for additive loading. Also the persistent GO may hold data relevant to multiple scenes, hold functionality that is built for general purpose, as reusable as it goes, but it never initializes anything itself or interfere with non-persistent GOs other than being called - used as a tool - or providing the data.
This arcane wisdom is mine though, so anyone more skilled reading this, please do correct me if I'm wrong before others take this to heart.
So to be exact, I made a non-persistent master INITIALIZER that does the exact thing. I get all the scene GOs into a collection once and no GameObject.Find again. I used LINQ queries to conveniently filter my results from that collection. Another thing was my InitializeMe abstract class (or to be more precise, its descendants).
public abstract class InitializeMe : MonoBehaviour
{
public int orderNumber;
public abstract void INIT(INITIALIZER init);
}
here orderNumber is used to determine the order of the initializations, should one object depend on the other. It also worths mentioning that doing like so can result in a very predictable way to setting things up, as it is done one after the other. Note that this doesn't create a bottleneck, because Unity's scripting API only be executed in the main thread - dividing the code into multiple Awake or Start methods wouldn't perform better to my best knowledge as of 2017. It is used by a LINQ query in the INITIALIZER.
The good thing in INITIALIZER is that - again - it holds itself everything, including a reference to the persistent object and the save data as well, so via referencing itself to the InitializeMe methods they can do everything they need - as DataBank provides general purpose tools with persistent data, and INITIALIZER provides volatile (hope I use that right, I mean relevant to only main menu) data with some additional functionality that is used only for the main menu.
Now, persistence was ensured not by the persistent class itself, but by this INITIALIZER in Awake like so:
//find by tag or rename upon instantiation as by doing so will result in a "(Clone)" added to its name.
var d = GameObject.FindGameObjectWithTag("DataBank");
if (d == null)
{
//instantiating it
DATA = GameObject.Instantiate<DataBank>(DATA_blueprint);
//intializing the databank itself. The parameters are needed for me,
// but serve as a good example - all of those are dragged from the inspector to the _INITIALIZER_ but will remain in the whole game as long as databank exists.
DATA.LoadDataFromINIT(_baseColor, _baseColor2, _outlineColor,
new MMtoolBundle(DATA));
//to make it persistent.
DontDestroyOnLoad(DATA);
}else
{
//in this case the main menu is loaded from another scene.
//so we only find that loaded object and get its reference, no initialization as it was already setup.
this.DATA = d.GetComponent<DataBank>();
}
where DATA is my persistent DataBank. Note that by default I have NO DataBank object on the scene. DATA_blueprint is the prefab of DataBank dragged by the inspector (as INITIALIZER is not persistent). It could be loaded via AssetDatabase as well but this was a bit more convenient.
it worths mentioning that while DataBank is a MonoBehaviour itself so it can appear in Unity scenes, none of its members are MonoBehaviours so it is possible to harness the power of inheritance. Should one wish to start a coroutine in a non-mono toolkit, it would have to be started with a reference from DataBank itself.
An obvious drawback of this method is that the existence of my persistent GO depends on INITIALIZER, so that one has to be on the first scene. But again, as most games start with a main menu, this shouldn't pose a big issue (talking about single player games).
YET AGAIN I would recommend someone more skilled to correct me in case I lead others astray, but this solution worked for me - got the same and only persistent GO in all my scenes with no other ever created.