Get 25% off your next order from the Unity Asset Store: YW86GYBU8S.
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)
- This topic has 3 replies, 3 voices, and was last updated 1 year ago by
Terence.
-
AuthorPosts
-
June 7, 2024 at 3:12 pm #15042::
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:
- Setting up a Bloody Tear VFX.
- Introducing Critical Chance Stat.
- Writing the Bloody Tear Projectile Script.
- Creating the Weapon Data and Evolution Data.
- Article Link: https://blog.terresquall.com/2023/12/creating-a-rogue-like-vampire-survivors-part-15/
- Video Link: https://youtu.be/bbktg7OPyFU?si=Ehm10w5kME4VqD0k
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 theLuckstat for this mechanic, which you can find in our video here: https://youtu.be/0J0Jmc2lZYA ]- Create a new float,
critChanceto the Weapon.cs under theclass Stats, then add it topublic static Stats operator. - Create a separate
protected float currentCritChance(Similarly tocurrentCooldown) and apublic bool criticalHitoutside of theclass Stats. - In the
public virtual float GetDamage(), outside ofclass Stats, add a new if statement to check if the weapon has anycritChance, checking if the weapon’scritChanceis more than [0]. If the weapon does not have anycritChance, we will return the normalGetDamage()function and setcriticalHitto false. - If the weapon has
critChance, set thecurrentCritChancetocritChancemultiplied byluck. - Add another if statement within the first, to check the
currentCritChanceagainst a random value from [1-100]. If thecurrentCritChanceis higher than the random value, return the damage value at twice the value and setcriticalHitto true, otherwise return the normal damage and setcriticalHitto 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’scriticalHitis true - Call the player’s
RestoreHealth()method if the weapon returns acritialHitas 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
- Create a new scriptable object using “Weapon Data” & select the “Whip Weapon” Behaviour.
- input the relevant values for the Bloody Tear [Which can be found here: https://vampire-survivors.fandom.com/wiki/Bloody_Tear ].
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. October 13, 2024 at 4:10 am #16047::<code> 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); } } }</code>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?
October 13, 2024 at 4:25 am #16048::<code>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); } }</code>I resolved it this way, but I’m still not sure if it’s okay.
has upvoted this post. October 13, 2024 at 1:08 pm #16049::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.ownerwithout having to useFindObjectOfType<PlayerStats>()(which is computationally more expensive).The
weaponvariable is accessible fromWeaponEffect, whichProjectileis a subclass of, and it gives you the weapon that spawned the projectile.From every weapon, you can use
ownerto find thePlayerStatsobject 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); -
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: