How can i save the data of some variables across scenes? [duplicate] - c#

This question already has answers here:
How to pass data (and references) between scenes in Unity
(6 answers)
Closed 3 years ago.
I am making a game that involves the score of the player. This score is always displayed on the screen. However when i change scenes the score resets back to 0. How can i make it so that the score gets saved across all of my scenes?
I have looked on the internet for hours but i was not able to fix it that way. The 2 main things that i came across were this:
Static variables
DontDestroyOnLoad()
While i am sure that these would fix my issue, i have no idea how to implement them in my code. Do i make a new script? Do i put them in an existing script? And if so, which one? How can call upon variables if they are static?
Thanks in advance for any help!

If you're just trying to be able to access simple data across screens, static variables seem like the most straightforward solution. It's fine if you've never used them before, they're very easy to use once you get used to them. :)
The first thing to do is create a class that will hold your static variables (yes, this means writing a new script and putting it in your Scripts folder). It's possible to add new static variables on a class you're already using, but if you're just learning I would recommend keeping things like that separated so it's more organized.
public class GameValues {
public static int score { get; set; }
// You can add other static variables here.
}
Notice I didn't inherit from MonoBehaviour. This is fine because we don't need to add this script to a GameObject, it's just going to be used to store our data. Now if we need to change or read the score, we can do it like this.
public class Example : MonoBehaviour {
public int scoreLimit = 100;
private void Update () {
/*
* When accessing static members of a class, no object reference is needed.
* Instead you access it as though it were a member of the class.
*/
int currentScore = GameValues.score;
if (Input.GetKeyDown (KeyCode.Space) && (currentScore < scoreLimit)) {
GameValues.score += 1;
}
}
}
Obviously this is a rather silly example, but you get the idea. If you'd like to do some further reading, here is the official Microsoft documentation on the static keyword.
Happy coding!

You have a couple of options.
1) PlayerPrefs
Unity has an in-built save procedure called PlayerPrefs that will store data between scenes and game restarts.
//Store Score Value
PlayerPrefs.SetInt("Score", Score);
// Retrieve Score Value if there is one
if (PlayerPrefs.HasKey("Score"))
{
Score = PlayerPrefs.GetInt("Score");
}
PlayerPrefs Documentation
2) Unity Singleton
The Unity Singleton differs from a normal programming languages slightly in its use of DontDestroyOnLoad to keep the GameObject holding the Singleton alive between scenes.
public class UnitySingleton : MonoBehaviour
{
public static UnitySingleton Instance { get; private set; }
public int Score { get; set; }
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
}
else
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
}
}
Usage
//Store Score
UnitySingleton.Instance.Score = value;
//Retrieve Score if set otherwise (it will) return 0
Score = UnitySingleton.Instance.Score;

Alright here's a minimal example
Save data object:
using UnityEngine;
public class SaveDataHere : MonoBehaviour {
public string myData;
public static string staticData = "Static data is still here";
// Use this for initialization
void Start () {
DontDestroyOnLoad(this);
myData = "I didn't get destroyed haha";
}
}
In the new scene:
using UnityEngine;
public class InNewScene : MonoBehaviour {
// Use this for initialization
void Start () {
var saveData = FindObjectOfType<SaveDataHere>();
Debug.Log("instance data is " + saveData.myData);
Debug.Log("static data is " + SaveDataHere.staticData);
}
}

Related

Change values from other scripts without having the target script attached to an GameObject

iam pretty new in C# and Unity and facing currently following issue in my game project.
Ive a class to instantiate for various shops each shop makes a different profit has a different name and coasts different power recourses. So my idea is to have a class to make those shops and on constructor call e.g shop creation it should change variables of another script where the balance, power, and so on is calculated here the example code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Shops
{
public int shopID;
public string shopName;
public float shopPower;
public double shopCoast;
public string shopType;
public double shopProfit;
public Shops(int id, string name, float power, double coast, string type, double profit)
{
this.shopID = id;
this.shopName = name;
this.shopPower = power;
this.shopCoast = coast;
this.shopType = type;
this.shopProfit = profit;
Debug.Log("Shop created");
addProfit(profit);
subPower(power);
subCoast(coast);
}
public void addProfit(double profit)
{
}
public void subPower(float power)
{
}
public void subCoast(double coast)
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Calculations : MonoBehaviour
{
public double money;
public float power;
public double profitPerHour, coastPerHour, totalPerHour;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
Is it correct or lets say common the way ive written it and that the constructor calls a few methods on call? And how can i accsess the variable double money from the Calculations : Monobeheivor script without the script being attached to a game object? Is there a way without attaching it to a gameobject or not? My idea is that the calculation script does all the calculations needed and iam just grabing the values for my UI from this script. Or wont the update() and start function work?
And one more Question
How can i store all created classobjects in a list or array so that i can print it later in a table where its sorted for example from lowest to highest profit. What is better to use an array or a list?
I hope i made my question clear and thank you in advance.
Like #Ben Rubin said in his comment, it is not a good idea to do another things than initialization of variables in the constructor. But you could do a method eg. Init() which will calls the methods on other classes.
If you have a calculation instance for each shop, then maybe you should merge the 2 classes in only one.
However, if your calculation is global for all the shops, you could make it singleton, without having to make it a MonoBehaviour. And inside you register your shops in a list where you can then access later inside the Calculations class.
public class Shop
{
...
public Shop(...)
{
// initialize only your variables
}
public void Init()
{
Calculations.Instance.RegisterShop(this, true);
// Do your calls to other classes here
}
public void Clear()
{
Calculations.Instance.RegisterShop(this, false);
}
...
}
public class Calculations
{
private static Calculations m_instance = null;
public static Calculations Instance { // singleton
get {
if(m_instance == null)
m_instance = new Calculations();
return m_instance;
}
}
private List<Shop> shopList = new List<Shop>();
public RegisterShop(Shop shop, bool register)
{
bool isRegistered = shopList.Contains(shop);
if(register && !isRegistered)
shopList.Add(shop);
else if(!register && isRegistered)
shopList.Remove(shop);
}
...
}
And for exemple in a script ShopSeller, attached to a GameObject in your scene (I don't know if you do something like that)
public class ShopSeller : MonoBehaviour
{
private Shop shop = null;
public void Start()
{
shop = new Shop(...);
shop.Init();
}
public void OnDestroy()
{
shop?.Clear();
}
...
}
Note that singleton needs to be used wisely, it is not the answer for all difficulties you encounter. It is the only way I can think of for you to not make a MonoBehaviour and attach it to a GameObject to use it.
For your bonus question, you could refer to this chart if you want to choose an appropriate container for what you want to do.
It uses the C++ container types, but it remain the same with some translations to C# :
vector = List
list = LinkedList
map = Dictionary
set = HashSet
deque = no equivalent in C#
The only things it lacks is the basic array, it is useful when you know exactly the length, and you know it will never grow or shrink during runtime.

Unity : Singleton ScriptableObjects resets after play

I have a singleton class that contains general information about my game.
public class GeneralGameData : ScriptableObject
{
private static GeneralGameData _currentGeneralGameData;
public static GeneralGameData CurrentGeneralGameData
{
get
{
if (_currentGeneralGameData == null)
{
_currentGeneralGameData = CreateInstance<GeneralGameData>();
}
DontDestroyOnLoad(_currentGeneralGameData);
return _currentGeneralGameData;
}
}
public string GameName;
public string GameVersion;
}
This class has no presents in the scene.
I also have a window to show and change that
public class GeneralGameDataMenuItem : EditorWindow
{
[MenuItem("CoreMaker/GeneralGameData")]
private static void _generalGameData()
{
GetWindow<GeneralGameDataMenuItem>("GeneralGameData");
}
void OnGUI()
{
GeneralGameData.CurrentGeneralGameData.GameName = EditorGUILayout.TextField("GameName", GeneralGameData.CurrentGeneralGameData.GameName);
GeneralGameData.CurrentGeneralGameData.GameVersion = EditorGUILayout.TextField("GameVersion", GeneralGameData.CurrentGeneralGameData.GameVersion);
EditorUtility.SetDirty(GeneralGameData.CurrentGeneralGameData);
}
}
The problem is that it wont save my changes after i hit play or restart unity.
any solutions??
A ScriptableObject is intended to be saved as an asset. Add the line [CreateAssetMenu(menuName = "MyGame/GeneralGameData")] above your ScriptableObject class declaration, then right click in the project pane, click Create > MyGame > GeneralGameData. Fill in all the fields you need. Any script that needs to reference it can just add a public field of type GeneralGameData and add that asset in the inspector.
This is the fixed code based on Nailuj29s answer but you do not need to get reference to it by having a public field , instead you just need to use GeneralGameData.CurrentGeneralGameData.
public class GeneralGameData : ScriptableObject
{
private static GeneralGameData _currentGeneralGameData;
public static GeneralGameData CurrentGeneralGameData
{
get
{
if (_currentGeneralGameData == null)
{
if (AssetDatabase.FindAssets("GeneralGameData", new []{ "Assets/CoreMaker" }).Length != 1)
{
_currentGeneralGameData = CreateInstance<GeneralGameData>();
if (!AssetDatabase.IsValidFolder("Assets/CoreMaker"))
{
AssetDatabase.CreateFolder("Assets", "CoreMaker");
}
AssetDatabase.CreateAsset(_currentGeneralGameData,"Assets/CoreMaker/GeneralGameData.asset");
}
}
_currentGeneralGameData = AssetDatabase.LoadAssetAtPath<GeneralGameData>("Assets/CoreMaker/GeneralGameData.asset");
return _currentGeneralGameData;
}
}
public string GameName;
public string GameVersion;
}
Keep in mind that when you reference GeneralGameData.CurrentGeneralGameData it is going to create and asset , if you delete that asset you are going to lose you data.
The reason you lose your data is because there are 2 ways people use ScriptableObject for singletons. The key difference is how the static value is set. (e.g. the _currentGeneralGameData = CreateInstance<GeneralGameData>() line in your code)
From an ASSET RESOURCE:
This is a shared, "actual" object/file that exists in your project Resources. When you change it, the changes are "permanent" (saved to that actual object), which I believe is what you're looking for. In your case, this would mean simply grabbing a reference to your existing asset resource, instead of creating a new instance, something along the lines of:
_currentGeneralGameData = Resources.Load<GeneralGameData>("GeneralGameData");
or
_currentGeneralGameData = Resources.FindObjectsOfTypeAll<GeneralGameData>().FirstOrDefault();
Note: Skipping verification/error handling code for clarity.
Important: Runtime (standalone) versus editor will get different behavior: you may need to put an explicit reference to the ScriptableObject asset in your scene to make sure it is not optimized out. More details at: https://baraujo.net/unity3d-making-singletons-from-scriptableobjects-automatically/
From an INSTANCE of an asset:
This is your current code. Instead of grabbing a shared asset, you create a FRESH instance, disconnected from what is saved on disk, which means you will not be saving any of the changes you make at runtime.
This is perfectly fine, and possibly preferable in some cases, but if you do want to keep changes then you should use the asset resource method.

Inheritance-Need Guidance

I am a novice programmer, so sorry if this is a really stupid question. I need to pass a value from one class to another. If I am not mistaken, this would be done through inheritance, but there seems to be something I am missing. Any help would be welcome. I am writing in C# within Unity.
public class baseStats{
private int STAMINA;
public static int Stamina{
get{return STAMINA;}
set{STAMINA = value;}
}
}
The above is a sample of my parent class. Here is my child class:
public class Profile : baseStats {
private static int PROFILE_STAMINA;
private void Stats ()
{
PROFILE_STAMINA = Stamina;
}
public static int profileStamina
{
get{return PROFILE_STAMINA;}
}
}
In a third script I created for the purposes of debugging, I wrote:
public class debug:MonoBehaviour{
void Start(){
Debug.Log(Profile.profileStamina.ToString());
}
}
It says I need an object reference for the non-static field, method, or property
'Profile.Profile.Stamina'. Easy fix: make everything static (I believe I need to make constructors or something like that to avoid having to use static all the time. A little help with that would be great!). Once error-free, I ran the program and the console outputs 0 for my profile stamina. I am really confused as to what is going on. Sorry this is such a long post, and thanks for any help I get.
I need to pass a value from one class to another. If I am not
mistaken, this would be done through inheritance.
No, you don't need inheritance to pass values from one class to another. If you don't know what inheritance is, you shouldn't be using it. It's very likely you don't need it. The-same thing applies to the static keyword. New programmers tend to make everything static when they don't know what the new keyword is used for.
Simply create a new instance of the Stats script inside the Profile constructor script. To use the Profile script, create a new instance of it which will automatically call the constructor that will create a new instance of the Stats script. You shouldn't be doing this with static variables and functions. Totally unnecessary. Values can be passed to another script with auto property or function but my solution used auto property to make it simple.
public class Stats
{
private int STAMINA;
public int Stamina
{
get { return STAMINA; }
set { STAMINA = value; }
}
}
public class Profile
{
private Stats stats;
private int PROFILE_STAMINA;
//Init only
public Profile()
{
stats = new Stats();
}
//Init with Stats stamina and profileStamina stamina values
public Profile(int stamina, int profileStamina)
{
stats = new Stats();
stats.Stamina = stamina;
PROFILE_STAMINA = profileStamina;
}
//Stamina From Profile
public int profileStamina
{
get { return PROFILE_STAMINA; }
set { PROFILE_STAMINA = value; }
}
//Stamina From Stats
public int Stamina
{
get { return stats.Stamina; }
set { stats.Stamina = value; }
}
}
Usage:
public class debug:MonoBehaviour{
Profile profile = null;
void Start()
{
//Creata new profile and set Stats stamina and ProfileStamina stamina values
profile = new Profile(10, 5);
profile.Stamina = 60;
profile.profileStamina = 60;
Debug.Log(profile.profileStamina.ToString());
}
Also take time and learn about Classes,Properties.
You're trying to access a non-static from a static property.
This code is the main problem:
public static int Stamina{
get{return STAMINA;}
set{STAMINA = value;}
}
STAMINA is defined as private int STAMINA. It isn't static. Quite simply, this means that inside every object of the type baseStats you have an int called STAMINA you can fill in. However, you're not working on an object there; you're in a static function, which is something that can be executed without an object. Since there is no object, there is no STAMINA int either; there's no object to get or fill in that property on.
On that note, your function profileStats() has the same issue; it tries to access the non-static STAMINA as well, again without having any kind of object. Any reference to just plain STAMINA is actually to this.STAMINA, and inside a static, there's no this.
I'm not sure what the purpose of this whole setup is; personally I'd avoid using statics containing global data like that. Instead I'd just keep an object around containing the necessary properties which is simply passed on to any functions that need to access the data in it. But anyway, from what I can see, you simply forgot to put static on that private int STAMINA.
Side note: this is completely unrelated to inheritance. Since Stamina is a public static property, and statics can't be inherited (except in some peculiar advanced cases with inherited generics), your "child class" never even needs the parent class as parent, as long as it references baseStats.Stamina instead of STAMINA. This can easily be shown by making both classes static.

How to call a function from one script attached to a game object to another in C#?

Basically I have one script set up which during play prints to the console.
//Method to loop find question to be asked and print it as well as loop through answerText and print the appropriate answer options/
public void AskNewQuestion ()
{
//prints Person: + apropriate question based on questionUpTo.
Debug.Log ("Person:" + questionsText [questionUpTo]);
//Loops through answersText and compares it to questionUpTo to find applicable answers.
for (int i = 0; i < answerText[questionUpTo].Count; i++)
{
//Prints applicable answers to console.
Debug.Log(i+1 + ":" + answerText[questionUpTo][i]);
}
}
Now I need this to print not to the console, but through another script which handles a canvas, and the canvas is where I need the text to print to.
public class Textboxmanager : MonoBehaviour {
public GameObject textBox;
public Text theText;
public TextAsset textFile;
public string[] textLines;
public int currentLine;
public int endAtLine;
public bool isActive;
// Use this for initialization
void Start ()
{
if(textFile != null)
{
textLines = (textFile.text.Split('\n'));
}
if(endAtLine == 0)
{
endAtLine = textLines.Length - 1;
}
if(isActive)
{
EnableTextBox();
}
else
{
DisableTextBox();
}
}
void Update()
{
if(!isActive)
{
return;
}
theText.text = textLines[currentLine];
if(Input.GetKeyDown(KeyCode.Return))
{
currentLine += 1;
}
if(currentLine > endAtLine)
{
DisableTextBox();
}
}
public void EnableTextBox()
{
textBox.SetActive(true);
}
public void DisableTextBox()
{
textBox.SetActive(false);
}
}
There are many ways in which you can accomplish this. First of all, i notice that the script from which you are trying to print to is a manager of sorts. In that case there is a high level concept called Singletons.
Here is a link on how to implement Singletons in C-Sharp:
https://unity3d.com/learn/tutorials/projects/2d-roguelike-tutorial/writing-game-manager
Watch the unity video on them and make sure this is the right approach to your problem.
If you don't want to go down that complex path and would rather avoid the headache then i will try and explain how to reference another script and all of its functions, variables and other data without singletons.
Class1 {
public Class2 class2Reference;
private void printTextFromClass2() {
class2Reference.printAnswerText();
}
}
Class2 {
public void printAnswerText() {
Debug.Log("String");
}
}
In this example you can see that i have two classes, these may be in different scripts and on completely different gameObjects within Unity. We created a variable with the datatype as the class name we are trying to access. In this case the datatype is Class2. We then use it as a variable any time that we need to access that script.
The only thing that you must do is give it a script to reference. Much like a string is null until we give it a value, our variable 'private Class2 class2Reference' is null until we give it a value.
This is the easy part. Save and go back to unity. You should see your variable in the inspector with "none" written next to it. Drag and drop your second script into that box and you should be ready to rumble.
Hope this helps you out buddy.
Instantiate the AskNewQuestion class inside the TextboxManager class.
puclic TextboxManager : Monobehavior {
AskNewQuestion askNewQuestion = new AskNewQuestion();
}
Make a function inside AskNewQuestion that returns the array string. Then call it on the TextboxManager class to get the array string. Like this: textLines = askNewQuestion.questions;.
Edit: You can use a { get; set; } method with this. First initialize on where you store the string array then apply it on the { get; set; } method.
string[] questions;
public string[] Questions {
get {
return quesions;
}
}

NullReferenceException error in Unity when trying to get data from a static class

I'm working on the save system for a local co-op game I'm working on. The goal of the code is to establish a Serializable Static class that has instances of the four players and the relevant data they need to store for saving in binary.
[System.Serializable]
public class GameState {
//Current is the GameState referenced during play
public static GameState current;
public Player mage;
public Player crusader;
public Player gunner;
public Player cleric;
public int checkPointState;
//Global versions of each player character that contains main health, second health, and alive/dead
//Also contains the checkpoint that was last activated
public GameState()
{
mage = new Player();
crusader = new Player();
gunner = new Player();
cleric = new Player();
checkPointState = 0;
}
}
The Player Class just contains the ints that track player stats and a bool for if they are in the alive state or not. My issue comes when one of my classes in the game scene needs to get data from this static class. When the static class is referenced, it throws the error.
void Start () {
mageAlive = GameState.current.mage.isAlive;
if (mageAlive == true)
{
mageMainHealth = GameState.current.mage.mainHealth;
mageSecondHealth = GameState.current.mage.secondHealth;
} else
{
Destroy(this);
}
}
I am new to coding so I'm not sure how Unity interacts with static classes that don't inherit from MonoBehaviour. I based this code off of a tutorial that worked pretty similarly, so I'm not sure what the issue is.
Nothing is initialising current.
One quick solution is to initialise current like this:
public static GameState current = new GameState();
This is the Singleton pattern, you can read a about it all over the web, but this post by Jon Skeet is a fairly decent place to start.
I'd consider making the GameState constructor private, and making current (aka Instance normally) into a property with only a getter:
private static GameState current = new GameState();
public static GameState Current
{
get { return current; }
}
There are many more ways to do this, especially if multi-threading is a concern, then you should read the Jon Skeet post.
To give another perspective: If you want to implement that as a static class, then this works differently, starting from referencing the class and its data and not ending with the constructor:
public class GameState {
// not needed here, because static
// public static GameState current;
public static Player mage;
public static Player crusader;
public static Player gunner;
[...]
public static GameState() {
[...]
Of course your methods would reference this static class' static data different now too:
void Start () {
mageAlive = GameState.mage.isAlive;
if (mageAlive == true) {
mageMainHealth = GameState.mage.mainHealth;
mageSecondHealth = GameState.mage.secondHealth;
If you want a (serializable!) Singleton - see DaveShaws answer.

Categories

Resources