VIDEO
Game Engine - Unity Engine, C#
Learning Outcome - Ability System, Buff/Debuff System
About the Project :
I tried to implement Ability system of Heros, Buffs and Debuffs and CoolDown system of Dota 2 Game.
I implemented different types of abilites, like targeted abilities, non targeted abilities, and passive abilites.
The project also have player that uses click-to-move system and emenies that follows player and attack them when in attack range. All of these implemented using UnityNavMesh. Player and enemies also have attack system which will take Physical Damage, while damage done by Abilities act as Magical Damage.
But purpose of this project to recreate abilities of Dota 2 heroes.
Below are some detailed example of abilities from above video.
Bristleback 3rd Ability :
Ability Type - Passive
Discription - This ability helps BristleBack to take less damage whenever he takes damage from rear or side.
White lines are for dubug purpose to see if damage is taken from back or not, and damage taken from side starts with red line and end at white line.
Extra - If Bristleback take more than certain amount of damage from the back, he release Quill Spray.
Video and Code Snippet are below.
public void TakeHit(float damage, Vector3 attackerDirection, string damageType, float spellAmpli)
{
float hitDirection = (180 + attackerDirection.y) - transform.localEulerAngles.y;
float reducedDamage;
//Side Damage - 8% Damage Reduction
if ((hitDirection > 70 && hitDirection < 110) || (hitDirection > -110 && hitDirection < -70))
{
if (damageType == "physical")
{
reducedDamage = damage * (1 - (8f / 100));
//Debug.Log("Side Damage" + " " + reducedDamage);
GetComponent().TakePhysicalDamage(reducedDamage);
}
else if (damageType == "magical")
{
reducedDamage = damage * (1 - (8f / 100));
//Debug.Log("Side Damage" + " " + reducedDamage);
GetComponent().TakeMagicalDamage(reducedDamage, spellAmpli);
}
else if(damageType == "pure")
{
GetComponent().TakePureDamage(damage);
}
}
//Back Damage - 16% Damage Reduction, and Higher Damage will trigger 2nd ability
else if ((hitDirection > 110 && hitDirection < 250) || (hitDirection > -250 && hitDirection < -110))
{
if (damage > 20)
{
GetComponent().SprayQuill(false);
}
if (damageType == "physical")
{
reducedDamage = damage * (1 - (16f / 100));
//Debug.Log("Rear Damage" + " " + reducedDamage);
GetComponent().TakePhysicalDamage(reducedDamage);
}
else if (damageType == "magical")
{
reducedDamage = damage * (1 - (16f / 100));
//Debug.Log("Rear Damage" + " " + reducedDamage);
GetComponent().TakeMagicalDamage(reducedDamage, spellAmpli);
}
else if (damageType == "pure")
{
GetComponent().TakePureDamage(damage);
}
}
else
{
if (damageType == "physical")
{
//reducedDamage = damage * (1 - (8 / 100));
//Debug.Log("Front Damage");
GetComponent().TakePhysicalDamage(damage);
}
else if (damageType == "magical")
{
//reducedDamage = damage * (1 - (8 / 100));
//Debug.Log("Front Damage");
GetComponent().TakeMagicalDamage(damage, spellAmpli);
}
else if (damageType == "pure")
{
GetComponent().TakePureDamage(damage);
}
}
}
private void OnDrawGizmos()
{
float positiveSideAngle = (70f + transform.localEulerAngles.y) * Mathf.Deg2Rad;
float positiveRearAngle = (110f + transform.localEulerAngles.y) * Mathf.Deg2Rad;
//float distance = 1f;
Vector3 myPos = new Vector3(transform.position.x, 0f, transform.position.z);
Vector3 finalPositiveSidePos = new Vector3(5 * Mathf.Sin(positiveSideAngle), 0f, 5 * Mathf.Cos(positiveSideAngle));
Vector3 posSideDirection = (myPos + finalPositiveSidePos);
Gizmos.color = Color.red;
Gizmos.DrawLine(myPos, posSideDirection);
Vector3 finalPositiveRearPos = new Vector3(5 * Mathf.Sin(positiveRearAngle), 0f, 5 * Mathf.Cos(positiveRearAngle));
Vector3 posRearDirection = (myPos + finalPositiveRearPos);
Gizmos.color = Color.white;
Gizmos.DrawLine(myPos, posRearDirection);
float negetiveSideAngle = (-70f + transform.localEulerAngles.y) * Mathf.Deg2Rad;
float negetiveRearAngle = (-110f + transform.localEulerAngles.y) * Mathf.Deg2Rad;
//float distance = 1f;
//Vector3 myPos = new Vector3(transform.position.x, 1f, transform.position.z);
Vector3 finalNegetiveSidePos = new Vector3(5 * Mathf.Sin(negetiveSideAngle), 0f, 5 * Mathf.Cos(negetiveSideAngle));
Vector3 negSideDirection = (myPos + finalNegetiveSidePos);
Gizmos.color = Color.red;
Gizmos.DrawLine(myPos, negSideDirection);
Vector3 finalNegetiveRearPos = new Vector3(5 * Mathf.Sin(negetiveRearAngle), 0f, 5 * Mathf.Cos(negetiveRearAngle));
Vector3 negRearDirection = (myPos + finalNegetiveRearPos);
Gizmos.color = Color.white;
Gizmos.DrawLine(myPos, negRearDirection);
}
BristleBack 1st Ability :
Ability Type - Target Unit
Discription - This ability add debuff to the target which reduce the armor and movement speed of target enemies. Multiple cast on same target stack up and refresh the duration of debuff.
Extra - This ability can stack upto maximum 4 times. The first debuff has more impact and after that stacked debuff affects the target less.
Video and Code Snippet are below.
public class GooDebuff : MonoBehaviour
{
public int currentStack = 0;
int maxStack = 4;
float duration = 5f;
float baseArmorReduction = 2f;
float armorReductionPerStack = 1.4f;
float baseMoveSpeedSlow = 20f;
float moveSpeedSlowPerStatck = 3f;
bool isDuffApplied = false;
float startTime;
// Start is called before the first frame update
void Start()
{
//buffDebuffPanle = FindObjectOfType();
StartCoroutine(ApplyDebuff());
}
IEnumerator ApplyDebuff()
{
startTime = 0f;
while (startTime <= duration)
{
startTime += Time.deltaTime;
if (!isDuffApplied)
{
//startTime = 0;
isDuffApplied = true;
// GameObject debuffImage = Instantiate(gooDebuffImage, buffDebuffPanle.transform) as GameObject;
GetComponent().ChangeArmor(-baseArmorReduction);
GetComponent().ChangeMoveSpeed(-baseMoveSpeedSlow);
}
//buffDebuffPanle.transform.Find("GooDebuffImage").GetComponent().fillAmount = startTime / duration;
yield return null;
}
//remove debuff
isDuffApplied = false;
GetComponent().ChangeArmor(baseArmorReduction + currentStack * armorReductionPerStack);
//float defaultMoveSpeed = (baseMoveSpeedSlow + 3 * currentStack) / (100 - (baseArmorReduction + 3 * currentStack));
//GetComponent().ChangeMoveSpeed(Mathf.Ceil(defaultMoveSpeed * 100));
if (currentStack == 0)
GetComponent().ChangeMoveSpeed((baseMoveSpeedSlow / (100 - baseMoveSpeedSlow)) * 100);
else if(currentStack > 0)
{
for (int i = 1; i <= currentStack; i++)
{
GetComponent().ChangeMoveSpeed((moveSpeedSlowPerStatck / (100 - moveSpeedSlowPerStatck)) * 100);
}
GetComponent().ChangeMoveSpeed((baseMoveSpeedSlow / (100 - baseMoveSpeedSlow)) * 100);
}
Destroy(this);
}
public void StackDebuff()
{
currentStack++;
if (isDuffApplied && currentStack <= maxStack)
{
startTime = 0;
GetComponent().ChangeArmor(-armorReductionPerStack);
GetComponent().ChangeMoveSpeed(-moveSpeedSlowPerStatck);
}
else if (currentStack > maxStack)
currentStack = maxStack;
}
}
BristleBack 4th Ability :
Ability Type - Passive
Discription - This ability add Buff on BristleBack whenever he cast any ability and this buff increase the movement speed and damage of this hero.
Extra - This buff can stack upto maximum 5 times. Each stack has its own duration and does not reset the buff duration of previous stack.
Video and Code Snippet are below.
//Called from another ability class, when ability is casted
if (gameObject.GetComponent().lastAbility.Count < 5)
{
gameObject.AddComponent();
}
//BristleFuryBuff class
public class BristleFuryBuff : MonoBehaviour
{
float bonusDamagePerStack = 22f;
float bonusMoveSpeedPerStack = 3f;
float duration = 14f;
float startTime;
bool isDuffApplied = false;
// Start is called before the first frame update
void Start()
{
gameObject.GetComponent().lastAbility.Add(this);
StartCoroutine(ApplyBuff());
}
IEnumerator ApplyBuff()
{
startTime = 0f;
while (startTime <= duration)
{
startTime += Time.deltaTime;
if (!isDuffApplied)
{
//startTime = 0;
isDuffApplied = true;
GetComponent().ChangeAttackDamage(bonusDamagePerStack);
GetComponent().ChangeMoveSpeed(bonusMoveSpeedPerStack);
}
yield return null;
}
isDuffApplied = false;
GetComponent().ChangeAttackDamage(-bonusDamagePerStack);
GetComponent().ChangeMoveSpeed((-bonusMoveSpeedPerStack) / (100 + bonusMoveSpeedPerStack) * 100f);
gameObject.GetComponent().lastAbility.Remove(this);
Destroy(this);
}
}
Line 3rd Ability :
Ability Type - Passive
Discription - This ability add Buff on Lina whenever she cast any ability and this buff increase the movement speed and attack speed of this hero.
Extra - This buff stacks upto maximum 3 times, and each time Lina casts any Spell, the whole duration of Buff resets.
Video and Code Snippet are below.
//Called from another ability class, when ability is casted
if (!gameObject.GetComponent())
gameObject.AddComponent();
else
gameObject.GetComponent().StackBuff();
//FierceSoulPassive class
public class FierceSoulPassive : MonoBehaviour
{
public int currentStack = 1;
int maxStack = 3;
float duration = 10f;
float bonusAttackSpeed = 40f;
float bonusMoveSpeed = 5f;
bool isBuffApplied = false;
float startTime;
// Start is called before the first frame update
void Start()
{
StartCoroutine(ApplyBuff());
}
IEnumerator ApplyBuff()
{
startTime = 0f;
while (startTime <= duration * currentStack)
{
startTime += Time.deltaTime;
if (!isBuffApplied)
{
//startTime = 0;
isBuffApplied = true;
GetComponent().ChangeAttackSpeed(bonusAttackSpeed);
GetComponent().ChangeMoveSpeed(bonusMoveSpeed);
}
yield return null;
}
isBuffApplied = false;
GetComponent().ChangeAttackSpeed(-bonusAttackSpeed * currentStack);
if (currentStack > 0)
{
for (int i = 1; i <= currentStack; i++)
{
GetComponent().ChangeMoveSpeed((-bonusMoveSpeed / (100 + bonusMoveSpeed)) * 100);
}
}
Destroy(this);
}
public void StackBuff()
{
currentStack++;
if (currentStack <= maxStack)
{
startTime = 0;
GetComponent().ChangeAttackSpeed(bonusAttackSpeed);
GetComponent().ChangeMoveSpeed(bonusMoveSpeed);
}
else if (currentStack > maxStack)
{
startTime = 0;
currentStack = maxStack;
}
}
}
Github Repo for all abilities from Video :