tommer
tommer

Reputation: 1

Scene save&load works fine when playing scene directly,but when i load from main menu it's null exception

when i starting my game on the game scene,everything is fine,but when i load from menu,first time is ok,then there is saving data to json,and when i come back to main menu,and then again press play it's " Object reference not set to an instance of an object " for my saveScript which is attached to empty gameObject on game scene.

loading just

SceneManager.LoadSceneAsync("game");

SAVE SCRIPT

public void PopulateSaveData(SaveData sd)
    {
        //character
        sd.s_XP = XP;
        respawnScript.instance.PopulateSaveData(sd);
        sd.s_x = gameObject.transform.position.x;
        sd.s_y = gameObject.transform.position.y;
        sd.s_z = gameObject.transform.position.z;

        playerHealth.instance.PopulateSaveData(sd);
        //sword
        playerSword.instance.PopulateSaveData(sd);
        //inventory
        Inventory.instance.PopulateSaveData(sd);
        //sd.s_allItemsPositions = allItemsPositions;
        //sd.s_allPotionInteractions = GameObject.FindObjectsOfType<potionInteraction>().ToList();
        if (allPotionInteractionO.Count > 0)
        {
            Debug.Log("ALL POTIONS COUNT " + allPotionInteractionO[0]);
            sd.s_allPotionInteractions = allPotionInteractionO;
        }
        
        sd.s_allWeaponInteractions = all_swords;
        sd.s_allWeaponInteractionsGO = all_swordsGO;

        for (int i = 0; i < all_swordsGO.Count; i++)
        {
            sd.s_allSwordsRotation.Add(all_swordsGO[i].transform.rotation);
        }
        for (int i = 0; i < all_swordsGO.Count; i++)
        {
            //because some objects can be destroyed thats why im initializing list again
            //allInteractableGameObjects = GameObject.FindGameObjectsWithTag("interactable object").ToList();
            // sd.s_allItemsPositions.Add(allInteractableGameObjects[i].transform.position);
            sd.s_allSwordsPositions.Add(all_swordsGO[i].transform.position);

        }
        for (int i = 0; i < allPotionInteractionO.Count; i++)
        {
            sd.s_allPotionsPositions.Add(allPotionInteractionO[i].transform.position);
        }
        ///going through list and check if there is used item,then saving its bool to another list at same position
        for (int i = 0; i < allPotionInteractionO.Count; i++)
        {
            if (allPotionInteractionO[i].isUsed)
            {
                allPotionsIsUsed[i] = true;
            }
        }
        sd.s_allPotionsIsUsed = allPotionsIsUsed;
        //quest part
        for (int i = 0; i < allNPCS.Count; i++)
        {
            if (allNPCS[i].quest == null)
            {
                allNpcQuests[i] = null;
            }
        }
        sd.s_allQuests = allNpcQuests;
        if(MarieleQuest.instance.currentMarieleQuest!=null)
        {
            sd.s_currentQuest = MarieleQuest.instance.currentMarieleQuest;
        }
        foreach (Slot slot in slotsToSave)
        {
            slot.PopulateSaveData(sd);

        }
        //enemies
        foreach (EnemyStats enemy in all_enemies)
        {
            enemy.PopulateSaveData(sd);
        }
        //procedural enemies
        foreach (ProceduralStats enemy in all_procedural_enemies)
        {
            enemy.PopulateSaveData(sd);
        }
    } 

LOAD

public void LoadFromSaveData(SaveData sd)
    {
        if (SceneManager.GetActiveScene().name == "game")
        {
            //character
            XP = sd.s_XP;
            if (sd.s_HP > 0)
            {
                playerHealth.instance.LoadFromSaveData(sd);
            }
            else if (sd.s_HP == 0)
            {
                playerHealth.instance.currentHealth = 100;
            }
            if (sd.s_respawnObject != null)
            {
                respawnScript.instance.LoadFromSaveData(sd);
            }
            if (sd.s_x != 0 && sd.s_y != 0 && sd.s_z != 0)
            {
                gameObject.transform.position = new Vector3(sd.s_x, sd.s_y, sd.s_z);
            }
            //sword
            if (sd.s_sword != null && sd.s_temp != null && sd.s_currentSword != null && sd.s_currentSwordGO != null)
            {
                playerSword.instance.LoadFromSaveData(sd);

            }
            else
            {
                playerSword.instance.currentSwordGameObject = GameObject.Find("character/mixamorig:Hips/mixamorig:Spine/mixamorig:Spine1/" + playerSword.instance.currentSwordGameObject.name);
                playerSword.instance.temp = playerSword.instance.currentSwordGameObject;
            }


            //potions
            if (sd.s_allPotionsIsUsed.Count > 0)
            {
                allPotionsIsUsed = sd.s_allPotionsIsUsed;
            }
            //quest
            if (sd.s_allQuests.Count > 0)
            {
                Debug.Log("SD ALL QUESTS" + sd.s_allQuests);
                allNpcQuests = sd.s_allQuests;
            }
            if (sd.s_allQuests.Count > 0)
            {
                for (int i = 0; i < allNPCS.Count; i++)
                {
                    allNPCS[i].quest = allNpcQuests[i];
                }
            }
            if (sd.s_currentQuest != null)
            {
                MarieleQuest.instance.currentMarieleQuest = sd.s_currentQuest;
            }
          

            if (sd.s_allPotionInteractions.Count > 0)//second pattern in if is changed(FOR REMEMBER)
            {
                Debug.Log("SD ALL POTIONS > 0");
                if (sd.s_allPotionInteractions[0] != null)
                {
                    allPotionInteractionO = sd.s_allPotionInteractions;
                }
                else
                {
                    allPotionInteractionO = GameObject.FindObjectsOfType<potionInteraction>().ToList();
                }
                for (int b = 0; b < allPotionInteractionO.Count; b++)
                {
                    allPotionInteractionO[b].isUsed = allPotionsIsUsed[b];
                }
                for (int i = 0; i < allPotionInteractionO.Count; i++)
                {

                    if (allPotionInteractionO[i].isUsed)
                    {
                        allPotionInteractionO[i].gameObject.SetActive(false);
                    }
                }
            }


            Inventory.instance.LoadFromSaveData(sd);
            if (sd.s_allWeaponInteractions.Count > 0)
            {

                //all_swords = sd.s_allWeaponInteractions;
               // all_swordsGO = sd.s_allWeaponInteractionsGO;
                for (int i = 0; i < all_swordsGO.Count; i++)
                {
                    if (!Inventory.instance.itemsGameObjects.Contains(all_swordsGO[i])
                    && all_swordsGO[i] != playerSword.instance.currentSwordGameObject)
                    {
                        all_swordsGO[i].SetActive(true);

                        all_swordsGO[i].transform.rotation = sd.s_allSwordsRotation[i];


                    }
                }
                for (int j = 0; j < sd.s_allSwordsPositions.Count; j++)
                {
                    //Debug.Log(sd.s_allItemsPositions[j] + " " + allInteractableGameObjects[j].name);
                    all_swordsGO[j].transform.position = sd.s_allSwordsPositions[j];
                    allPotionInteractionO[j].transform.position = sd.s_allPotionsPositions[j];
                }
            }

            foreach (Slot slot in slotsToSave)
            {
                slot.LoadFromSaveData(sd);

            }
            //enemies
            foreach (EnemyStats enemy in all_enemies)
            {
                enemy.LoadFromSaveData(sd);
            }
            foreach (string id in dead_enemies_ids)
            {
                SaveData.EnemyData enemyData = new SaveData.EnemyData();
                enemyData.e_Health = 0;
                enemyData.e_id = id;
                sd.enemyData.Add(enemyData);
            }
            //procedural enemies
            foreach (ProceduralStats enemy in all_procedural_enemies)
            {
                enemy.LoadFromSaveData(sd);
            }
            foreach (string id in all_procedural_ids)
            {
                SaveData.ProceduralEnemyData enemyData = new SaveData.ProceduralEnemyData();
                enemyData.e_ProcHealth = 0;
                enemyData.e_ProcId = id;
                sd.proceduralEnemyData.Add(enemyData);
            }
        }
    }

these two scripts just writing data to lists,and reading it from saved file, saving is called once OnApplicationQuit()
load called at the end of Start()
when i open scene game just from itself and close and load again,everything work fine,because its destroying objects and load them again,but
once i start from main menu,from there do LoadScene("game") i'm getting error that some fields are not exist anymore,but it's like unreal
SaveData script which have all info and store it to file,then read

public class SaveData : MonoBehaviour
{
    private void Awake()
    {
        
        
    }
    private void Start()
    {
        s_inventoryGO = new List<GameObject>();
        Debug.Log("s_inventoryGO" + s_inventoryGO.Count);
    }
    //character saving
    public int s_XP;
    public GameObject s_respawnObject;
    public float s_x;
    public float s_y;
    public float s_z;
    //inventory
    public List<Item> s_inventory = new List<Item>();
    public List<GameObject> s_inventoryGO = new List<GameObject>();
    public List<GameObject> s_allGameObjectInventory = new List<GameObject>();
    
    public List<GameObject> s_allInteractableObjects = new List<GameObject>();
    public List<potionInteraction> s_allPotionInteractions = new List<potionInteraction>();
    public List<weaponInteract> s_allWeaponInteractions = new List<weaponInteract>();
    public List<GameObject> s_allWeaponInteractionsGO = new List<GameObject>();
    public List<Quaternion> s_allSwordsRotation = new List<Quaternion>();
    public List<Vector3> s_allSwordsPositions = new List<Vector3>();
    public List<Vector3> s_allPotionsPositions = new List<Vector3>();
    public List<bool> s_allPotionsIsUsed = new List<bool>();
    public List<Quest> s_allQuests = new List<Quest>();

    public Quest s_currentQuest;
    public int s_HP;
    [System.Serializable]
    public struct SlotsData
    {
        public Item s_slotItem;
        public string s_id;
        //public Sprite s_icon ;
    }
    public List<SlotsData> s_slots = new List<SlotsData>();
    
    //sword
    public Transform s_sword;
    public GameObject s_currentSwordGO;
    public GameObject s_temp;
    public swordEquipping s_currentSword;
    //enemy saving
    [System.Serializable]
    public struct EnemyData
    {
        public int e_Health;
        public string e_id;
    }
    public List<EnemyData> enemyData = new List<EnemyData>();
    //procedural enemy saving
    [System.Serializable]
    public struct ProceduralEnemyData
    {
        public int e_ProcHealth;
        public string e_ProcId;
    }
    public List<ProceduralEnemyData> proceduralEnemyData = new List<ProceduralEnemyData>();
    public string toJson()
    {
        return JsonUtility.ToJson(this);
    }
    public void LoadFromJson(string json)
    {

        JsonUtility.FromJsonOverwrite(json, this);
    }


}

Upvotes: 0

Views: 441

Answers (1)

TEEBQNE
TEEBQNE

Reputation: 6275

When using DontDestroyOnLoad wherever you want this object to exist, make a copy of it in your scene if you want the data stored to persist between your game session.

Here is a snippet from the docs

void Awake()
{
    GameObject[] objs = GameObject.FindGameObjectsWithTag("music");

    if (objs.Length > 1)
    {
        Destroy(this.gameObject);
    }

    DontDestroyOnLoad(this.gameObject);
}

It will search for other objects in the scene with the same tag. If it finds it, it will destroy itself. What you can do here is have a public Init() function on your script. When a new scene is loaded with the object and your object already finds an instance of itself, it can pass the local references to the existing object. Something like...

public GameObject objRef1;
public GameObject objRef2;
  
public void Init(GameObject obj1, GameObject obj2)
{
    objRef1 = obj1;
    objRef2 = obj2;
    ...
    etc.
}

Inside of the Awake() of the new object, you would call the existing objects Init with the local references it has, which work as it loaded with your scene.

void Awake()
{
    // make the tag unique so you only grab objects of this save type
    GameObject[] objs = GameObject.FindGameObjectsWithTag("yourObjectsTag");

    if (objs.Length > 1)
    {
        objs[0].GetComponent<YourScriptName>().Init(obj1, obj2, ..., etc.);
        Destroy(this.gameObject);
    }

    DontDestroyOnLoad(this.gameObject);
}

In this approach it means you have a local copy of your DontDestroyOnLoad object in every scene so when the existing one loads in, it can just grab the references from it.

Upvotes: 0

Related Questions