Forum begins after the advertisement:


[General] Creating Bloody Tear (Whip Evolution)

Home Forums Video Game Tutorial Series Creating a Rogue-like Shoot-em Up in Unity [General] Creating Bloody Tear (Whip Evolution)

Viewing 4 posts - 1 through 4 (of 4 total)
  • Author
    Posts
  • #15042
    Joseph Tang
    Level 13
    Moderator
    Helpful?
    Up
    1
    ::

    Update 13 October 2024: Made some improvements to the healing portion of the script.

    This is a post to provide detail on how to create the Bloody Tear Weapon Evolution for our Rogue-like Shoot-em Up series.
    To do this we’ll be going through 4 things:

    1. Setting up a Bloody Tear VFX.
    2. Introducing Critical Chance Stat.
    3. Writing the Bloody Tear Projectile Script.
    4. Creating the Weapon Data and Evolution Data.

    For convenience, below are the links to the article and the video that our code & scriptable objects will be based off:


    The Bloody Tear is the evolved form of the Whip. It performs the same function as the Whip weapon, with the added bonus of having a critical hit chance and healing for said critical hits. To create this weapon, we will need to give it: a new Whip VFX, a new projectile script to add the healing function, creating a critical chance mechanic, and setting up it’s Weapon Data.

    1. Setting up a Bloody Tear VFX

    • Duplicate the basic “Whip Projectile” Prefab.
    • Change the “Start Colour” of the Particle System to Red/Crimson.

    View post on imgur.com


    A look at the Bloody Tear VFX and it’s Particle System values.

    2. Introducing Critical Chance Stat

    [This is a stat that will be created in the series soon.]
    [We will be making use of the Luck stat for this mechanic, which you can find in our video here: https://youtu.be/0J0Jmc2lZYA ]

    • Create a new float, critChance to the Weapon.cs under the class Stats, then add it to public static Stats operator.
    • Create a separate protected float currentCritChance (Similarly to currentCooldown) and a public bool criticalHit outside of the class Stats.
    • In the public virtual float GetDamage(), outside of class Stats, add a new if statement to check if the weapon has any critChance, checking if the weapon’s critChance is more than [0]. If the weapon does not have any critChance, we will return the normal GetDamage() function and set criticalHit to false.
    • If the weapon has critChance, set the currentCritChance to critChance multiplied by luck.
    • Add another if statement within the first, to check the currentCritChance against a random value from [1-100]. If the currentCritChance is higher than the random value, return the damage value at twice the value and set criticalHit to true, otherwise return the normal damage and set criticalHit to false.
        public class Stats : LevelData
        {
            ...
            [Header("Values")]
            public float lifespan; // If 0, it will last forever.
            public float damage, damageVariance, area, speed, cooldown, projectileInterval, knockback, critChance;
            public int number, piercing, maxInstances;
    
            ...
    
            public static Stats operator +(Stats s1, Stats s2)
            {
                ...
    
                result.knockback = s1.knockback + s2.knockback;
                result.critChance = s1.critChance + s2.critChance;
                return result;
            }
    
            ...
        }
    
        protected Stats currentStats;
    
        public bool criticalHit; // Checks if the damage done is a critical hit.
    
        protected float currentCooldown, currentCritChance;
    
        protected PlayerMovement movement; // Reference to the player's movement.
    
        ...
    
    
        // 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.
        // If the weapon has any "critChance", the critical chance will be multiplied by the players luck stat.
        // if the critical chance is more than or equal to a randomly generated number, the damage done is doubled.
        public virtual float GetDamage()
        {
    
            
            return currentStats.GetDamage() * owner.Stats.might;
    
            if (currentStats.critChance > 0)
            {
                currentCritChance = currentStats.critChance * owner.Stats.luck;
    
                if (currentCritChance >= Random.Range(1f, 100f))
                {
                    criticalHit = true;
                    return currentStats.GetDamage() * owner.Stats.might * 2;
                }
                else
                {
                    criticalHit = false;
                    return currentStats.GetDamage() * owner.Stats.might;
                }
            }
            else
            {
                criticalHit = false;
                return currentStats.GetDamage() * owner.Stats.might;
            }
        }

    3. Writing the Bloody Tear Projectile Script

    • Create a new subclass script called “BloodyTearProjectile.cs”, inheriting from Projectile.cs
    • In OnTriggerEnter2D(), call the base function and add an if statement to check if the weapon’s criticalHit is true
    • Call the player’s RestoreHealth() method if the weapon returns a critialHit as true, healing the player for a value we’ll set as [16] (following the original Bloody Tear value). This allows the Bloody Tear to heal the player on critical hit.
    public class BloodyTearProjectile : Projectile
    {
        protected override void Start()
        {
            base.Start();
        }
    
        // If the projectile is homing, it will automatically find a suitable target
        // to move towards.
        public override void AcquireAutoAimFacing()
        {
            base.AcquireAutoAimFacing();
        }
    
        // Update is called once per frame
        protected override void FixedUpdate()
        {
            base.FixedUpdate();
        }
    
        protected override void OnTriggerEnter2D(Collider2D other)
        {
            base.OnTriggerEnter2D(other);
            //Check if the damage dealt was a critical hit, then heal player
            if (weapon.criticalHit)
            {
                weapon.owner.RestoreHealth(16);
            }
        }
    }

    4. Creating the Weapon Data and Evolution Data

    Bloody Tear Weapon Data


    Set the values to the following image for the Bloody Tear.

    Whip/Hollow Heart Evolution Data

    • Select the Whip & Hollow Heart Scriptable Object & add a new “Evolution Data” to it.
    • Set the “Condition” to “Treasure Chest” & the “Consumes” to “Weapons”, ensuring the weapon only evolves when picking up a chest and will only remove the weapon involved with the evolution.
    • Set the “Evolution Level” to [8] for the Whip and [5] for the Hollow Heart, before adding the “Catalyst” which selects the other weapon/passive item for the corresponding level of [8] & [5].
    • Select the Bloody Tear Scriptable Object as the outcome and set it at level [1]

    Set the Evolution Data to the following image for the Whip. [Copy the setup for the Hollow Heart]

    That concludes how to create the Bloody Tear Weapon.

    If you encounter any issues in creating the Bloody Tear, you can submit a forum post to go into detail on your problem for further assistance.

    has upvoted this post.
    #16047
    정춤고 (ChoomGo)
    Level 13
    Participant
    Helpful?
    Up
    0
    ::
    
    public class SytheProjectile: Projectile
    {
        protected override void Start()
        {
            base.Start();
        }
    
        // If the projectile is homing, it will automatically find a suitable target
        // to move towards.
        public override void AcquireAutoAimFacing()
        {
            base.AcquireAutoAimFacing();
        }
    
        // Update is called once per frame
        protected override void FixedUpdate()
        {
            base.FixedUpdate();
        }
    
        protected override void OnTriggerEnter2D(Collider2D other)
        {
            base.OnTriggerEnter2D(other);
            //Check if the damage dealt was a critical hit, then heal player
            if (weapon.criticalHit)
            {
    <strong>            PlayerStats player = FindObjectOfType();</strong>
                player.RestoreHealth(16);
            }
        }
    }

    Assets\02. Scripts\Weapon\WeaponEffects\SytheProjectile.cs(31,39): error CS0411: The type arguments for method ‘Object.FindObjectOfType<T>()’ cannot be inferred from the usage. Try specifying the type arguments explicitly.

    I keep encountering errors in this part. How can I fix it? Could you help me?

    #16048
    정춤고 (ChoomGo)
    Level 13
    Participant
    Helpful?
    Up
    1
    ::
    protected override void OnTriggerEnter2D(Collider2D other)
    {
        base.OnTriggerEnter2D(other);
        //Check if the damage dealt was a critical hit, then heal player
        if (weapon.criticalHit)
        {
            PlayerStats playerStats = GetComponentInParent<PlayerStats>();
            playerStats.RestoreHealth(16);
        }
    }

    I resolved it this way, but I’m still not sure if it’s okay.

    has upvoted this post.
    #16049
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    ChoomGo, you did it correct. There is a much more efficient way to do it though. From the projectile, you can actually access the player through weapon.owner without having to use FindObjectOfType<PlayerStats>() (which is computationally more expensive).

    The weapon variable is accessible from WeaponEffect, which Projectile is a subclass of, and it gives you the weapon that spawned the projectile.

    From every weapon, you can use owner to find the PlayerStats object that owns the weapon.

        protected override void OnTriggerEnter2D(Collider2D other)
        {
            base.OnTriggerEnter2D(other);
            //Check if the damage dealt was a critical hit, then heal player
            if (weapon.criticalHit)
            {
                PlayerStats player = FindObjectOfType();
                player.RestoreHealth(16);
                weapon.owner.RestoreHealth(16);
            }
        }

    If you want to have Bloody Tear heal the amount of damage that you deal, you can also record the enemy’s health before and after calling base.OnTriggerEnter2D(other);, and taking the difference to be the damage like so.

    EnemyStats e = other.GetComponent<EnemyStats>();
    float originalHp = 0, afterHp = 0;
    if(e) originalHp = enemyStats.currentHealth;
    base.OnTriggerEnter2D(other);
    if(e) afterHp = enemyStats.currentHealth;
    
    float damageDealt = originalHP - afterHp;
    

    This can then be passed to RestoreHealth():

    weapon.owner.RestoreHealth(damageDealt);
Viewing 4 posts - 1 through 4 (of 4 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: