Forum begins after the advertisement:


[Part 12] Nullreference of SoilManager

Home Forums Video Game Tutorial Series Creating a Farming RPG in Unity [Part 12] Nullreference of SoilManager

Viewing 11 posts - 1 through 11 (of 11 total)
  • Author
    Posts
  • #16752
    Jhan Khendrick Perez
    Level 5
    Participant
    Helpful?
    Up
    0
    ::

    Good Afternoon

    I am having problem in regarding the saving of FarmData and CropData, the rest is working. Is this because of a bad arrangement in the Hierarchy? My SoilManager is placed in the PlantingArea gameobject, and the error NullReference is being point in on the Line 73 of my Code

    View post on imgur.com
    #16753
    Jhan Khendrick Perez
    Level 5
    Participant
    Helpful?
    Up
    0
    ::

    I commented as of now the lines that is causing such error

    #16756
    Jhan Khendrick Perez
    Level 5
    Participant
    Helpful?
    Up
    0
    ::

    For Reference this is what my SoilSaveState look like, CropSaveState and SoilManager

    SoilSaveState

    [System.Serializable]
    public struct SoilSaveState
    {
        public PottingSoil.SoilStatus soilStatus;
        public GameTimeStamp lastWatered;
    
        public SoilSaveState(PottingSoil.SoilStatus soilStatus, GameTimeStamp lastWatered)
        {
            this.soilStatus = soilStatus;
            this.lastWatered = lastWatered;
        }
    }

    CropSaveState

    [System.Serializable]
    public struct CropSaveState
    {
        public int soilID;
    
        public string seedToGrow;
        public NewCropBehaviour.CropState cropState;
        public int growth;
        public int health;
    
        public CropSaveState(int soilID, string seedToGrow, NewCropBehaviour.CropState cropState, int growth, int health)
        {
            this.soilID = soilID;
            this.seedToGrow = seedToGrow;
            this.cropState = cropState;
            this.growth = growth;
            this.health = health;
        }
    }

    SoilManager

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class SoilManager : MonoBehaviour
    {
        public static SoilManager Instance { get; private set; }
    
        public static Tuple<List<SoilSaveState>, List<CropSaveState>> urbanFarmData = null;
    
        public GameObject[] plantingAreas;
    
        List<PottingSoil> soilPlots = new List<PottingSoil>();
    
        List<SoilSaveState> soilData = new List<SoilSaveState>();
        List<CropSaveState> cropData = new List<CropSaveState>();
    
        private void Awake()
        {
            if (Instance != null && Instance != this)
            {
                Destroy(this);
            }
            else
            {
                Instance = this;
            }
        }
    
        private void OnEnable()
        {
            RegisterSoilPlots();
            StartCoroutine(LoadFarmData());
        }
    
        IEnumerator LoadFarmData()
        {
            yield return new WaitForEndOfFrame();
            if (urbanFarmData != null)
            {
                //Load any save data
                ImportSoilData(urbanFarmData.Item1);
                ImportCropData(urbanFarmData.Item2);
            }
        }
    
        private void OnDestroy()
        {
            urbanFarmData = new Tuple<List<SoilSaveState>, List<CropSaveState>>(soilData, cropData);
            cropData.ForEach((CropSaveState crop) => {
                Debug.Log(crop.seedToGrow);
            });
        }
    
        #region Register and Deregistering
        void RegisterSoilPlots()
        {
            foreach (GameObject plantingArea in plantingAreas)
            {
                foreach (Transform soilTransform in plantingArea.transform)
                {
                    PottingSoil potSoil = soilTransform.GetComponent<PottingSoil>();
                    if (potSoil != null)
                    {
                        soilPlots.Add(potSoil);
                        soilData.Add(new SoilSaveState());
                        potSoil.id = soilPlots.Count - 1; // Assign unique ID
                    }
                }
            }
    
            Debug.Log($"Registered {soilPlots.Count} soil plots across {plantingAreas.Length} planting areas.");
        }
    
        //Registers the crop onto the Instance
        public void RegisterCrop(int landID, SeedData seedToGrow, NewCropBehaviour.CropState cropState, int growth, int health)
        {
            cropData.Add(new CropSaveState(landID, seedToGrow.name, cropState, growth, health));
        }
    
        public void DeregisterCrop(int soilID)
        {
            cropData.RemoveAll(x => x.soilID == soilID);
        }
        #endregion
    
        #region State Changes
        //Update the corresponding Soil Data on ever change to the soil's state
        public void OnSoilStateChange(int id, PottingSoil.SoilStatus soilStatus, GameTimeStamp lastWatered)
        {
            soilData[id] = new SoilSaveState(soilStatus, lastWatered);
        }
    
        public void OnCropStateChange(int soilID, NewCropBehaviour.CropState cropState, int growth, int health)
        {
            int cropIndex = cropData.FindIndex(x => x.soilID == soilID);
    
            if (cropIndex == -1)
            {
                Debug.LogError($"No crop found with landID {soilID}");
                return;
            }
    
            string seedToGrow = cropData[cropIndex].seedToGrow;
            cropData[cropIndex] = new CropSaveState(soilID, seedToGrow, cropState, growth, health);
        }
        #endregion
    
        #region LoadingData
        public void ImportSoilData(List<SoilSaveState> soilDatasetToLoad)
        {
            for(int i = 0; i < soilDatasetToLoad.Count; i++)
            {
                SoilSaveState soilDataToLoad = soilDatasetToLoad[i];
                soilPlots[i].LoadSoilData(soilDataToLoad.soilStatus, soilDataToLoad.lastWatered);
            }
    
            soilData = soilDatasetToLoad;
        }
    
        public void ImportCropData(List<CropSaveState> cropDatasetToLoad)
        {
            cropData = cropDatasetToLoad;
            foreach (CropSaveState cropSave in cropDatasetToLoad)
            {
                PottingSoil soilToPlant = soilPlots[cropSave.soilID];
    
                NewCropBehaviour cropToPlant = soilToPlant.SpawnCrop();
                Debug.Log(cropToPlant.gameObject);
    
                SeedData seedToGrow = (SeedData)NewInventoryManager.Instance.itemIndex.GetItemFromString(cropSave.seedToGrow);
                cropToPlant.LoadCrop(cropSave.soilID, seedToGrow, cropSave.cropState, cropSave.growth, cropSave.health);
            }
        }
        #endregion
    }
    #16769
    Jonathan Teo
    Level 18
    Moderator
    Helpful?
    Up
    1
    ::

    Please provide your code for GameStateManager and GameSaveState as well

    has upvoted this post.
    #16770
    Jhan Khendrick Perez
    Level 5
    Participant
    Helpful?
    Up
    0
    ::

    GameStateManager

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class GameStateManager : MonoBehaviour
    {
        public static GameStateManager Instance { get; private set; }
    
        public Image fadeImage;
        public float fadeDuration = 1.0f;
    
        private void Awake()
        {
            if (Instance != null && Instance != this)
            {
                Destroy(this);
            }
            else
            {
                Instance = this;
            }
        }
    
        public void Sleep()
        {
            GameTimeStamp timestampOfNextDay = TimeManager.Instance.GetGameTimeStamp();
            timestampOfNextDay.day += 1;
            timestampOfNextDay.hour = 6;
            timestampOfNextDay.minute = 0;
            Debug.Log(timestampOfNextDay.day + " " + timestampOfNextDay.hour + ":" + timestampOfNextDay.minute);
    
            TimeManager.Instance.SkipTime(timestampOfNextDay);
    
            SaveManager.Save(ExportSaveState());
    
            StartCoroutine(FadeInOut());
        }
    
        private IEnumerator FadeInOut()
        {
            fadeImage.gameObject.SetActive(true);
    
            yield return StartCoroutine(Fade(0, 1));
    
            yield return new WaitForSeconds(0.5f);
    
            yield return StartCoroutine(Fade(1, 0));
    
            fadeImage.gameObject.SetActive(false);
        }
    
        private IEnumerator Fade(float startAlpha, float endAlpha)
        {
            float elapsedTime = 0f;
    
            Color color = fadeImage.color;
    
            while (elapsedTime < fadeDuration)
            {
                elapsedTime += Time.deltaTime;
                float alpha = Mathf.Lerp(startAlpha, endAlpha, elapsedTime / fadeDuration);
                fadeImage.color = new Color(color.r, color.g, color.b, alpha);
                yield return null;
            }
    
            fadeImage.color = new Color(color.r, color.g, color.b, endAlpha);
        }
    
        public GameSaveState ExportSaveState()
        {
            //Retrieving the Farm Data
            List<SoilSaveState> soilData = SoilManager.urbanFarmData.Item1;
            List<CropSaveState> cropData = SoilManager.urbanFarmData.Item2;
    
            //Retrieving the Inventory Data
            ItemSlotData[] storageSlots = NewInventoryManager.Instance.GetInventorySlots(NewInventorySlot.InventoryType.Storage);
            ItemSlotData[] harvestlots = NewInventoryManager.Instance.GetInventorySlots(NewInventorySlot.InventoryType.Harvest);
    
            ItemSlotData equippedStorageSlot = NewInventoryManager.Instance.GetEquippedSlot(NewInventorySlot.InventoryType.Storage);
            ItemSlotData equippedHarvestSlot = NewInventoryManager.Instance.GetEquippedSlot(NewInventorySlot.InventoryType.Harvest);
    
            //Time
            GameTimeStamp timestamp = TimeManager.Instance.GetGameTimeStamp();
    
            return new GameSaveState(soilData, cropData, storageSlots, harvestlots, equippedStorageSlot, equippedHarvestSlot, timestamp);
        }
    }

    GameSaveState

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    [System.Serializable]
    public class GameSaveState
    {
        //Farming Data
        public List<SoilSaveState> soilData;
        public List<CropSaveState> cropData;
    
        //Inventory
        public ItemSlotData[] storageSlot;
        public ItemSlotData[] harvestSlot;
    
        public ItemSlotData equippedStorageSlot;
        public ItemSlotData equippedHarvestSlot;
    
        //Time
        public GameTimeStamp timestamp;
    
        public GameSaveState(List<SoilSaveState> soilData, List<CropSaveState> cropData, ItemSlotData[] storageSlot, ItemSlotData[] harvestSlot, ItemSlotData equippedStorageSlot, ItemSlotData equippedHarvestSlot, GameTimeStamp timestamp)
        {
            this.soilData = soilData;
            this.cropData = cropData;
            this.storageSlot = storageSlot;
            this.harvestSlot = harvestSlot;
            this.equippedStorageSlot = equippedStorageSlot;
            this.equippedHarvestSlot = equippedHarvestSlot;
            this.timestamp = timestamp;
        }
    }
    #16771
    Jonathan Teo
    Level 18
    Moderator
    Helpful?
    Up
    1
    ::

    This might have something to do with the execution order. Is this error triggered when the player sleeps?

    has upvoted this post.
    #16772
    Jhan Khendrick Perez
    Level 5
    Participant
    Helpful?
    Up
    0
    ::

    Yes

    #16773
    Jonathan Teo
    Level 18
    Moderator
    Helpful?
    Up
    1
    ::

    Ah you can ignore this bug for now and continue following the series, it gets fixed later on. The cause is because you sleep before the variables were initialised. If you were to load into the Farm Scene and went back to your home to sleep this error would not trigger.

    Make sure that the lines you commented out are uncommented because it is needed to save the game!

    has upvoted this post.
    #16774
    Jhan Khendrick Perez
    Level 5
    Participant
    Helpful?
    Up
    0
    ::

    Will this also work even though I am only playing in one scene, since I am doing it like an urban farming in which I am farming inside my house?

    #16775
    Jonathan Teo
    Level 18
    Moderator
    Helpful?
    Up
    1
    ::

    If you want it to work in one scene you have to move everything in the OnDestroy function to a new function and have it called every time the land or crop state is updated. Right now it is designed to only save your changes globally after you leave the farm scene.

    Alternatively, you can just get rid of the urbanFarmData entirely and directly take the variables from LandManager since it is always going to exist if everything is in one scene.

    has upvoted this post.
    #16776
    Jhan Khendrick Perez
    Level 5
    Participant
    Helpful?
    Up
    1
    ::

    I will try first doing the multiple scenes so that we will be on the same page 😅

    has upvoted this post.
Viewing 11 posts - 1 through 11 (of 11 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: