Forum begins after the advertisement:
[Part 11] The tree wilted after scene transitions
Home › Forums › Video Game Tutorial Series › Creating a Farming RPG in Unity › [Part 11] The tree wilted after scene transitions
- This topic has 2 replies, 3 voices, and was last updated 3 weeks, 5 days ago by
Jonathan Teo.
-
AuthorPosts
-
March 27, 2025 at 6:01 pm #17626::
View post on imgur.com
After I finished part 11 something happened. I planted some seeds and moved to another scene and then came back. Some seeds disappeared, some turned into withered plants.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Land : MonoBehaviour, ITimeTracker { public int id; public enum LandStatus { Soil, Farmland, Watered } public LandStatus landStatus; public Material soilMat, farmlandMat, wateredMat; new Renderer renderer; //The selection gameobject to enable when the player is selecting the land public GameObject select; //Cache the time the land was watered GameTimestamp timeWatered; [Header("Crops")] //The crop prefab to instantiate public GameObject cropPrefab; //The crop currently planted on the land CropBehaviour cropPlanted = null; // Start is called before the first frame update void Start() { //Get the renderer component renderer = GetComponent<Renderer>(); //Set the land to soil by default SwitchLandStatus(LandStatus.Soil); //Deselect the land by default Select(false); //Add this to TimeManager's Listener list TimeManager.Instance.RegisterTracker(this); } public void LoadLandData(LandStatus statusToSwitch, GameTimestamp lastWatered) { //Set land status accordingly landStatus = statusToSwitch; timeWatered = lastWatered; Material materialToSwitch = soilMat; //Decide what material to switch to switch (statusToSwitch) { case LandStatus.Soil: //Switch to the soil material materialToSwitch = soilMat; break; case LandStatus.Farmland: //Switch to farmland material materialToSwitch = farmlandMat; break; case LandStatus.Watered: //Switch to watered material materialToSwitch = wateredMat; break; } //Get the renderer to apply the changes renderer.material = materialToSwitch; } public void SwitchLandStatus(LandStatus statusToSwitch) { //Set land status accordingly landStatus = statusToSwitch; Material materialToSwitch = soilMat; //Decide what material to switch to switch (statusToSwitch) { case LandStatus.Soil: //Switch to the soil material materialToSwitch = soilMat; break; case LandStatus.Farmland: //Switch to farmland material materialToSwitch = farmlandMat; break; case LandStatus.Watered: //Switch to watered material materialToSwitch = wateredMat; //Cache the time it was watered timeWatered = TimeManager.Instance.GetGameTimestamp(); break; } //Get the renderer to apply the changes renderer.material = materialToSwitch; LandManager.Instance.OnLandStateChange(id, landStatus, timeWatered); } public void Select(bool toggle) { select.SetActive(toggle); } //When the player presses the interact button while selecting this land public void Interact() { //Check the player's tool slot ItemData toolSlot = InventoryManager.Instance.GetEquippedSlotItem(InventorySlot.InventoryType.Tool); //If there's nothing equipped, return if (!InventoryManager.Instance.SlotEquipped(InventorySlot.InventoryType.Tool)) { return; } //Try casting the itemdata in the toolslot as EquipmentData EquipmentData equipmentTool = toolSlot as EquipmentData; //Check if it is of type EquipmentData if (equipmentTool != null) { //Get the tool type EquipmentData.ToolType toolType = equipmentTool.toolType; switch (toolType) { case EquipmentData.ToolType.Hoe: SwitchLandStatus(LandStatus.Farmland); break; case EquipmentData.ToolType.WateringCan: //The land must be tilled first if (landStatus != LandStatus.Soil) { SwitchLandStatus(LandStatus.Watered); } break; case EquipmentData.ToolType.Shovel: //Remove the crop from the land if (cropPlanted != null) { cropPlanted.RemoveCrop(); } break; } //We don't need to check for seeds if we have already confirmed the tool to be an equipment return; } //Try casting the itemdata in the toolslot as SeedData SeedData seedTool = toolSlot as SeedData; ///Conditions for the player to be able to plant a seed ///1: He is holding a tool of type SeedData ///2: The Land State must be either watered or farmland ///3. There isn't already a crop that has been planted if (seedTool != null && landStatus != LandStatus.Soil && cropPlanted == null) { SpawnCrop(); //Plant it with the seed's information cropPlanted.Plant(id, seedTool); //Consume the item InventoryManager.Instance.ConsumeItem(InventoryManager.Instance.GetEquippedSlot(InventorySlot.InventoryType.Tool)); } } public CropBehaviour SpawnCrop() { //Instantiate the crop object parented to the land GameObject cropObject = Instantiate(cropPrefab, transform); //Move the crop object to the top of the land gameobject cropObject.transform.position = new Vector3(transform.position.x, 0.2f, transform.position.z); //Access the CropBehaviour of the crop we're going to plant cropPlanted = cropObject.GetComponent<CropBehaviour>(); return cropPlanted; } public void ClockUpdate(GameTimestamp timestamp) { //Checked if 24 hours has passed since last watered if (landStatus == LandStatus.Watered) { //Hours since the land was watered int hoursElapsed = GameTimestamp.CompareTimestamps(timeWatered, timestamp); Debug.Log(hoursElapsed + " hours since this was watered"); //Grow the planted crop, if any if (cropPlanted != null) { cropPlanted.Grow(); } if (hoursElapsed > 24) { //Dry up (Switch back to farmland) SwitchLandStatus(LandStatus.Farmland); } } //Handle the wilting of the plant when the land is not watered if (landStatus != LandStatus.Watered && cropPlanted != null) { //If the crop has already germinated, start the withering if (cropPlanted.cropState != CropBehaviour.CropState.Seed) { cropPlanted.Wither(); } } } }
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class LandManager : MonoBehaviour { public static LandManager Instance { get; private set; } public static Tuple<List<LandSaveState>, List<CropSaveState>> farmData = null; List<Land> landPlots = new List<Land>(); List<LandSaveState> landData = new List<LandSaveState>(); List<CropSaveState> cropData = new List<CropSaveState>(); private void Awake() { //If there is more than one instance, destroy the extra if (Instance != null && Instance != this) { Destroy(this); } else { //Set the static instance to this instance Instance = this; } } void OnEnable() { RegisterLandPlots(); StartCoroutine(LoadFarmData()); } IEnumerator LoadFarmData() { yield return new WaitForEndOfFrame(); //Load farm data if any if (farmData != null) { //Load in any saved data ImportLandData(farmData.Item1); ImportCropData(farmData.Item2); } } private void OnDestroy() { //Save the Instance variables over to the static variable farmData = new Tuple<List<LandSaveState>, List<CropSaveState>>(landData, cropData); } //Get all the Land Objects in the scene and cache it void RegisterLandPlots() { foreach (Transform landTransform in transform) { Land land = landTransform.GetComponent<Land>(); landPlots.Add(land); //Create a corresponding LandSaveState landData.Add(new LandSaveState()); //Assign it an id based on its index land.id = landPlots.Count - 1; } } //Registers the crop onto the Instance public void RegisterCrop(int landID, SeedData seedToGrow, CropBehaviour.CropState cropState, int growth, int health) { cropData.Add(new CropSaveState(landID, seedToGrow.name, cropState, growth, health)); } public void DeregisterCrop(int landID) { //Find its index in the list from the landID and remove it cropData.RemoveAll(x => x.landID == landID); } //Update the corresponding Land Data on ever change to the Land's state public void OnLandStateChange(int id, Land.LandStatus landStatus, GameTimestamp lastWatered) { landData[id] = new LandSaveState(landStatus, lastWatered); } //Update the corresponding Crop Data on ever change to the Land's state public void OnCropStateChange(int landID, CropBehaviour.CropState cropState, int growth, int health) { //Find its index in the list from the landID int cropIndex = cropData.FindIndex(x => x.landID == landID); string seedToGrow = cropData[cropIndex].seedToGrow; cropData[cropIndex] = new CropSaveState(landID, seedToGrow, cropState, growth, health); } //Load over the static farmData onto the Instance's landData public void ImportLandData(List<LandSaveState> landDatasetToLoad) { for (int i = 0; i < landDatasetToLoad.Count; i++) { //Get the individual land save state LandSaveState landDataToLoad = landDatasetToLoad[i]; //Load it up onto the Land instance landPlots[i].LoadLandData(landDataToLoad.landStatus, landDataToLoad.lastWatered); } landData = landDatasetToLoad; } //Load over the static farmData onto the Instance's cropData public void ImportCropData(List<CropSaveState> cropDatasetToLoad) { cropData = cropDatasetToLoad; foreach (CropSaveState cropSave in cropDatasetToLoad) { //Access the land Land landToPlant = landPlots[cropSave.landID]; //Spawn the crop CropBehaviour cropToPlant = landToPlant.SpawnCrop(); Debug.Log(cropToPlant.gameObject); //Load in the data SeedData seedToGrow = (SeedData)InventoryManager.Instance.itemIndex.GetItemFromString(cropSave.seedToGrow); cropToPlant.LoadCrop(cropSave.landID, seedToGrow, cropSave.cropState, cropSave.growth, cropSave.health); } } // Update is called once per frame void Update() { } }
‘using System.Collections; using System.Collections.Generic; using UnityEngine;
public class CropBehaviour : MonoBehaviour { //The ID of the land the crop belongs to int landID;
//Information on what the crop will grow into SeedData seedToGrow; [Header("Stages of Life")] public GameObject seed; public GameObject wilted; private GameObject seedling; private GameObject harvestable; //The growth points of the crop int growth; //How many growth points it takes before it becomes harvestable int maxGrowth; //The crop can stay alive for 48 hours without water before it dies int maxHealth = GameTimestamp.HoursToMinutes(48); int health; public enum CropState { Seed, Seedling, Harvestable, Wilted } //The current stage in the crop's growth public CropState cropState; //Initialisation for the crop GameObject //Called when the player plants a seed public void Plant(int landID, SeedData seedToGrow) { LoadCrop(landID, seedToGrow, CropState.Seed, 0, 0); LandManager.Instance.RegisterCrop(landID, seedToGrow, cropState, growth, health); } public void LoadCrop(int landID, SeedData seedToGrow, CropState cropState, int growth, int health) { this.landID = landID; //Save the seed information this.seedToGrow = seedToGrow; //Instantiate the seedling and harvestable GameObjects seedling = Instantiate(seedToGrow.seedling, transform); //Access the crop item data ItemData cropToYield = seedToGrow.cropToYield; //Instantiate the harvestable crop harvestable = Instantiate(cropToYield.gameModel, transform); //Convert Days To Grow into hours int hoursToGrow = GameTimestamp.DaysToHours(seedToGrow.daysToGrow); //Convert it to minutes maxGrowth = GameTimestamp.HoursToMinutes(hoursToGrow); //Set the growth and health accordingly this.growth = growth; this.health = health; //Check if it is regrowable if (seedToGrow.regrowable) { //Get the RegrowableHarvestBehaviour from the GameObject RegrowableHarvestBehaviour regrowableHarvest = harvestable.GetComponent<RegrowableHarvestBehaviour>(); //Initialise the harvestable regrowableHarvest.SetParent(this); } //Set the initial state to Seed SwitchState(cropState); } //The crop will grow when watered public void Grow() { //Increase the growth point by 1 growth++; //Restore the health of the plant when it is watered if (health < maxHealth) { health++; } //The seed will sprout into a seedling when the growth is at 50% if (growth >= maxGrowth / 2 && cropState == CropState.Seed) { SwitchState(CropState.Seedling); } //Grow from seedling to harvestable if (growth >= maxGrowth && cropState == CropState.Seedling) { SwitchState(CropState.Harvestable); } //Inform LandManager on the changes LandManager.Instance.OnCropStateChange(landID, cropState, growth, health); } //The crop will progressively wither when the soil is dry public void Wither() { health--; //If the health is below 0 and the crop has germinated, kill it if (health <= 0 && cropState != CropState.Seed) { SwitchState(CropState.Wilted); } //Inform LandManager on the changes LandManager.Instance.OnCropStateChange(landID, cropState, growth, health); } //Function to handle the state changes void SwitchState(CropState stateToSwitch) { //Reset everything and set all GameObjects to inactive seed.SetActive(false); seedling.SetActive(false); harvestable.SetActive(false); wilted.SetActive(false); switch (stateToSwitch) { case CropState.Seed: //Enable the Seed GameObject seed.SetActive(true); break; case CropState.Seedling: //Enable the Seedling GameObject seedling.SetActive(true); //Give the seed health health = maxHealth; break; case CropState.Harvestable: //Enable the Harvestable GameObject harvestable.SetActive(true); //If the seed is not regrowable, detach the harvestable from this crop gameobject and destroy it. if (!seedToGrow.regrowable) { //Unparent it to the crop harvestable.transform.parent = null; RemoveCrop(); } break; case CropState.Wilted: //Enable the wilted GameObject wilted.SetActive(true); break; } //Set the current crop state to the state we're switching to cropState = stateToSwitch; } //Destroys and Deregisters the Crop public void RemoveCrop() { LandManager.Instance.DeregisterCrop(landID); Destroy(gameObject); } //Called when the player harvests a regrowable crop. Resets the state to seedling public void Regrow() { //Reset the growth //Get the regrowth time in hours int hoursToRegrow = GameTimestamp.DaysToHours(seedToGrow.daysToRegrow); growth = maxGrowth - GameTimestamp.HoursToMinutes(hoursToRegrow); //Switch the state back to seedling SwitchState(CropState.Seedling); }
}’
March 27, 2025 at 6:10 pm #17627::Based on the reference information, this is likely happening because of the scene transition and how the LandManager handles saving and loading crop data.
The crops are disappearing or withering because the
farmData
static variable in LandManager is being used to persist crop state between scene transitions. When you move to another scene and come back, the crops are being loaded from this saved state, which might have incomplete or partially degraded data.To troubleshoot, I recommend checking:
- Are you properly saving the
farmData
before scene transition? - Is the
farmData
being reset accidentally during scene load? - Are the crops’ health and growth states being correctly preserved?
Would you like me to help you review the scene transition and data saving process to ensure crop data is correctly maintained?
March 27, 2025 at 6:28 pm #17628::To be precise, only the first seed planted became a withered plant, all the remaining seeds disappeared.
March 27, 2025 at 6:31 pm #17629::Based on the reference information and the updated specifics, this suggests a potential issue with how crops are being saved and loaded during scene transitions.
The fact that one seed became a withered plant while others disappeared entirely points to an inconsistency in the crop data saving mechanism. This could be related to how the
LandManager.ImportCropData()
method handles crop restoration.Could you confirm:
- Are all crop prefabs set up identically in your scene?
- Did you check if the
farmData
static variable contains all the crop save states before and after scene transition? - Are there any error messages in the console during scene loading?
To help diagnose the issue more precisely, could you provide more details about how you’re managing scene transitions and what specific steps you’re taking when moving between scenes?
March 31, 2025 at 3:43 pm #17703::Try to debug as Tarha has directed, and if not you can refer to this thread which another user encountered a similar problem
- Are you properly saving the
-
AuthorPosts
- You must be logged in to reply to this topic.