Reputation: 2499
I want to make spell damage over time. So here is my code:
public class Spell : MonoBehaviour
{
public float damage = 1.0f;
public bool ignoreCaster = true;
public float delayBeforeCasting = 0.4f;
public float applyEveryNSeconds = 1.0f;
public int applyDamageNTimes = 5;
private bool delied = false;
private int appliedTimes = 0;
void OnTriggerStay(Collider other)
{
IDamageable takeDamage = other.gameObject.GetComponent<IDamageable>();
if(takeDamage != null)
{
StartCoroutine(CastDamage(takeDamage));
}
}
IEnumerator CastDamage(IDamageable damageable)
{
if(!delied)
{
yield return new WaitForSeconds(delayBeforeCasting);
delied = true;
}
while(appliedTimes < applyDamageNTimes)
{
damageable.TakeDamage(damage);
yield return new WaitForSeconds(applyEveryNSeconds);
appliedTimes++;
}
}
}
Problem is where while
starts. I want to check if appliedTimes < applyDamageNTimes
, then if it is true to do damage, wait for delay (applyEveryNSeconds) and then check again but i am not handy with coroutine and for some reason it is not doing that.
Here is working code. Also look for other answers if someone need!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spell : MonoBehaviour
{
public float damage = 1.0f;
public bool ignoreCaster = true;
public float delayBeforeCasting = 0.0f;
public float applyEveryNSeconds = 1.0f;
public int applyDamageNTimes = 1;
private bool delied = false;
private int appliedTimes = 0;
private bool test = false;
void OnTriggerStay(Collider other)
{
IDamageable takeDamage = other.gameObject.GetComponent<IDamageable>();
if(takeDamage != null)
{
StartCoroutine(CastDamage(takeDamage));
}
}
IEnumerator CastDamage(IDamageable damageable)
{
if(!test && appliedTimes <= applyDamageNTimes || !test && applyEveryNSeconds == 0)
{
test = true;
if(!delied)
{
yield return new WaitForSeconds(delayBeforeCasting);
delied = true;
}
else
{
yield return new WaitForSeconds(applyEveryNSeconds);
}
damageable.TakeDamage(damage);
appliedTimes++;
test = false;
}
}
}
Upvotes: 2
Views: 9197
Reputation: 5108
OnTriggerStay is called every frame 2 objects are colliding. This means that an asynchronous instance of CastDamage coroutine is called every frame. So what happens is that you're getting a whole lot of damage per seconds, which simulates not any damage per second, hehe.
So change OnTriggerStay to OnTriggerEnter.
Depending on the kind of spell it is, I'd rather create a DPS script and apply that to the game object so...
And then the MyDpsSpell does damage every N seconds to the Object it's on, and removes itself when it is done:
// Spell.cs
void OnTriggerEnter(Collider col) {
// if you don't want more than 1 dps instance on an object, otherwise remove if
if (col.GetComponent<MyDpsAbility>() == null) {
var dps = col.AddComponent<MyDpsAbility>();
dps.Damage = 10f;
dps.ApplyEveryNSeconds(1);
// and set the rest of the public variables
}
}
// MyDpsAbility.cs
public float Damage { get; set; }
public float Seconds { get; set; }
public float Delay { get; set; }
public float ApplyDamageNTimes { get; set; }
public float ApplyEveryNSeconds { get; set; }
private int appliedTimes = 0;
void Start() {
StartCoroutine(Dps());
}
IEnumerator Dps() {
yield return new WaitForSeconds(Delay);
while(appliedTimes < ApplyDamageNTimes)
{
damageable.TakeDamage(damage);
yield return new WaitForSeconds(ApplyEveryNSeconds);
appliedTimes++;
}
Destroy(this);
}
If it's an aura spell where units take damage every second they're in range you could do something like:
float radius = 10f;
float damage = 10f;
void Start() {
InvokeRepeating("Dps", 1);
}
void Dps() {
// QueryTriggerInteraction.Collide might be needed
Collider[] hitColliders = Physics.OverlapSphere(gameObject.position, radius);
foreach(Collider col in hitColliders) {
col.getComponent<IDamagable>().TakeDamage(10);
}
}
https://docs.unity3d.com/ScriptReference/Physics.OverlapSphere.html
Upvotes: 4
Reputation: 439
Why not create, instead a system that applies auras to in-game characters? For example, if a spell like this adds a damage over time debuff it could instead add the debuff-aura script to the game object and then remove itself once its conditions are met.
Upvotes: 1