Get 25% off your next order from the Unity Asset Store: YW86GYBU8S.
Forum begins after the advertisement:
[Part 16] Evolution bug
Home › Forums › Video Game Tutorial Series › Creating a Rogue-like Shoot-em Up in Unity › [Part 16] Evolution bug
- This topic has 26 replies, 3 voices, and was last updated 1 month, 2 weeks ago by
Alp Apustaja.
-
AuthorPosts
-
September 2, 2025 at 6:15 pm #18786::
Code : TreasureChest
using System.Collections; using System.Collections.Generic; using UnityEngine; public class TreasureChest : MonoBehaviour { private void OnTriggerEnter2D(Collider2D col) { PlayerInventory p = col.GetComponent<PlayerInventory>(); if (p) { bool randomBool = Random.Range(0, 2) == 0; OpenTreasureChest(p, randomBool); Destroy(gameObject); } } public void OpenTreasureChest(PlayerInventory inventory, bool isHigherTier) { // Loop through every weapon to check whether it can evolve. foreach (PlayerInventory.Slot s in inventory.weaponSlots) { Weapon w = s.item as Weapon; if (w.data.evolutionData == null) continue; // Ignore weapon if it cannot evolve. // Loop through every possible evolution of the weapon. foreach (ItemData.Evolution e in w.data.evolutionData) { // Only attempt to evolve weapons via treasure chest evolution. if (e.condition == ItemData.Evolution.Condition.treasureChest) { bool attempt = w.AttemptEvolution(e, 0); if (attempt) return; // If evolution succeeds, stop. } } } } }September 2, 2025 at 6:25 pm #18787::Based on the reference information provided, the code you’ve shared appears to be a more advanced implementation of the TreasureChest script compared to the earlier tutorial version. The reference information describes a simpler initial implementation, so I cannot fully validate this code against the original tutorial instructions.
The key differences include:
- More complex evolution logic
- Handling weapon evolution directly in the TreasureChest script
- Using a different method of checking and attempting weapon evolution
Would you like me to clarify how this compares to the original tutorial implementation? Or do you have a specific question about the TreasureChest script?
September 2, 2025 at 10:37 pm #18788::Masdog, judging from your Console log, your problem appears to be in the Item script. Specifically, the
CanEvolve()function might’ve been coded wrongly.Can you share that script as well?
September 3, 2025 at 11:18 am #18790::Code : Item
using System.Collections.Generic; using UnityEngine; /// <summary> /// Base class for both the Passive and the Weapon classes. It is primarily intended to handle weapon /// evolution, as we want both weapons and passives to be evolve-able. /// </summary> public abstract class Item : MonoBehaviour { public int currentLevel = 1, maxLevel = 1; protected ItemData.Evolution[] evolutionData; protected PlayerInventory inventory; protected PlayerStats owner; public virtual void Initialise(ItemData data) { maxLevel = data.maxLevel; // Store the evolution data as we have to track wherher // all the catalysts are in the inventory so we can evolve. evolutionData = data.evolutionData; // We have to find a better way to reference the player inventory // in future, as this is inefficient inventory = FindObjectOfType<PlayerInventory>(); owner = FindObjectOfType<PlayerStats>(); } // Call this function to get all the evolutions that the weapon // can currently evolve to. public virtual ItemData.Evolution[] CanEvolve() { List<ItemData.Evolution> possibleEvolutions = new List<ItemData.Evolution>(); // Check each listed evolution and whether it is in // the inventory. foreach (ItemData.Evolution e in evolutionData) { if (CanEvolve(e)) possibleEvolutions.Add(e); } return possibleEvolutions.ToArray(); } // Checks if a specific evolution is possible. public virtual bool CanEvolve(ItemData.Evolution evolution, int levelUpAmount = 1) { // Cannot evolve if the item hasn't reached the level to evolve. if (evolution.evolutionLevel > currentLevel + levelUpAmount) { Debug.LogWarning(string.Format("Evolution failed. Current level {0}, evolution level {1}", currentLevel, evolution.evolutionLevel)); return false; } // Checks to see if all the catalysts are in the inventory. foreach (ItemData.Evolution.Config c in evolution.catalysts) { Item item = inventory.Get(c.itemType); if (!item || item.currentLevel < c.level) { Debug.LogWarning(string.Format("Evolution failed. Missing {0}", c.itemType.name)); return false; } } return true; } // AttemptEvolution will spawn a new weapon for the character, and remove all // the weapons that are supposed to be consumed. public virtual bool AttemptEvolution(ItemData.Evolution evolutionData, int levelUpAmount = 1) { if (!CanEvolve(evolutionData, levelUpAmount)) return false; // Should we consume passives / weapons? bool consumePassives = (evolutionData.consumes & ItemData.Evolution.Consumption.passives) > 0; bool consumeWeapons = (evolutionData.consumes & ItemData.Evolution.Consumption.weapons) > 0; // Loop through all the catalysts and check if we should consume them. foreach (ItemData.Evolution.Config c in evolutionData.catalysts) { if (c.itemType is PassiveData && consumePassives) inventory.Remove(c.itemType, true); if (c.itemType is WeaponData && consumeWeapons) inventory.Remove(c.itemType, true); } // Should we consume ourselves as well? if (this is Passive && consumePassives) inventory.Remove((this as Passive).data, true); else if (this is Weapon && consumeWeapons) inventory.Remove((this as Weapon).data, true); // Add the new weapon onto our inventory. inventory.Add(evolutionData.outcome.itemType); return true; } public virtual bool CanLevelUp() { return currentLevel <= maxLevel; } // Whenever an item levels up, attempt to make it evolve. public virtual bool DoLevelUp() { if (evolutionData == null) return true; // Tries to evolve into every listed evolution of this weapon, // if the weapon's evolution condition is levelling up. foreach (ItemData.Evolution e in evolutionData) { if (e.condition == ItemData.Evolution.Condition.auto) AttemptEvolution(e); } return true; } // What effects you receive on equipping an item. public virtual void OnEquip() { } // What effects are removed on unequipping an item. public virtual void OnUnequip() { } }September 3, 2025 at 11:20 am #18791::The code you’ve provided matches the reference information almost exactly. It is an implementation of the Item abstract base class described in the reference information, with methods for:
- Initializing item data
- Checking possible evolutions
- Determining if an evolution is possible
- Attempting item evolution
- Checking if an item can level up
- Handling level-up and potential auto-evolution
- Placeholder methods for equipping and unequipping
The code includes additional implementation details not explicitly shown in the reference information, such as the
AttemptEvolutionmethod, which handles consuming items during evolution and adding the new evolved item to the inventory.Would you like me to elaborate on any specific aspect of the Item class implementation?
September 3, 2025 at 5:03 pm #18792::@Alp, the Item code that Masdog has provided has a NullReferenceException on line 59. Can you look at his code and point out the areas that might cause this issue?
Repasting the code here:
using System.Collections.Generic; using UnityEngine; /// <summary> /// Base class for both the Passive and the Weapon classes. It is primarily intended to handle weapon /// evolution, as we want both weapons and passives to be evolve-able. /// </summary> public abstract class Item : MonoBehaviour { public int currentLevel = 1, maxLevel = 1; protected ItemData.Evolution[] evolutionData; protected PlayerInventory inventory; protected PlayerStats owner; public virtual void Initialise(ItemData data) { maxLevel = data.maxLevel; // Store the evolution data as we have to track wherher // all the catalysts are in the inventory so we can evolve. evolutionData = data.evolutionData; // We have to find a better way to reference the player inventory // in future, as this is inefficient inventory = FindObjectOfType<PlayerInventory>(); owner = FindObjectOfType<PlayerStats>(); } // Call this function to get all the evolutions that the weapon // can currently evolve to. public virtual ItemData.Evolution[] CanEvolve() { List<ItemData.Evolution> possibleEvolutions = new List<ItemData.Evolution>(); // Check each listed evolution and whether it is in // the inventory. foreach (ItemData.Evolution e in evolutionData) { if (CanEvolve(e)) possibleEvolutions.Add(e); } return possibleEvolutions.ToArray(); } // Checks if a specific evolution is possible. public virtual bool CanEvolve(ItemData.Evolution evolution, int levelUpAmount = 1) { // Cannot evolve if the item hasn't reached the level to evolve. if (evolution.evolutionLevel > currentLevel + levelUpAmount) { Debug.LogWarning(string.Format("Evolution failed. Current level {0}, evolution level {1}", currentLevel, evolution.evolutionLevel)); return false; } // Checks to see if all the catalysts are in the inventory. foreach (ItemData.Evolution.Config c in evolution.catalysts) { Item item = inventory.Get(c.itemType); if (!item || item.currentLevel < c.level) { Debug.LogWarning(string.Format("Evolution failed. Missing {0}", c.itemType.name)); return false; } } return true; } // AttemptEvolution will spawn a new weapon for the character, and remove all // the weapons that are supposed to be consumed. public virtual bool AttemptEvolution(ItemData.Evolution evolutionData, int levelUpAmount = 1) { if (!CanEvolve(evolutionData, levelUpAmount)) return false; // Should we consume passives / weapons? bool consumePassives = (evolutionData.consumes & ItemData.Evolution.Consumption.passives) > 0; bool consumeWeapons = (evolutionData.consumes & ItemData.Evolution.Consumption.weapons) > 0; // Loop through all the catalysts and check if we should consume them. foreach (ItemData.Evolution.Config c in evolutionData.catalysts) { if (c.itemType is PassiveData && consumePassives) inventory.Remove(c.itemType, true); if (c.itemType is WeaponData && consumeWeapons) inventory.Remove(c.itemType, true); } // Should we consume ourselves as well? if (this is Passive && consumePassives) inventory.Remove((this as Passive).data, true); else if (this is Weapon && consumeWeapons) inventory.Remove((this as Weapon).data, true); // Add the new weapon onto our inventory. inventory.Add(evolutionData.outcome.itemType); return true; } public virtual bool CanLevelUp() { return currentLevel <= maxLevel; } // Whenever an item levels up, attempt to make it evolve. public virtual bool DoLevelUp() { if (evolutionData == null) return true; // Tries to evolve into every listed evolution of this weapon, // if the weapon's evolution condition is levelling up. foreach (ItemData.Evolution e in evolutionData) { if (e.condition == ItemData.Evolution.Condition.auto) AttemptEvolution(e); } return true; } // What effects you receive on equipping an item. public virtual void OnEquip() { } // What effects are removed on unequipping an item. public virtual void OnUnequip() { } }September 3, 2025 at 5:20 pm #18794::Potential NullReferenceException sources on line 59 (inventory.Add(evolutionData.outcome.itemType)):
inventorymight be null ifFindObjectOfType()fails to find a PlayerInventory componentevolutionData.outcomemight be nullevolutionData.outcome.itemTypemight be null
Recommended fixes:
- Add null checks before calling
inventory.Add() - Ensure
FindObjectOfType()is reliable - Validate that
evolutionData.outcomeandevolutionData.outcome.itemTypeare not null before attempting to add
Would you like me to provide a code snippet demonstrating these null checks?
September 3, 2025 at 5:26 pm #18796::Masdog, your issue seems to be that in your
Initialise()function, theinventory = FindObjectOfType()function is returning null. Can you add this line and show me the line that appears in your Console whenever you receive a new weapon?public virtual void Initialise(ItemData data) { maxLevel = data.maxLevel; // Store the evolution data as we have to track wherher // all the catalysts are in the inventory so we can evolve. evolutionData = data.evolutionData; // We have to find a better way to reference the player inventory // in future, as this is inefficient inventory = FindObjectOfType<PlayerInventory>(); print("inventory: " + inventory); owner = FindObjectOfType<PlayerStats>(); }September 4, 2025 at 12:24 pm #18805::I tried adding the line print(“inventory: ” + inventory); to the code but it still doesn’t work.
Code : Item
using System.Collections.Generic; using UnityEngine; /// <summary> /// Base class for both the Passive and the Weapon classes. It is primarily intended to handle weapon /// evolution, as we want both weapons and passives to be evolve-able. /// </summary> public abstract class Item : MonoBehaviour { public int currentLevel = 1, maxLevel = 1; protected ItemData.Evolution[] evolutionData; protected PlayerInventory inventory; protected PlayerStats owner; public virtual void Initialise(ItemData data) { maxLevel = data.maxLevel; // Store the evolution data as we have to track wherher // all the catalysts are in the inventory so we can evolve. evolutionData = data.evolutionData; // We have to find a better way to reference the player inventory // in future, as this is inefficient inventory = FindObjectOfType<PlayerInventory>(); print("inventory: " + inventory); owner = FindObjectOfType<PlayerStats>(); } // Call this function to get all the evolutions that the weapon // can currently evolve to. public virtual ItemData.Evolution[] CanEvolve() { List<ItemData.Evolution> possibleEvolutions = new List<ItemData.Evolution>(); // Check each listed evolution and whether it is in // the inventory. foreach (ItemData.Evolution e in evolutionData) { if (CanEvolve(e)) possibleEvolutions.Add(e); } return possibleEvolutions.ToArray(); } // Checks if a specific evolution is possible. public virtual bool CanEvolve(ItemData.Evolution evolution, int levelUpAmount = 1) { // Cannot evolve if the item hasn't reached the level to evolve. if (evolution.evolutionLevel > currentLevel + levelUpAmount) { Debug.LogWarning(string.Format("Evolution failed. Current level {0}, evolution level {1}", currentLevel, evolution.evolutionLevel)); return false; } // Checks to see if all the catalysts are in the inventory. foreach (ItemData.Evolution.Config c in evolution.catalysts) { Item item = inventory.Get(c.itemType); if (!item || item.currentLevel < c.level) { Debug.LogWarning(string.Format("Evolution failed. Missing {0}", c.itemType.name)); return false; } } return true; } // AttemptEvolution will spawn a new weapon for the character, and remove all // the weapons that are supposed to be consumed. public virtual bool AttemptEvolution(ItemData.Evolution evolutionData, int levelUpAmount = 1) { if (!CanEvolve(evolutionData, levelUpAmount)) return false; // Should we consume passives / weapons? bool consumePassives = (evolutionData.consumes & ItemData.Evolution.Consumption.passives) > 0; bool consumeWeapons = (evolutionData.consumes & ItemData.Evolution.Consumption.weapons) > 0; // Loop through all the catalysts and check if we should consume them. foreach (ItemData.Evolution.Config c in evolutionData.catalysts) { if (c.itemType is PassiveData && consumePassives) inventory.Remove(c.itemType, true); if (c.itemType is WeaponData && consumeWeapons) inventory.Remove(c.itemType, true); } // Should we consume ourselves as well? if (this is Passive && consumePassives) inventory.Remove((this as Passive).data, true); else if (this is Weapon && consumeWeapons) inventory.Remove((this as Weapon).data, true); // Add the new weapon onto our inventory. inventory.Add(evolutionData.outcome.itemType); return true; } public virtual bool CanLevelUp() { return currentLevel <= maxLevel; } // Whenever an item levels up, attempt to make it evolve. public virtual bool DoLevelUp() { if (evolutionData == null) return true; // Tries to evolve into every listed evolution of this weapon, // if the weapon's evolution condition is levelling up. foreach (ItemData.Evolution e in evolutionData) { if (e.condition == ItemData.Evolution.Condition.auto) AttemptEvolution(e); } return true; } // What effects you receive on equipping an item. public virtual void OnEquip() { } // What effects are removed on unequipping an item. public virtual void OnUnequip() { } }September 4, 2025 at 3:01 pm #18806::It appears that some of your items are not able to find the inventory object. Remove the previous line and add this line so we can see which are the items:
// Checks if a specific evolution is possible. public virtual bool CanEvolve(ItemData.Evolution evolution, int levelUpAmount = 1) { // Cannot evolve if the item hasn't reached the level to evolve. if (evolution.evolutionLevel > currentLevel + levelUpAmount) { Debug.LogWarning(string.Format("Evolution failed. Current level {0}, evolution level {1}", currentLevel, evolution.evolutionLevel)); return false; } print("Checking if {name} can evolve."); // Checks to see if all the catalysts are in the inventory. foreach (ItemData.Evolution.Config c in evolution.catalysts) { Item item = inventory.Get(c.itemType); if (!item || item.currentLevel < c.level) { Debug.LogWarning(string.Format("Evolution failed. Missing {0}", c.itemType.name)); return false; } } return true; }Just screenshot the errors/ messages that appear. You don’t need to record the whole video.
September 4, 2025 at 5:37 pm #18807::Did you remove the line print(“inventory: ” + inventory); and add print(“Checking if {name} can evolve.”); ?
View post on imgur.com
September 4, 2025 at 5:40 pm #18808::It seems like you’re referencing changes made to the code regarding logging statements. If you want to replace the log that prints the inventory with a log that indicates whether a specific item can evolve, you can update it as follows:
- Remove the line that prints the inventory.
- Add a line to print the item name before checking if it can evolve.
Here’s how you could implement this:
public virtual bool CanEvolve(ItemData.Evolution evolution, int levelUpAmount = 1) { // Log the item name before checking evolution Debug.Log($"Checking if {this.name} can evolve."); // Cannot evolve if the item hasn't reached the level to evolve. if (evolution.evolutionLevel > currentLevel + levelUpAmount) { Debug.LogWarning(string.Format("Evolution failed. Current level {0}, evolution level {1}", currentLevel, evolution.evolutionLevel)); return false; } // Checks to see if all the catalysts are in the inventory. foreach (ItemData.Evolution.Config c in evolution.catalysts) { Item item = inventory.Get(c.itemType); if (!item || item.currentLevel < c.level) { Debug.LogWarning(string.Format("Evolution failed. Missing {0}", c.itemType.name)); return false; } } return true; }Remember to ensure that
this.namecorrectly refers to the name of the item instance. If you have any further questions or need additional modifications, feel free to ask!September 4, 2025 at 11:14 pm #18809September 5, 2025 at 2:49 am #18810September 5, 2025 at 12:53 pm #18811::Try evolving the weapon and show me the Console again with the newly added message. You set up some of your items wrongly, and I’m not sure where the mistakes you make are given the information you provided.
September 5, 2025 at 7:39 pm #18812::Okay, I tried adding the code to the line.
Code : Item
using System.Collections.Generic; using UnityEngine; /// <summary> /// Base class for both the Passive and the Weapon classes. It is primarily intended to handle weapon /// evolution, as we want both weapons and passives to be evolve-able. /// </summary> public abstract class Item : MonoBehaviour { public int currentLevel = 1, maxLevel = 1; protected ItemData.Evolution[] evolutionData; protected PlayerInventory inventory; protected PlayerStats owner; public virtual void Initialise(ItemData data) { maxLevel = data.maxLevel; // Store the evolution data as we have to track wherher // all the catalysts are in the inventory so we can evolve. evolutionData = data.evolutionData; // We have to find a better way to reference the player inventory // in future, as this is inefficient inventory = FindObjectOfType<PlayerInventory>(); owner = FindObjectOfType<PlayerStats>(); } // Call this function to get all the evolutions that the weapon // can currently evolve to. public virtual ItemData.Evolution[] CanEvolve() { List<ItemData.Evolution> possibleEvolutions = new List<ItemData.Evolution>(); // Check each listed evolution and whether it is in // the inventory. foreach (ItemData.Evolution e in evolutionData) { if (CanEvolve(e)) possibleEvolutions.Add(e); } return possibleEvolutions.ToArray(); } // Checks if a specific evolution is possible. public virtual bool CanEvolve(ItemData.Evolution evolution, int levelUpAmount = 1) { // Cannot evolve if the item hasn't reached the level to evolve. if (evolution.evolutionLevel > currentLevel + levelUpAmount) { Debug.LogWarning(string.Format("Evolution failed. Current level {0}, evolution level {1}", currentLevel, evolution.evolutionLevel)); return false; } print("Checking if {name} can evolve."); // Checks to see if all the catalysts are in the inventory. foreach (ItemData.Evolution.Config c in evolution.catalysts) { Item item = inventory.Get(c.itemType); if (!item || item.currentLevel < c.level) { Debug.LogWarning(string.Format("Evolution failed. Missing {0}", c.itemType.name)); return false; } } return true; } // AttemptEvolution will spawn a new weapon for the character, and remove all // the weapons that are supposed to be consumed. public virtual bool AttemptEvolution(ItemData.Evolution evolutionData, int levelUpAmount = 1) { if (!CanEvolve(evolutionData, levelUpAmount)) return false; // Should we consume passives / weapons? bool consumePassives = (evolutionData.consumes & ItemData.Evolution.Consumption.passives) > 0; bool consumeWeapons = (evolutionData.consumes & ItemData.Evolution.Consumption.weapons) > 0; // Loop through all the catalysts and check if we should consume them. foreach (ItemData.Evolution.Config c in evolutionData.catalysts) { if (c.itemType is PassiveData && consumePassives) inventory.Remove(c.itemType, true); if (c.itemType is WeaponData && consumeWeapons) inventory.Remove(c.itemType, true); } // Should we consume ourselves as well? if (this is Passive && consumePassives) inventory.Remove((this as Passive).data, true); else if (this is Weapon && consumeWeapons) inventory.Remove((this as Weapon).data, true); // Add the new weapon onto our inventory. inventory.Add(evolutionData.outcome.itemType); return true; } public virtual bool CanLevelUp() { return currentLevel <= maxLevel; } // Whenever an item levels up, attempt to make it evolve. public virtual bool DoLevelUp() { if (evolutionData == null) return true; // Tries to evolve into every listed evolution of this weapon, // if the weapon's evolution condition is levelling up. foreach (ItemData.Evolution e in evolutionData) { if (e.condition == ItemData.Evolution.Condition.auto) AttemptEvolution(e); } return true; } // What effects you receive on equipping an item. public virtual void OnEquip() { } // What effects are removed on unequipping an item. public virtual void OnUnequip() { } }September 5, 2025 at 7:40 pm #18813::The line
print("Checking if {name} can evolve.");is incorrect. In C#, string interpolation requires a different syntax. The correct version should be:Debug.Log($"Checking if {name} can evolve.");Or if you want to use
print(), which is a Unity-specific method:print($"Checking if {name} can evolve.");The key differences are:
- Using
$before the string for interpolation - Using
Debug.Log()orprint()instead of justprint
Would you like me to explain string interpolation in more detail?
September 6, 2025 at 12:11 am #18814::I’m sorry Masdog. You need to add a $ in front of the print. I missed that out in my code to you. That prints out the name of the item that is causing the issue, so we can home in on what to fix.
print($"Checking if {name} can evolve.");You just need to show me this portion thereafter:
View post on imgur.com
September 6, 2025 at 1:08 pm #18815September 6, 2025 at 1:10 pm #18816::I apologize, but the image link you provided seems to be broken or inaccessible. Could you please re-upload the image or describe what you’re seeing in the result after editing the code? Without being able to see the specific output or error, I cannot provide a precise diagnosis.
Would you be willing to describe the result or re-share the image?
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: