Forum begins after the advertisement:
[Part 16] Garlic weapon bug fixes
Home › Forums › Video Game Tutorial Series › Creating a Rogue-like Shoot-em Up in Unity › [Part 16] Garlic weapon bug fixes
- This topic has 2 replies, 2 voices, and was last updated 1 week, 4 days ago by
Yip Si Yuan.
-
AuthorPosts
-
May 21, 2025 at 4:07 pm #18180::
While working on the Vampire Survivors project, I encountered a bug related to the Garlic weapon’s aura. When the weapon is leveled up, a new aura prefab is instantiated, but the old one isn’t properly destroyed. This leads to overlapping auras, as shown in the screenshot below:
After investigating the issue, I discovered the root cause: the original code attempts to destroy the currentAura directly, which doesn’t correctly remove the GameObject from the scene. The fix is to explicitly destroy currentAura.gameObject instead.
Here’s the relevant change in the AuraWeapon script:
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);if (currentAura) { Destroy(currentAura.gameObject); currentAura = null; } Destroy(currentAura); currentAura = Instantiate(currentStats.auraPrefab, transform); currentAura.weapon = this; currentAura.owner = owner; float area = GetArea(); currentAura.transform.localScale = new Vector3(area, area, area); } } public override void OnUnequip() { if (currentAura) Destroy(currentAura); } public override bool DoLevelUp() { if (!base.DoLevelUp()) return false; // Ensure that the aura is refreshed if a different aura is assigned for a higher level. OnEquip(); // 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; } }This ensures the old aura is properly cleaned up before the new one is instantiated, preventing any unwanted overlap. The call to currentAura = null is added for extra safety, ensuring no lingering references remain.
The next problem the garlic weapon is its inability to scale with additional area effect buffs. For example in my testing when you receive a Candelabrador. The area of the garlic remains unchanged.
Here is the relevent changes in the
Aura.sc
scriptusing System.Collections.Generic; using UnityEngine; /// <summary> /// An aura is a damage-over-time effect that applies to a specific area in timed intervals. /// It is used to give the functionality of Garlic, and it can also be used to spawn holy /// water effects as well. /// </summary> public class Aura : WeaponEffect { Dictionary<EnemyStats, float> affectedTargets = new Dictionary<EnemyStats, float>(); List<EnemyStats> targetsToUnaffect = new List<EnemyStats>(); // Update is called once per frame void Update() { Weapon.Stats stats = weapon.GetStats(); float trueArea = stats.area * Owner.Stats.area; transform.localScale = new Vector3(trueArea, trueArea, trueArea);</ul> Dictionary<EnemyStats, float> affectedTargsCopy = new Dictionary<EnemyStats, float>(affectedTargets); // Loop through every target affected by the aura, and reduce the cooldown // of the aura for it. If the cooldown reaches 0, deal damage to it. foreach (KeyValuePair<EnemyStats, float> pair in affectedTargsCopy) { affectedTargets[pair.Key] -= Time.deltaTime; if (pair.Value <= 0) { if (targetsToUnaffect.Contains(pair.Key)) { // If the target is marked for removal, remove it. affectedTargets.Remove(pair.Key); targetsToUnaffect.Remove(pair.Key); } else { // Reset the cooldown and deal damage. affectedTargets[pair.Key] = stats.cooldown * Owner.Stats.cooldown; pair.Key.TakeDamage(GetDamage(), transform.position, stats.knockback); weapon.ApplyBuffs(pair.Key); // Apply all assigned buffs to the target. // Play the hit effect if it is assigned. if (stats.hitEffect) { Destroy(Instantiate(stats.hitEffect, pair.Key.transform.position, Quaternion.identity), 5f); } } } } } void OnTriggerEnter2D(Collider2D other) { if (other.TryGetComponent(out EnemyStats es)) { // If the target is not yet affected by this aura, add it // to our list of affected targets. if (!affectedTargets.ContainsKey(es)) { // Always starts with an interval of 0, so that it will get // damaged in the next Update() tick. affectedTargets.Add(es, 0); } else { if (targetsToUnaffect.Contains(es)) { targetsToUnaffect.Remove(es); } } } } void OnTriggerExit2D(Collider2D other) { if (other.TryGetComponent(out EnemyStats es)) { // Do not directly remove the target upon leaving, // because we still have to track their cooldowns. if (affectedTargets.ContainsKey(es)) { targetsToUnaffect.Add(es); } } } }
Now the code will constantly check for additional area stat changes and scale the garlic accordingly.
Hope this has been helpful. I’m open to any suggestions for further optimization. If there are other strategies we can explore to improve performance, I’d be glad to hear them.
has upvoted this post. May 21, 2025 at 4:11 pm #18194::These are great observations. Thank you for highlighting them.
has upvoted this post. May 22, 2025 at 1:01 am #18195::Hi! These are the updated scripts, with highlights to clearly show the changes made. This should make it easier to follow along and understand what’s been added or modified.
AuraWeapon.cs
scriptusing 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);if (currentAura) { Destroy(currentAura.gameObject); currentAura = null; } Destroy(currentAura); currentAura = Instantiate(currentStats.auraPrefab, transform); currentAura.weapon = this; currentAura.owner = owner; float area = GetArea(); currentAura.transform.localScale = new Vector3(area, area, area); } } public override void OnUnequip() { if (currentAura) Destroy(currentAura); } public override bool DoLevelUp() { if (!base.DoLevelUp()) return false; // Ensure that the aura is refreshed if a different aura is assigned for a higher level. OnEquip(); // 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; } }Aura.cs
scriptusing System.Collections.Generic; using UnityEngine; /// <summary> /// An aura is a damage-over-time effect that applies to a specific area in timed intervals. /// It is used to give the functionality of Garlic, and it can also be used to spawn holy /// water effects as well. /// </summary> public class Aura : WeaponEffect { Dictionary<EnemyStats, float> affectedTargets = new Dictionary<EnemyStats, float>(); List<EnemyStats> targetsToUnaffect = new List<EnemyStats>(); // Update is called once per frame void Update() { Weapon.Stats stats = weapon.GetStats(); float trueArea = stats.area * Owner.Stats.area; transform.localScale = new Vector3(trueArea, trueArea, trueArea);</ul> Dictionary<EnemyStats, float> affectedTargsCopy = new Dictionary<EnemyStats, float>(affectedTargets); // Loop through every target affected by the aura, and reduce the cooldown // of the aura for it. If the cooldown reaches 0, deal damage to it. foreach (KeyValuePair<EnemyStats, float> pair in affectedTargsCopy) { affectedTargets[pair.Key] -= Time.deltaTime; if (pair.Value <= 0) { if (targetsToUnaffect.Contains(pair.Key)) { // If the target is marked for removal, remove it. affectedTargets.Remove(pair.Key); targetsToUnaffect.Remove(pair.Key); } else {
Weapon.Stats stats = weapon.GetStats();// Reset the cooldown and deal damage. affectedTargets[pair.Key] = stats.cooldown * Owner.Stats.cooldown; pair.Key.TakeDamage(GetDamage(), transform.position, stats.knockback); weapon.ApplyBuffs(pair.Key); // Apply all assigned buffs to the target. // Play the hit effect if it is assigned. if (stats.hitEffect) { Destroy(Instantiate(stats.hitEffect, pair.Key.transform.position, Quaternion.identity), 5f); } } } } } void OnTriggerEnter2D(Collider2D other) { if (other.TryGetComponent(out EnemyStats es)) { // If the target is not yet affected by this aura, add it // to our list of affected targets. if (!affectedTargets.ContainsKey(es)) { // Always starts with an interval of 0, so that it will get // damaged in the next Update() tick. affectedTargets.Add(es, 0); } else { if (targetsToUnaffect.Contains(es)) { targetsToUnaffect.Remove(es); } } } } void OnTriggerExit2D(Collider2D other) { if (other.TryGetComponent(out EnemyStats es)) { // Do not directly remove the target upon leaving, // because we still have to track their cooldowns. if (affectedTargets.ContainsKey(es)) { targetsToUnaffect.Add(es); } } } } -
AuthorPosts
- You must be logged in to reply to this topic.