Forum begins after the advertisement:
[Part 15] Unable to upgrade knife / garlic
Home › Forums › Video Game Tutorial Series › Creating a Rogue-like Shoot-em Up in Unity › [Part 15] Unable to upgrade knife / garlic
- This topic has 7 replies, 3 voices, and was last updated 1 week, 3 days ago by
Terence.
-
AuthorPosts
-
August 17, 2025 at 4:16 pm #18633::
@masdogjooner6117 is having an issue with knife upgrades.
I followed the video up to part 15, but when I tested playing the game, I found that during gameplay I couldn’t upgrade. When I pressed upgrade, the popup that appeared didn’t add anything. But when I upgraded the knife, the knife disappeared from the player instead of throwing two knives. Also, when I chose the Garlic Aura weapon at the start of the game, I still only got the knife. What should I do? Is there a fix for this in another video?
Spinach, Pummarol, and Wings can still be upgraded, but Knife and Garlic can’t. I press upgrade and nothing happens. Knife upgraded and the knife disappeared. Garlic upgraded and nothing happened.
August 17, 2025 at 5:21 pm #18634::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() { } }
August 17, 2025 at 5:31 pm #18635::It looks like you’re working on an item system in Unity that includes evolution mechanics for weapons and passives. Your code seems well-structured, but I notice that there are HTML encoded characters (like
<
and>
) in your code, which should be replaced with “ respectively.Here’s a cleaned-up version of your class:
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 whether // 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(); owner = FindObjectOfType(); } // Call this function to get all the evolutions that the weapon // can currently evolve to. public virtual ItemData.Evolution[] CanEvolve() { List possibleEvolutions = new List(); // 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 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() { } }
Make sure to replace the
ItemData
,PlayerInventory
,PlayerStats
,PassiveData
, andWeaponData
classes as necessary, ensuring they align with your project.If you have any other specific issues or errors you're encountering in this code, feel free to provide more details!
August 17, 2025 at 5:35 pm #18636::using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// Component to be attached to all Weapon prefabs. The Weapon prefab works together with the WeaponData /// ScriptableObjects to manage and run the behaviours of all weapons in the game. /// </summary> public class Weapon : Item { [System.Serializable] public struct Stats { public string name, description; [Header("Visuals")] public Projectile projectilePrefab; // If attached, a projectile will spawn every time the weapon cools down. public Aura auraPrefab; // If attached, an aura will spawn when weapon is equipped. public ParticleSystem hitEffect; public Rect spawnVariance; [Header("Values")] public float lifespan; // If 0, it will last forever. public float damage, damageVariance, area, speed, cooldown, projectileInterval, knockback; public int number, piercing, maxInstances; // Allows us to use the + operator to add 2 Stats together. // Very important later when we want to increase our weapon stats. public static Stats operator +(Stats s1, Stats s2) { Stats result = new Stats(); result.name = s2.name ?? s1.name; result.description = s2.description ?? s1.description; result.projectilePrefab = s2.projectilePrefab ?? s1.projectilePrefab; result.auraPrefab = s2.auraPrefab ?? s1.auraPrefab; result.hitEffect = s2.hitEffect == null ? s1.hitEffect : s2.hitEffect; result.spawnVariance = s2.spawnVariance; result.lifespan = s1.lifespan + s2.lifespan; result.damage = s1.damage + s2.damage; result.damageVariance = s1.damageVariance + s2.damageVariance; result.area = s1.area + s2.area; result.speed = s1.speed + s2.speed; result.cooldown = s1.cooldown + s2.cooldown; result.number = s1.number + s2.number; result.piercing = s1.piercing + s2.piercing; result.projectileInterval = s1.projectileInterval + s2.projectileInterval; result.knockback = s1.knockback + s2.knockback; return result; } // Get damage dealt. public float GetDamage() { return damage + Random.Range(0, damageVariance); } } protected Stats currentStats; public WeaponData data; protected float currentCooldown; protected PlayerMovement movement; //Reference to the player's movement. // For dynamically created weapons, call initialise to set everything up. public virtual void Initialise(WeaponData data) { maxLevel = data.maxLevel; owner = FindObjectOfType<PlayerStats>(); this.data = data; currentStats = data.baseStats; movement = GetComponentInParent<PlayerMovement>(); currentCooldown = currentStats.cooldown; } protected virtual void Awake() { // Assign the stats early, as it will be used by other scripts later on. if (data) currentStats = data.baseStats; } protected virtual void Start() { // Don't initialise the weapon if the weapon data is not assigned. if (data) { Initialise(data); } } protected virtual void Update() { currentCooldown -= Time.deltaTime; if (currentCooldown <= 0f) //Once the cooldown becomes 0, attack { Attack(currentStats.number); } } // Levels up the weapon by 1, and calculates the corresponding stats. public override bool DoLevelUp() { base.DoLevelUp(); // Prevent level up if we are already at max level. if (!CanLevelUp()) { Debug.LogWarning(string.Format("Cannot level up {0} to Level {1}, max level of {2} already reached.", name, currentLevel, data.maxLevel)); return false; } // Otherwise, add stats of the next level to our weapon. currentStats += data.GetLevelData(++currentLevel); return true; } // Lets us check whether this weapon can attack at this current moment. public virtual bool CanAttack() { return currentCooldown <= 0; } // Performs an attack with the weapon. // Returns true if the attack was successful. // This doesn't do anything. We have to override this at the child class to add a behaviour. protected virtual bool Attack(int attackCount = 1) { if (CanAttack()) { currentCooldown += currentStats.cooldown; return true; } return false; } // Gets the amount of damage that the weapon is supposed to deal. // Factoring in the weapon's stats (including damage variance), // as well as the character's Might stat. public virtual float GetDamage() { return currentStats.GetDamage() * owner.CurrentMight; } // For retrieving the weapon's stats public virtual Stats GetStats() { return currentStats; } }
August 17, 2025 at 5:36 pm #18637::Based on the reference information, I can help you with your query. However, you haven’t actually asked a specific question. Could you clarify what specific aspect of the Item or Weapon classes you would like assistance with?
Some potential areas I could help you with include:
- Understanding the evolution mechanics
- Explaining the level up process
- Clarifying the purpose of specific methods
- Identifying potential improvements or optimizations
Would you like to provide more details about what you’re looking to understand or achieve?
August 17, 2025 at 11:36 pm #18642::Sorry about the AI response masdog, can you share your Garlic script as well? I will help you prompt the AI. He can be quite anal about the formatting sometimes.
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// Component to be attached to all Weapon prefabs. The Weapon prefab works together with the WeaponData /// ScriptableObjects to manage and run the behaviours of all weapons in the game. /// </summary> public class Weapon : Item { [System.Serializable] public struct Stats { public string name, description; [Header("Visuals")] public Projectile projectilePrefab; // If attached, a projectile will spawn every time the weapon cools down. public Aura auraPrefab; // If attached, an aura will spawn when weapon is equipped. public ParticleSystem hitEffect; public Rect spawnVariance; [Header("Values")] public float lifespan; // If 0, it will last forever. public float damage, damageVariance, area, speed, cooldown, projectileInterval, knockback; public int number, piercing, maxInstances; // Allows us to use the + operator to add 2 Stats together. // Very important later when we want to increase our weapon stats. public static Stats operator +(Stats s1, Stats s2) { Stats result = new Stats(); result.name = s2.name ?? s1.name; result.description = s2.description ?? s1.description; result.projectilePrefab = s2.projectilePrefab ?? s1.projectilePrefab; result.auraPrefab = s2.auraPrefab ?? s1.auraPrefab; result.hitEffect = s2.hitEffect == null ? s1.hitEffect : s2.hitEffect; result.spawnVariance = s2.spawnVariance; result.lifespan = s1.lifespan + s2.lifespan; result.damage = s1.damage + s2.damage; result.damageVariance = s1.damageVariance + s2.damageVariance; result.area = s1.area + s2.area; result.speed = s1.speed + s2.speed; result.cooldown = s1.cooldown + s2.cooldown; result.number = s1.number + s2.number; result.piercing = s1.piercing + s2.piercing; result.projectileInterval = s1.projectileInterval + s2.projectileInterval; result.knockback = s1.knockback + s2.knockback; return result; } // Get damage dealt. public float GetDamage() { return damage + Random.Range(0, damageVariance); } } protected Stats currentStats; public WeaponData data; protected float currentCooldown; protected PlayerMovement movement; //Reference to the player's movement. // For dynamically created weapons, call initialise to set everything up. public virtual void Initialise(WeaponData data) { maxLevel = data.maxLevel; owner = FindObjectOfType<PlayerStats>(); this.data = data; currentStats = data.baseStats; movement = GetComponentInParent<PlayerMovement>(); currentCooldown = currentStats.cooldown; } protected virtual void Awake() { // Assign the stats early, as it will be used by other scripts later on. if (data) currentStats = data.baseStats; } protected virtual void Start() { // Don't initialise the weapon if the weapon data is not assigned. if (data) { Initialise(data); } } protected virtual void Update() { currentCooldown -= Time.deltaTime; if (currentCooldown <= 0f) //Once the cooldown becomes 0, attack { Attack(currentStats.number); } } // Levels up the weapon by 1, and calculates the corresponding stats. public override bool DoLevelUp() { base.DoLevelUp(); // Prevent level up if we are already at max level. if (!CanLevelUp()) { Debug.LogWarning(string.Format("Cannot level up {0} to Level {1}, max level of {2} already reached.", name, currentLevel, data.maxLevel)); return false; } // Otherwise, add stats of the next level to our weapon. currentStats += data.GetLevelData(++currentLevel); return true; } // Lets us check whether this weapon can attack at this current moment. public virtual bool CanAttack() { return currentCooldown <= 0; } // Performs an attack with the weapon. // Returns true if the attack was successful. // This doesn't do anything. We have to override this at the child class to add a behaviour. protected virtual bool Attack(int attackCount = 1) { if (CanAttack()) { currentCooldown += currentStats.cooldown; return true; } return false; } // Gets the amount of damage that the weapon is supposed to deal. // Factoring in the weapon's stats (including damage variance), // as well as the character's Might stat. public virtual float GetDamage() { return currentStats.GetDamage() * owner.CurrentMight; } // For retrieving the weapon's stats public virtual Stats GetStats() { return currentStats; } }
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() { } }
@Alp.Apustaja do you see anything wrong with the
Item
andWeapon
classes that would be preventing the Garlic weapon script from upgrading? Garlic uses theAuraWeapon
script.August 18, 2025 at 1:35 am #18651::After carefully reviewing the
Item
andWeapon
classes, I don’t see any immediate issues that would prevent the Garlic weapon from upgrading. However, to provide a more definitive answer, I would need to see:- The
AuraWeapon
script - The
WeaponData
for the Garlic weapon - The implementation of the
PlayerInventory.Add()
andPlayerInventory.Remove()
methods
Could you provide those additional scripts or code snippets? Without seeing the full context, I can only speculate about potential upgrade issues.
Specifically, I’d like to understand:
- How is the Garlic weapon’s level data stored?
- How does the
PlayerInventory
handle adding and removing weapons? - Are there any custom implementations in the
AuraWeapon
script that might interfere with the standard upgrade process?
August 18, 2025 at 8:00 pm #18680::using UnityEngine; public class AuraWeapon : Weapon { protected Aura currentAura; // Update is called once per frame protected override void Update() { } public override void OnEquip() { // Try to replace the aura the weapon has with a new one. if (currentStats.auraPrefab) { if (currentAura) Destroy(currentAura); currentAura = Instantiate(currentStats.auraPrefab, transform); currentAura.weapon = this; currentAura.owner = owner; } } public override void OnUnequip() { if (currentAura) Destroy(currentAura); } public override bool DoLevelUp() { if (!base.DoLevelUp()) return false; // If there is an aura attached to this weapon, we update the aura. if (currentAura) { currentAura.transform.localScale = new Vector3(currentStats.area, currentStats.area, currentStats.area); } return true; } }
August 18, 2025 at 8:05 pm #18681::After reviewing the
AuraWeapon
script, the implementation looks correct. TheDoLevelUp()
method follows the standard pattern:- It calls the base
DoLevelUp()
method - If the base method returns false, it stops the level up process
- If an aura exists, it updates the aura’s scale based on the current stats
There are no obvious issues in this script that would prevent the Garlic weapon from upgrading.
To help diagnose any potential upgrade problems, I have a few clarifying questions:
- Can you confirm that the Garlic weapon’s
WeaponData
has level-up data defined? - Are you seeing any specific error messages when trying to level up the Garlic weapon?
- Can you show the implementation of the
PlayerInventory.Add()
andPlayerInventory.Remove()
methods?
These details would help pinpoint any potential issues with the weapon upgrade mechanism.
August 18, 2025 at 9:53 pm #18683::Let’s fix the issue of the Garlic character having the Knife first. You need to ensure that the Garlic button has the CharacterData of the garlic character, not the knife character.
After that’s done, test out the garlic’s level up again.
August 18, 2025 at 10:47 pm #18688::Okay, so I created a Character Data Garlic and inserted it, and set the Layer to Foreground so I can see the Garlic Aura weapon, right? https://youtu.be/vlwL3CYMMFM
has upvoted this post. August 18, 2025 at 10:50 pm #18689::I apologize, but I cannot actually view the YouTube video link you’ve shared. Could you describe what you’re seeing or experiencing with the Garlic weapon?
Specifically, I’m interested in understanding:
- Are you able to select the Garlic character?
- Is the Garlic weapon spawning correctly?
- Are you encountering any specific issues with the weapon’s visibility or functionality?
Please provide more details about what you’re observing so I can help you troubleshoot effectively.
August 19, 2025 at 1:21 am #18691::Okay, so I created a Character Data Garlic and inserted it, and set the Layer to Foreground so I can see the Garlic Aura weapon, right? https://youtu.be/vlwL3CYMMFM
Yup, all of this is correct.
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: