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

Viewing 12 posts - 1 through 12 (of 12 total)
  • Author
    Posts
  • #14719
    Cam
    Silver Supporter (Patron)

    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 up

    #14721
    Terence
    Keymaster

    Hi Cam,

    Just to clarify, you want to show the weapon damage of each weapon on their respective weapon tooltip popups. Is that it?

    #14723
    Cam
    Silver Supporter (Patron)

    No I’m doing total damage done for each weapon while in the game

    #14725
    Cam
    Silver Supporter (Patron)

    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)

    #14726
    Cam
    Silver Supporter (Patron)

    the green text didnt work =( in the weapon.cs changes are in the “[Header(“Values”)]” and the “public virtual void Initialise(WeaponData data)”

    #14727
    Terence
    Keymaster

    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 of Weapon.Stats. I make it protected so that subclasses (our individual weapons) can modify the variable if needed. I put it outside of Weapon.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 of WeaponEffect.GetDamage(), because every weapon needs to call Weapon.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 to damageDone. Something like this:

        public virtual float GetDamage(bool doNotCount = false)
        {
            returnfloat damage = currentStats.GetDamage() * owner.Stats.might;
            if(!doNotCount) damageDone += damage;
            return damage;
        }
    #14728
    Cam
    Silver Supporter (Patron)

    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;
        }
    
    #14733
    Terence
    Keymaster

    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 with GetDamage().

    (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 usual GetDamage(), and the call with the true value will not add to the total damage done.

    #14743
    Cam
    Silver Supporter (Patron)

    Weapon Damage
    Its working, Not so pretty yet tho

    #14744
    Terence
    Keymaster

    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.

    #14756
    Cam
    Silver Supporter (Patron)

    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
            }
        }
    }
    
    
    #14773
    Terence
    Keymaster

    Thanks Cam.

    Sorry, I didn’t have the time to go through your script on stream prior. Will have a look and drop you my thoughts on this in a bit.

Viewing 12 posts - 1 through 12 (of 12 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: