- Home /
problems with damage over time script
Hi, I am trying to create a script that is attached to the player that can be triggered when I want to apply damage over time.
The problem is that it seems to remove a random amount from the player, and also does it in about 2 seconds flat.
Can someone tell me what is wrong with my code please?
using UnityEngine;
using System.Collections;
public class ScripPlayerOnePoisoned : MonoBehaviour {
public float dmgAmount = 40;
bool enable;
ScriptPlayerOneManager script;
// Use this for initialization
void Start ()
{
script = GameObject.Find("PrefabPlayerOne").GetComponent<ScriptPlayerOneManager>();
}
// Update is called once per frame
void Update ()
{
if (script.playerStatus.playerOnePoisoned == true)
{
StartCoroutine(playerOnePoisoned());
playerOnePoisoned();
}
}
public IEnumerator playerOnePoisoned()
{
if (dmgAmount >= 0)
{
script.playerActualAttributes.playerOneActualHealth -= 1;
dmgAmount -=1;
yield return new WaitForSeconds(1);
}
script.playerStatus.playerOnePoisoned = false;
}
}
Answer by NerdRageStudios · Jul 15, 2014 at 06:22 PM
Thank you so much for that!
I have implemented the code as you suggest, but if for example I pass 20 as the damage, it goes to -2000 health in like 5 seconds and doesn't stop decreasing?
using UnityEngine;
using System.Collections;
public class ScriptPlayerOnePoison : MonoBehaviour
{
float dmg = 0;
float dmgDecreaser = 1; // How much the dmg gets decreased by
bool _poisonCoroutineRunning = false; // Ensures ONE coroutine is running.
ScriptPlayerOneManager script;
void Start()
{
script = GameObject.Find("PrefabPlayerOne").GetComponent<ScriptPlayerOneManager>();
}
void Update()
{
CheckPoison();
}
void CheckPoison()
{
if (!_poisonCoroutineRunning && script.playerStatus.playerOnePoisoned) // IF you're poisoned and you aren't calculating any poison damage
{
_poisonCoroutineRunning = true; // You lock yourself into 1 instance of the coroutine
StartCoroutine(UpdatePoison()); // You start the coroutine
}
}
IEnumerator UpdatePoison()
{
while (script.playerStatus.playerOnePoisoned && dmg >= 0) // While poisoned and the damage taken is greater than 0
{
script.playerActualAttributes.playerOneActualHealth -= dmg; // Drops health
dmg -= Time.deltaTime * dmgDecreaser; // Drops damage
yield return new WaitForSeconds(Time.deltaTime); // Waits 1 frame
}// One of the conditions became false if it breaks past here
script.playerStatus.playerOnePoisoned = false;
_poisonCoroutineRunning = false;
yield break;
}
}
Ah, ok I got it working now, but I have another problem now which is different so I shall start a new question ins$$anonymous$$d.
For those that want a D$$anonymous$$G over time script, this is what is now working for me...
using UnityEngine;
using System.Collections;
public class ScriptPlayerOnePoison : $$anonymous$$onoBehaviour
{
public float dmgTimer = 10;
public float dmgAmount = 10;
float dmgDecreaser = 1; // How much the dmg gets decreased by
bool _poisonCoroutineRunning = false; // Ensures ONE coroutine is running.
ScriptPlayerOne$$anonymous$$anager script;
void Start()
{
script = GameObject.Find("PrefabPlayerOne").GetComponent<ScriptPlayerOne$$anonymous$$anager>();
}
void Update()
{
CheckPoison();
}
void CheckPoison()
{
if (!_poisonCoroutineRunning && script.playerStatus.playerOnePoisoned) // IF you're poisoned and you aren't calculating any poison damage
{
_poisonCoroutineRunning = true; // You lock yourself into 1 instance of the coroutine
StartCoroutine(UpdatePoison()); // You start the coroutine
}
}
IEnumerator UpdatePoison()
{
while (script.playerStatus.playerOnePoisoned && dmgTimer >= 0) // While poisoned and the damage taken is greater than 0
{
script.playerActualAttributes.playerOneActualHealth -= dmgAmount; // Drops health
dmgTimer -= dmgDecreaser; // Drops damage
yield return new WaitForSeconds(1); // Waits 1 frame
}// One of the conditions became false if it breaks past here
script.playerStatus.playerOnePoisoned = false;
_poisonCoroutineRunning = false;
yield break;
}
}
Answer by Anti-Social Fred · Jul 15, 2014 at 05:27 PM
I see two main problems.
Multiple instances of coroutines can run. So EVERY FRAME you're telling the damage to decrement by 1 and then wait 1 second. A coroutine shouldn't be used like this.
Wait(X) where X isn't equal to Time.deltaTime can cause problems like this. I suggest:
float dmg = 0; float dmgDecreaser = 1; // How much the dmg gets decreased by bool _poisonCoroutineRunning = false; // Ensures ONE coroutine is running. void Update() { CheckPoison(); } void CheckPoison() { if (!_poisonCoroutineRunning && script.isPoisoned) // IF you're poisoned and you aren't calculating any poison damage { _poisonCoroutineRunning = true; // You lock yourself into 1 instance of the coroutine StartCoroutine(UpdatePoison()); // You start the coroutine } } IEnumerator UpdatePoison() { while (script.isPoisoned && dmg >= 0 ) // While poisoned and the damage taken is greater than 0 { script.actualHealth -= dmg; // Drops health dmg -= Time.deltaTime * dmgDecreaser; // Drops damage yield return new WaitForSeconds(Time.deltaTime); // Waits 1 frame }// One of the conditions became false if it breaks past here script.isPoisoned = false; _poisonCoroutineRunning = false; yield break; }
This method goes along with Vitor_r's suggestion; just substitute your own values and you should be golden!
As long as your instance of the script variable is poisoned this is run, so every frame the health is decremented by dmg * Time.deltaTime. It is almost always advantageous to use Time.deltaTime instead of a constant value.
I prefer this way as well because the coroutine will automatically stop if you pick up a poison cure, for instance, and set the isPoisoned variable to false.
I don't know how your game works, but another thing that I saw just now was that after you decrement the health and wait, you set the isPoisoned to false, every time, regardless of everything. I'm not sure if you want a one-shot poison, but if that's the case use a regular void() method to decrement health, and then use an IEnumerator as a timer to take off poison after X seconds.
Your answer