Debugging Save / Load Glitches? (Serialization)
Situation as follows:
Modified Roll-a-Ball tutorial game to accept serialized saving & loading. Finally got it to work! Code is here. It was meant to save player position, score, and track which collectible objects had been picked up.
However "load" can only be used once (without "Reloading"), after which collectibles start randomly disappearing. I stuck a 'print' line into the initialization, and the console shows that after a few clicks, only "Pickup 0" through "Pickup 2" are initializing. "Pickup 0," 8, and 9 remain active and all the others disappear.
public void Restart() { //Future: Include warning message about losing unsaved progress SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex); GameControl.Instance.InitializeNR(); GameControl.Instance.Count = 0; print("Level " + SceneManager.GetActiveScene().buildIndex + " reloaded"); }
If the player uses Restart() - also called from the pause menu - Load proceeds to work as normal (for the first use only). Here's the kicker: calling the SceneManager... line to reload in the LoadGame() method causes the player to snap back to the scene origin and all collectibles to reset their Active status to true (regardless of whether they should actually be active or not). The only thing that is preserved from LoadGame() is the player's score. The same is true if the Restart() here is called from LoadGame().
So basically the glitch is, either Load will gradually stop initializing my Collectibles, or SceneManager will override all my savegame's data (except the player's score). I managed to muddle my way through this far, but I don't even know where to begin fixing this one.
EDIT: Think I figured out the root of my problem, still not sure how to solve. In GameControl.cs, my initialization calls "GameObject.Find..."
public void InitializeNR()
{
var i = 0;
foreach (GameObject go in GameObject.FindGameObjectsWithTag("Pick-up"))
{
go.SetActive(true);
print("Pickup " + i + " initialized");
Pickups[i] = go;
Collected[i] = false;
i++;
}
}
As I have since learned, GO.Find... functions don't locate inactive objects. The trouble is, I used that call specifically because loading / reloading the scene resulted in "Missing object" reference errors in the Pickups[] array, from collectibles that were destroyed/reloaded.
I suppose my question then becomes, how do I circumvent this error while avoiding the use of Find? I've seen suggestions about doing an If check for null references, but while that bypasses the error, it doesn't fix the missing reference.
Answer by KieveKRS · Jul 05, 2016 at 09:50 AM
Save/Load is now fully functional and I haven't managed to break it yet.
Step 1: All "pickup" items were parented to an empty game object, which was given the tag "PICKUPS" (yes, the caps were necessary, because reasons).
Step 2: GameControl.cs was edited as follows
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class GameControl : MonoBehaviour
{
public static GameControl Instance;
public static int TC = 12;
public GameObject pickups = new GameObject();
public GameObject[] Pickups = new GameObject[TC];
public bool[] Collected = new bool[TC];
public int Count;
public Vector3 PlayerPosition;
void Awake()
{
if (Instance == null)
{
DontDestroyOnLoad(gameObject);
Instance = this;
}
else if (Instance != this)
Destroy(gameObject);
pickups = GameObject.FindGameObjectWithTag("PICKUPS");
for(int i = 0; i < TC; i++)
{
Pickups[i] = pickups.transform.GetChild(i).gameObject;
Pickups[i].name = i.ToString();
Pickups[i].SetActive(true);
print("Pickup " + i + " initialized");
Collected[i] = false;
}
}
public void InitializeNR()
{
pickups = GameObject.FindGameObjectWithTag("PICKUPS");
for (int i = 0; i < TC; i++)
{
Pickups[i] = pickups.transform.GetChild(i).gameObject;
Pickups[i].SetActive(true);
print("Pickup " + i + " initialized");
Collected[i] = false;
}
}
}
Now a "pickups" GameObject variable holds a reference to the parent object. All child objects (the collectibles themselves) can then be referenced with .transform.GetChild(index).gameObject and loaded into the Pickups[] array, even if they've been disabled. This way, when LoadGame() calls back to GC to re-initialize, references are no longer being lost via inactive objects (which the Find was previously missing).
Special thanks to @chetanisinanand for providing the key to solving this headache.
Your answer
Follow this Question
Related Questions
Save/Load Serialization by @Cherno 0 Answers
Problem with changing scenes: Stuck in a loop where two scenes keep loading each other 0 Answers
Storing the state of a collectible in save-file? 1 Answer
Loading and Saving a Serializable Array. 1 Answer
problem with loading data with serialization in the right time 0 Answers