Forum begins after the advertisement:
[Part 12] Withering of Plant Problem
Home › Forums › Video Game Tutorial Series › Creating a Farming RPG in Unity › [Part 12] Withering of Plant Problem
- This topic has 13 replies, 3 voices, and was last updated 3 weeks, 6 days ago by
Jonathan Teo.
-
AuthorPosts
-
January 28, 2025 at 2:55 pm #17156::
partpaI encountered a problem regarding the Wither status of the Plant.
Scenario: -I plant, watered and leave the planting area scene -Plant health goes up -Plant health goes down when not watered (Sample 15 Plant Health) -Return to the Planting Area scene -Leave the Planting Area scene -Plant Health is 2,500
View post on imgur.com
January 29, 2025 at 11:07 am #17157::Did the Debug message come from GameStateManager?
- 1 anonymous person
January 29, 2025 at 11:55 pm #17160January 30, 2025 at 11:49 am #17161::The problem happens when you load into the scene. Try adding debug logs on the functions that get called, like
public void ImportCropData(List<CropSaveState> cropDatasetToLoad) { Debug.Log("ImportCropData called"); cropData = cropDatasetToLoad; foreach (CropSaveState cropSave in cropDatasetToLoad) { Debug.Log($"Importing crop - Health: {cropSave.health}"); //... rest of the code ... } }
- 1 anonymous person
January 30, 2025 at 3:03 pm #17165January 30, 2025 at 3:18 pm #17166January 30, 2025 at 3:22 pm #17167::This is my UpdateFarmState
void UpdateFarmState(GameTimeStamp timestamp) { //Updates the Land and Crop Save states as long as the player is outside of the PlantingArea scene if(SceneTransitionManager.Instance.currentLocation != SceneTransitionManager.Location.PlantingArea) { if (SoilManager.urbanFarmData == null) { return; } List<SoilSaveState> soilData = SoilManager.urbanFarmData.Item1; List<CropSaveState> cropData = SoilManager.urbanFarmData.Item2; //If there are no crops planted on a certain soil, don't need to update if(cropData.Count == 0) { return; } for(int i = 0; i < cropData.Count; i++) { CropSaveState crop = cropData[i]; SoilSaveState soil = soilData[crop.soilID]; //Check if the crop is already wilted if(crop.cropState == NewCropBehaviour.CropState.Wilted) { continue; } //Update the Soil's state soil.ClockUpdate(timestamp); //Update the crop's state based on the soil state if(soil.soilStatus == PottingSoil.SoilStatus.Watered) { crop.Grow(); } else if(crop.cropState != NewCropBehaviour.CropState.Seed) { crop.Wither(); } //Update the element in the array cropData[i] = crop; soilData[crop.soilID] = soil; } SoilManager.urbanFarmData.Item2.ForEach((CropSaveState crop) => { Debug.LogWarning(crop.seedToGrow + "\n Health: " + crop.health + "\n Growth: " + crop.growth + "\n State: " + crop.cropState.ToString()); }); } }
February 1, 2025 at 9:28 am #17172::I mean the ImportCropData in LandManager
//Load over the static farmData onto the Instance's cropData public void ImportCropData(List<CropSaveState> cropDatasetToLoad) { cropData = cropDatasetToLoad; foreach (CropSaveState cropSave in cropDatasetToLoad) { Debug.Log($"Importing crop - Health before: {cropSave.health}"); //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.GetItemFromString(cropSave.seedToGrow); cropToPlant.LoadCrop(cropSave.landID, seedToGrow, cropSave.cropState, cropSave.growth, cropSave.health); } }
- 1 anonymous person
February 1, 2025 at 8:07 pm #17173::This is the Result
Might be wondering why the Soil changed to Green, it just turn into Weed state and not watered
View post on imgur.com
February 2, 2025 at 6:07 am #17177::Something is increasing the health in between. Where’s the debug logs from the update farm state? In Visual Studio, right click SoilManager.urbanFarmData.Item2 on “Item2”, select Find all references to it, and check what is being updated. If you can’t find the part of your code that is increasing the value, add a debug log everywhere it is used.
March 4, 2025 at 6:45 pm #17385::After Month of looking, I was not able to locate the one that is causing the increase of value, however, this are the Scripts that has reference on the Item2
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."); } //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) { //Find its index in the list from the landID and remove it 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); } //Update the corresponding Crop Data on ever change to the Land's state public void OnCropStateChange(int soilID, NewCropBehaviour.CropState cropState, int growth, int health) { //Find its index in the list from the landID 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 //Load over the static farmData onto the Instance's landData public void ImportSoilData(List<SoilSaveState> soilDatasetToLoad) { for(int i = 0; i < soilDatasetToLoad.Count; i++) { //Get the individual land save state SoilSaveState soilDataToLoad = soilDatasetToLoad[i]; //Load it up onto the Land instance soilPlots[i].LoadSoilData(soilDataToLoad.soilStatus, soilDataToLoad.lastWatered); } soilData = soilDatasetToLoad; } public void ImportCropData(List<CropSaveState> cropDatasetToLoad) { //Load over the static farmData onto the Instance's cropData cropData = cropDatasetToLoad; foreach (CropSaveState cropSave in cropDatasetToLoad) { Debug.LogWarning($"Importing crop - Health before: {cropSave.health}"); //Access the Soil PottingSoil soilToPlant = soilPlots[cropSave.soilID]; //Spawn the crop NewCropBehaviour cropToPlant = soilToPlant.SpawnCrop(); Debug.Log(cropToPlant.gameObject); //Load in the data SeedData seedToGrow = (SeedData)NewInventoryManager.Instance.itemIndex.GetItemFromString(cropSave.seedToGrow); cropToPlant.LoadCrop(cropSave.soilID, seedToGrow, cropSave.cropState, cropSave.growth, cropSave.health); Debug.Log($"Loaded crop with seed {seedToGrow.name} on soil ID {cropSave.soilID}"); } } #endregion }
GameStateManager
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class GameStateManager : MonoBehaviour, ITimeTracker { 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; } } void Start() { TimeManager.Instance.RegisterTracker(this); // Ensure fade image is active and fully black at the start fadeImage.gameObject.SetActive(true); fadeImage.color = new Color(fadeImage.color.r, fadeImage.color.g, fadeImage.color.b, 1); // Start fading to transparent StartCoroutine(FadeIn()); } private void Update() { if(Input.GetKey(KeyCode.P)) { TimeManager.Instance.Tick(); } } public void ClockUpdate(GameTimeStamp timestamp) { UpdateFarmState(timestamp); } void UpdateFarmState(GameTimeStamp timestamp) { //Updates the Land and Crop Save states as long as the player is outside of the PlantingArea scene if(SceneTransitionManager.Instance.currentLocation != SceneTransitionManager.Location.PlantingArea) { if (SoilManager.urbanFarmData == null) { return; } List<SoilSaveState> soilData = SoilManager.urbanFarmData.Item1; List<CropSaveState> cropData = SoilManager.urbanFarmData.Item2; //If there are no crops planted on a certain soil, don't need to update if(cropData.Count == 0) { return; } for(int i = 0; i < cropData.Count; i++) { CropSaveState crop = cropData[i]; SoilSaveState soil = soilData[crop.soilID]; //Check if the crop is already wilted if(crop.cropState == NewCropBehaviour.CropState.Wilted) { continue; } //Update the Soil's state soil.ClockUpdate(timestamp); //Update the crop's state based on the soil state if(soil.soilStatus == PottingSoil.SoilStatus.Watered) { crop.Grow(); } else if(crop.cropState != NewCropBehaviour.CropState.Seed) { crop.Wither(); } //Update the element in the array cropData[i] = crop; soilData[crop.soilID] = soil; } SoilManager.urbanFarmData.Item2.ForEach((CropSaveState crop) => { Debug.LogWarning(crop.seedToGrow + "\n Health: " + crop.health + "\n Growth: " + crop.growth + "\n State: " + crop.cropState.ToString()); }); } } public void Sleep() { //Set the time GameTimeStamp timestampOfNextDay = TimeManager.Instance.GetGameTimeStamp(); timestampOfNextDay.day += 1; timestampOfNextDay.hour = 6; timestampOfNextDay.minute = 0; TimeManager.Instance.SkipTime(timestampOfNextDay); PlayerStats.ResetMiniGameStatus(); SaveManager.Save(ExportSaveState()); StartCoroutine(SleepSequence()); } private IEnumerator SleepSequence() { // Trigger fade-out yield return FadeOut(); PlayerStats.RestoreStamina(100); // Trigger fade-in yield return FadeIn(); } public IEnumerator FadeOut() { fadeImage.gameObject.SetActive(true); yield return StartCoroutine(Fade(0, 1)); } public IEnumerator FadeIn() { 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, PlayerStats.Money, PlayerStats.Stamina, PlayerStats.Hunger); } public void LoadSave() { GameSaveState save = SaveManager.Load(); TimeManager.Instance.LoadTime(save.timestamp); ItemSlotData[] storageSlots = save.storageSlot; ItemSlotData equippedStorageSlot = save.equippedStorageSlot; ItemSlotData[] harvestSlots = save.harvestSlot; ItemSlotData equippedHarvestSlot = save.equippedHarvestSlot; NewInventoryManager.Instance.LoadInventory(storageSlots, equippedStorageSlot, harvestSlots, equippedHarvestSlot); SoilManager.urbanFarmData = new System.Tuple<List<SoilSaveState>, List<CropSaveState>>(save.soilData, save.cropData); PlayerStats.LoadStats(save.money, save.stamina, save.hunger); } }
March 4, 2025 at 6:51 pm #17386::It seems like you’re trying to troubleshoot an issue related to unexpected increases in the value of an item in your game’s logic, specifically around the
SoilManager
andGameStateManager
scripts. To help you identify the source of the problem, here are some steps you can follow:-
Check Crop Growth Logic: Look closely at the growth logic in the
UpdateFarmState
method of theGameStateManager
class. The conditions under which crops grow or wither are based on the soil status. Ensure that the conditions for growth are not met more frequently than intended.if (soil.soilStatus == PottingSoil.SoilStatus.Watered) { crop.Grow(); // Check this method for any logic that increases crop value }
Investigate what the
Grow()
method does in theCropSaveState
class. If it increases the crop’s value or health, ensure that it’s not being triggered too often. -
Debugging Output: Add more debugging output before and after key actions that change values. For example, right before and after the
Grow()
method is called, print out the crop’s health and growth values:Debug.Log($"Before Grow - Health: {crop.health}, Growth: {crop.growth}"); crop.Grow(); Debug.Log($"After Grow - Health: {crop.health}, Growth: {crop.growth}");
-
Evaluate Time Management: Since your game likely uses an in-game time management system, check that the
ClockUpdate
method inGameStateManager
isn’t being called more frequently than expected. This could lead to more crop growth than intended. -
Inspect Save and Load Logic: Look at how you are loading and saving your game state in the
LoadSave()
andExportSaveState()
methods. Ensure that the values being loaded back into the game are correct and that there is no unintended alteration of crop values during these processes. -
Review Deregistration of Crops: Make sure that crops are being properly deregistered when they die or are removed. In the
DeregisterCrop
method, if crops are not being removed from thecropData
list correctly, you might end up with lingering references that are still affecting your game state. -
Check for Side Effects: Look for any additional places in your code where crop values might be modified outside of the expected methods. For example, check any other scripts that may interact with
CropSaveState
orSoilSaveState
. -
Look for Multithreading Issues: If you are using any coroutines or asynchronous processes that may affect the crop or soil states, ensure that these are not causing race conditions where values could be updated unexpectedly.
-
Unit Tests: If possible, write unit tests for the methods that are responsible for growing crops, changing soil states, and saving/loading game state. This will help you isolate the issues more effectively.
By following these steps, you should be able to pinpoint where the unexpected increases in crop value are occurring. If you need further assistance with specific implementations or debugging, feel free to ask!
March 4, 2025 at 7:09 pm #17387::This is my CropSaveState in which crop.Grow() is going, so far I don’t see any issue or just me.
using System.Collections; using System.Collections.Generic; using UnityEngine; [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; } public void Grow() { growth++; SeedData seedInfo = (SeedData) NewInventoryManager.Instance.itemIndex.GetItemFromString(seedToGrow); int maxGrowth = GameTimeStamp.HoursToMinutes(GameTimeStamp.DaysToHours(seedInfo.daysToGrow)); int maxHealth = GameTimeStamp.HoursToMinutes(48); if (health < maxHealth) { health++; } //The seed will sprout into a seedling when growth is at 50% if (growth >= maxGrowth / 2 && cropState == NewCropBehaviour.CropState.Seed) { cropState = NewCropBehaviour.CropState.Seedling; } //Grow to seedling if (growth >= maxGrowth && cropState == NewCropBehaviour.CropState.Seedling) { cropState = NewCropBehaviour.CropState.Harvestable; } } public void Wither() { health--; if (health <= 0 && cropState != NewCropBehaviour.CropState.Seed) { cropState = NewCropBehaviour.CropState.Wilted; } } }
March 4, 2025 at 7:11 pm #17388::Your
CropSaveState
struct looks well-structured overall, but there are a few potential issues and improvements that you might want to consider. Here’s a breakdown of what to check and some suggestions:-
Use of HTML Entities: In your code, you have used HTML entities like
<
and>
. These should be replaced with “ respectively. This could potentially cause syntax errors when compiling your code.if (health = maxGrowth / 2 && cropState == NewCropBehaviour.CropState.Seed)
if (growth >= maxGrowth && cropState == NewCropBehaviour.CropState.Seedling)
-
Health Management: You are incrementing health in the
Grow
method, but you never check if health is already at maximum before incrementing. You might want to ensure that health does not exceedmaxHealth
.if (health < maxHealth) { health++; }
-
Growth Logic: You should ensure that
growth
does not exceedmaxGrowth
. This will prevent potential issues if theGrow
method is called excessively.if (growth < maxGrowth) { growth++; }
-
Handling
Wither
Method: In theWither
method, you decrement health and check if it’s less than or equal to zero. If it is, you set the crop state toWilted
. Make sure to also handle what happens when health is less than zero, as you might want to prevent negative health values. -
Potential Null Reference: Ensure that
seedInfo
is not null before trying to access its properties, as this may lead to aNullReferenceException
.SeedData seedInfo = (SeedData) NewInventoryManager.Instance.itemIndex.GetItemFromString(seedToGrow); if (seedInfo == null) { Debug.LogError("Seed data not found for: " + seedToGrow); return; // Early exit if seedInfo is null }
Here’s a revised version of your
Grow
method with these suggestions applied:public void Grow() { // Ensure growth does not exceed maxGrowth SeedData seedInfo = (SeedData) NewInventoryManager.Instance.itemIndex.GetItemFromString(seedToGrow); if (seedInfo == null) { Debug.LogError("Seed data not found for: " + seedToGrow); return; } int maxGrowth = GameTimeStamp.HoursToMinutes(GameTimeStamp.DaysToHours(seedInfo.daysToGrow)); int maxHealth = GameTimeStamp.HoursToMinutes(48); if (growth < maxGrowth) { growth++; } if (health = maxGrowth / 2 && cropState == NewCropBehaviour.CropState.Seed) { cropState = NewCropBehaviour.CropState.Seedling; } // Grow to harvestable if (growth >= maxGrowth && cropState == NewCropBehaviour.CropState.Seedling) { cropState = NewCropBehaviour.CropState.Harvestable; } }
Make sure to implement similar checks in your
Wither
method as well for health management. These small adjustments should help maintain the integrity of your crop growth logic and prevent potential runtime issues.March 4, 2025 at 7:23 pm #17389::I’m not sure if this is the fix or what but somehow it works????
if (this.health <= 0) //Only reset health if it's empty { health = maxHealth; }
void SwitchState(CropState stateToSwitch) { seed.SetActive(true); seedling.SetActive(false); harvestable.SetActive(false); wilted.SetActive(false); switch (stateToSwitch) { case CropState.Seed: seed.SetActive(true); break; case CropState.Seedling: seedling.SetActive(true); if (this.health <= 0) //Only reset health if it's empty { health = maxHealth; } //health = maxHealth; break; case CropState.Harvestable: harvestable.SetActive(true); //If the seed is not regrowable, detach the harvestable from this gameobject and destroy it. if (!seedToGrow.regrowable) { //Unparenting harvestable.transform.parent = null; //RemoveCrop(); harvestable.GetComponent<InteractableObject>().onInteract.AddListener(RemoveCrop); } break; case CropState.Wilted: wilted.SetActive(true); break; } cropState = stateToSwitch; }
-
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: