Forum begins after the advertisement:
[Part 16] Adding info to the weapon
Home › Forums › Video Game Tutorial Series › Creating a Rogue-like Shoot-em Up in Unity › [Part 16] Adding info to the weapon
- This topic has 11 replies, 2 voices, and was last updated 6 months ago by Terence.
-
AuthorPosts
-
May 20, 2024 at 8:15 am #14719::
I’ve worked out a way to collect each weapon damage and show it have it come up in the console, I will up load it when I’m on my weekend but my question is what the best way to store it in the weapon data at the moment I’ve added a weapon tag name in to weapon script and it calls on that name and then shows the damage done.
How do I add a damageTotal float to the weapon script that doesn’t come up as a base stat, as it doesn’t need to change when weapon levels upMay 20, 2024 at 3:21 pm #14721::Hi Cam,
Just to clarify, you want to show the weapon damage of each weapon on their respective weapon tooltip popups. Is that it?
May 20, 2024 at 8:03 pm #14723May 21, 2024 at 9:25 am #14725::ok its not 100% there is some issues like the lighting ring dosnt work for this method..
Weapon.cd
using System.Collections; using System.Collections.Generic; using UnityEngine; ///
/// 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. /// public abstract class Weapon : Item { [System.Serializable] public class Stats : LevelData { [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; public float damageDone = 0f; // 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.damageDone = s2.damageDone + s1.damageDone; 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; 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) { base.Initialise(data); this.data = data; if (data.baseStats != null) { currentStats = data.baseStats; currentStats.damageDone = 0; // Reset damageDone at the start of each game } else { Debug.LogError("WeaponData baseStats is null!"); currentStats = new Stats(); // Fallback to a new instance to avoid null reference issues } movement = GetComponentInParent(); ActivateCooldown(); } protected virtual void Update() { currentCooldown -= Time.deltaTime; if (currentCooldown <= 0f) //Once the cooldown becomes 0, attack { Attack(currentStats.number + owner.Stats.amount); } } // 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 += (Stats)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()) { float damageDealt = GetDamage() * attackCount; ActivateCooldown(); currentStats.damageDone += damageDealt; // Update total damage done 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.Stats.might; } // Get the area, including modifications from the player's stats. public virtual float GetArea() { return currentStats.area * owner.Stats.area; } // For retrieving the weapon's stats. public virtual Stats GetStats() { return currentStats; } // Refreshes the cooldown of the weapon. // If is true, refreshes only when currentCooldown < 0. public virtual bool ActivateCooldown(bool strict = false) { // When is enabled and the cooldown is not yet finished, // do not refresh the cooldown. if(strict && currentCooldown > 0) return false; // Calculate what the cooldown is going to be, factoring in the cooldown // reduction stat in the player character. float actualCooldown = currentStats.cooldown * Owner.Stats.cooldown; // Limit the maximum cooldown to the actual cooldown, so we cannot increase // the cooldown above the cooldown stat if we accidentally call this function // multiple times. currentCooldown = Mathf.Min(actualCooldown, currentCooldown + actualCooldown); return true; } } this is adding the float to hold the Damage Done by the weapon.
and then adding a way to reset the Damage Done to 0 when the weapon in equipped, with a null reference.WeaponEfect.cs
using UnityEngine; public abstract class WeaponEffect : MonoBehaviour { [HideInInspector] public PlayerStats owner; [HideInInspector] public Weapon weapon; public PlayerStats Owner { get { return owner; } } public float GetDamage() { Weapon.Stats weaponStats = weapon.GetStats(); float damage = weapon.GetDamage(); // Add the calculated damage to the weapon's damageDone field weaponStats.damageDone += damage; return damage; } }
and ive add the WeaponStats.damageDone to get added to for each damage done( i do think i can tidy this one up a bit more tho)
May 21, 2024 at 9:27 am #14726::the green text didnt work =( in the weapon.cs changes are in the “[Header(“Values”)]” and the “public virtual void Initialise(WeaponData data)”
May 21, 2024 at 11:53 am #14727::I get what you’re going for now. I would code it in a simpler manner. With the Weapon script, I will put
damageDone
in as a protected variable outside ofWeapon.Stats
. I make it protected so that subclasses (our individual weapons) can modify the variable if needed. I put it outside ofWeapon.Stats
because that makes it easier to access, and because I would not classify it as a weapon stat — but you can put it anywhere you feel is appropriate.As for recording the damage, I would put it in
Weapon.GetDamage()
instead ofWeaponEffect.GetDamage()
, because every weapon needs to callWeapon.GetDamage()
at some point or other, but not every weapon uses a weapon effect.I will also add an optional variable in
GetDamage()
so that I can control whether the call will add the damage amount todamageDone
. Something like this:public virtual float GetDamage(bool doNotCount = false) {
returnfloat damage = currentStats.GetDamage() * owner.Stats.might; if(!doNotCount) damageDone += damage; return damage; }May 21, 2024 at 12:02 pm #14728::also added in the Weapon.cs
protected virtual bool Attack(int attackCount = 1) { if (CanAttack()) { float damageDealt = GetDamage() * attackCount; ActivateCooldown(); currentStats.damageDone += damageDealt; // Update total damage done return true; } return false; }
May 21, 2024 at 2:21 pm #14733::also added in the Weapon.cs
If you’re adding it in
GetDamage()
, you don’t have to add it anywhere else, provided you always retrieve the damage dealt withGetDamage()
.(If you’re using my code above), in instances where you don’t want to add to the damage done, just call
GetDamage(true)
instead of the usualGetDamage()
, and the call with thetrue
value will not add to the total damage done.May 22, 2024 at 10:00 am #14743May 22, 2024 at 1:53 pm #14744::That’s nice! You can try and modularise the display of the total damage done for weapons like we’ve done for the character stats in Part 18.
May 23, 2024 at 12:57 pm #14756::this is my script for displying the Weapon Damage Totals
using System.Collections; using System.Collections.Generic; using UnityEngine; using TMPro; using UnityEngine.UI; using System.Linq; // Add this for LINQ public class WeaponUIDisplay : MonoBehaviour { public Transform slotsContainer; // Reference to the parent container for weapon slots public WeaponData[] allWeapons; // Array to hold all weapon ScriptableObjects private List<Transform> weaponSlots = new List<Transform>(); // List to keep track of weapon slots void Start() { if (slotsContainer == null) { Debug.LogError("WeaponUIDisplay: No slotsContainer assigned."); return; } if (allWeapons == null || allWeapons.Length == 0) { Debug.LogWarning("WeaponUIDisplay: No weapons assigned to the allWeapons array."); return; } // Set damageTotal to 0 for all weapons foreach (WeaponData weaponData in allWeapons) { if (weaponData != null) { weaponData.damageTotal = 0; } } // Get all weapon slots and deactivate them for (int i = 0; i < slotsContainer.childCount; i++) { Transform slot = slotsContainer.GetChild(i); weaponSlots.Add(slot); slot.gameObject.SetActive(false); } // Start the coroutine to update the UI every 1 second StartCoroutine(UpdateUIRoutine()); } // Coroutine to update the UI every 1 second private IEnumerator UpdateUIRoutine() { while (true) { // Sort the weapons by damageTotal in descending order var sortedWeapons = allWeapons .Where(weapon => weapon != null && weapon.baseStats != null && weapon.damageTotal > 1) .OrderByDescending(weapon => weapon.damageTotal) .ToList(); // Deactivate all slots initially foreach (var slot in weaponSlots) { slot.gameObject.SetActive(false); } // Iterate through sorted weapons and update the slots int slotIndex = 0; foreach (WeaponData weaponData in sortedWeapons) { if (slotIndex < weaponSlots.Count) { // Get the slot to update Transform slot = weaponSlots[slotIndex]; // Get the Image and TMP_Text components Image weaponIcon = slot.Find("WeaponIcon").GetComponent<Image>(); TMP_Text weaponText = slot.Find("WeaponText").GetComponent<TMP_Text>(); // Activate the slot slot.gameObject.SetActive(true); // Set the weapon icon weaponIcon.sprite = weaponData.icon; // Using the icon property from WeaponData // Set the weapon info text weaponText.text = weaponData.weaponName + ": " + Mathf.FloorToInt(weaponData.damageTotal); slotIndex++; // Move to the next slot } } yield return new WaitForSeconds(1f); // Wait for 1 second before updating again } } }
May 23, 2024 at 4:31 pm #14773 -
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: