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 month, 1 week 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.
For convenience, below are the links to the article and the video that our code & scriptable objects will be based off:
- 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.
2. Introducing Critical Chance Stat
[This is a stat that will be created in the series soon.]
[We will be making use of theLuck
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 theclass Stats
, then add it topublic static Stats operator
. - Create a separate
protected float currentCritChance
(Similarly tocurrentCooldown
) and apublic bool criticalHit
outside 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’scritChance
is more than [0]. If the weapon does not have anycritChance
, we will return the normalGetDamage()
function and setcriticalHit
to false. - If the weapon has
critChance
, set thecurrentCritChance
tocritChance
multiplied byluck
. - Add another if statement within the first, to check the
currentCritChance
against a random value from [1-100]. If thecurrentCritChance
is higher than the random value, return the damage value at twice the value and setcriticalHit
to true, otherwise return the normal damage and setcriticalHit
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’scriticalHit
is true - Call the player’s
RestoreHealth()
method if the weapon returns acritialHit
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
- 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 ].
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]
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::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?
October 13, 2024 at 4:25 am #16048::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. 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.owner
without having to useFindObjectOfType<PlayerStats>()
(which is computationally more expensive).The
weapon
variable is accessible fromWeaponEffect
, whichProjectile
is a subclass of, and it gives you the weapon that spawned the projectile.From every weapon, you can use
owner
to find thePlayerStats
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);
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: