Listed in this answer are the fundamental ways of handling this situation. Although, most of these methods do not scale well to large projects. If you want something more scalable and are not afraid of getting your hands dirty, check out the answer by Lea Hayes about Dependency Injection frameworks.
1. A static script for holding data only
You can create a static script to hold data only. Since it is static, you don't need to assign it to a GameObject. You can simply access your data like ScriptName.Variable = data;
etc.
Pros:
- No instance or singleton required.
- You can access data from everywhere in your project.
- No extra code to pass values between scenes.
- All variables and data in a single database-like script make it easy to handle them.
Cons:
- You will not be able to use a Coroutine inside the static script.
- You will probably end up with huge lines of variables in a single class if you don't organize well.
- You can't assign fields/variables inside the editor.
An Example:
public static class PlayerStats
{
private static int kills, deaths, assists, points;
public static int Kills
{
get
{
return kills;
}
set
{
kills = value;
}
}
public static int Deaths
{
get
{
return deaths;
}
set
{
deaths = value;
}
}
public static int Assists
{
get
{
return assists;
}
set
{
assists = value;
}
}
public static int Points
{
get
{
return points;
}
set
{
points = value;
}
}
}
2. DontDestroyOnLoad
If you need your script to be assigned to a GameObject or derive from MonoBehavior, then you can add DontDestroyOnLoad(gameObject);
line to your class where it can be executed once (Placing it in Awake()
is usally the way to go for this).
Pros:
- All MonoBehaviour jobs (for example Coroutines) can be done safely.
- You can assign fields inside the editor.
Cons:
- You will probably need to adjust your scene depending on the script.
- You will probably need to check which secene is loaded to determine what to do in Update or other general functions/methods. For example, if you are doing something with UI in Update(), then you need to check if correct scene is loaded to do the job. This causes loads of if-else or switch-case checks.
3. PlayerPrefs
You can implement this if you also want your data to be stored even if the game gets closed.
Pros:
- Easy to manage since Unity handles all background process.
- You can pass data not only between scenes but also between instances (game sessions).
Cons:
- Uses file system.
- Data can easily be changed from prefs file.
4. Saving to a file
This is a bit overkill for storing values between scenes. If you don't need encryption, I discourage you from this method.
Pros:
- You are in control of data saved as opposed to PlayerPrefs.
- You can pass data not only between scenes but also between instances (game sessions).
- You can transfer the file (user-generated content concept relies on this).
Cons:
- Slow.
- Uses file system.
- Possibility of reading/loading conflicts caused by stream interruption while saving.
- Data can easily be changed from the file unless you implement an encryption (Which will make the code even slower.)
5. Singleton pattern
Singleton pattern is a really hot topic in object-oriented programming. Some suggest it, and some don't. Research it yourself and make the appropriate call depending on your project's conditions.
Pros:
- Easy to both set-up and use.
- You can access data from everywhere in your project.
- All variables and data in a single database-like script make it easy to handle them.
Cons:
- Lots of boilerplate code whose only job is to maintain and secure the singleton instance.
- There are strong arguments against the use of singleton pattern. Be cautious and make your research beforehand.
- Possibility of data clash due to poor implementation.
- Unity may have a difficulty handling singleton patterns1.
1: In the summary of OnDestroy
method of Singleton Script provided in Unify Wiki, you can see the author describing ghost objects which bleed into the editor from runtime:
When Unity quits, it destroys objects in a random order. In principle, a Singleton is only destroyed when application quits. If any script calls Instance after it have been destroyed, it will create a buggy ghost object that will stay on the Editor scene even after stopping playing the Application. Really bad! So, this was made to be sure we're not creating that buggy ghost object.