- Home /
how to restart coroutine on event?
I have a non-MonoBehavior class with some background functions that are supposed to wait for some time, then make a minor operation. A single component has several instances of that class, so I can't call invokeRepeating from the component (cannot access the individual instances' functions) nor the class itself (not a MonoBehaviour). Coroutines seem to be the most appropriate.
However, I need to be able to 'restart' the coroutine on a specific event, i.e. reset its delays. Any hints on how to do this from the outside or passively? Or can I only do this by explicitly checking the state every Update cycle?
In specific, my objects have several layers of health. Each layer can regenerate a full point of health every few seconds but if a layer receives damage, it looses the progress on regenerating the current new health point and must wait for a penalty delay before beginning the regeneration process anew.
// start after receiving damage
function regenerate () {
// wait for damage penalty being done xor cancel and restart when receiving damage
yield WaitForSeconds(attributes.regenDamageDelay); // <- need hook here
while (true) {
// wait a cycle xor cancel and restart when receiving damage
yield WaitForSeconds(attributes.regenSecondsPerTick); // <- need hook here
currentHealth++;
}
}
Answer by Golan2781 · Dec 30, 2012 at 11:30 AM
Okay, I got this working now with a mixture of the two: a coroutine carrying a timestamp of its own invocation and the parent class carrying a timestamp of the last invocation of its coroutine.
When the function gets invoked, it will set both the class timestamp and its own timestamp to be the same. Before each action, it will check whether the two are identical, i.e. if in the meantime another one was invoked. For robustness, the timestamp doesn't store the actual time but the order of invocation.
The stamp is big enough for 1000 invocations every second for about 1.5 months...
class healthTest {
var id : String;
var health : int;
var invokeStamp : uint;
var timeInitial : float = 1;
var timeRepeat : float = 2;
function classCoroutine ( ) {
invokeStamp++; // store in the class when last this routine was invoked
var initStamp : uint = invokeStamp; // store in the routine when this specific routine instance was invoked
Debug.Log('started coroutine');
yield WaitForSeconds(timeInitial);
if (invokeStamp!=initStamp) return; // <- exit when not most current coroutine
Debug.Log('finished initial delay @ ' + health + ' for ' + id);
while ( true ) {
yield WaitForSeconds(timeRepeat);
if (invokeStamp!=initStamp) return; // <- exit when not most current coroutine
health++; // <- do stuff when still alive
Debug.Log('finished repeat delay @ ' + health + ' for ' + id);
}
}
}
Note that this specific example is bound to a parent class. The example class is not derived from MonoBehaviour so it cannot invoke the coroutine, this is done by the parent class.
// testComponent.js
var regenDummy1 : healthTest;
var regenDummy2 : healthTest;
function Start () {
regenDummy1.id='regenDummy1';
regenDummy2.id='regenDummy2';
regenDummy2.timeRepeat*=2;
StartCoroutine(regenDummy1.classCoroutine());
StartCoroutine(regenDummy2.classCoroutine());
}
function OnCollisionEnter(collision : Collision) {
// 're'start the coroutines
StartCoroutine(regenDummy1.classCoroutine());
StartCoroutine(regenDummy2.classCoroutine());
}
Answer by save · Dec 30, 2012 at 12:21 AM
If I understand you correctly you want to be able to break the cycle at any point. Using a condition for when the part receives damage and current elapsed time will let you do this. Set a boolean variable to true (called receivedDamage
in example) when the part is taking damage, then yield one frame before you call Regenerate()
.
private var receivedDamage : boolean = false;
function Regenerate () {
receivedDamage = false;
//Do a time stamp
var t : float = Time.time;
//Use the time stamp as a condition
while (Time.time<t+attributes.regenDamageDelay) {
//Use another condition for when receiving damage to exit loop
if (receivedDamage) return;
yield;
}
//Set the condition to repeat while currentHealth is less than maxHealth and doesn't receive damage
while (!receivedDamage && currentHealth<maxHealth) {
//Do another time stamp
t = Time.time;
while (Time.time<t+attributes.regenSecondsPerTick) {
if (receivedDamage) return;
yield;
}
currentHealth++;
yield;
}
}
Yes, that's the 'long' version of what I want to do. I was hoping to get it done without the need to do all the timestamping and constant loops, coroutines make for much cleaner code - not to mention better performance as far as I know.
Still, if it's not possible to be done with coroutines, I think your answer is the way to go.
Your answer
Follow this Question
Related Questions
Yield Wait for Seconds (A little help here) 2 Answers
Need to call yield TWICE ??? (ANSWERED) 3 Answers
C# Wait for Coroutine 3 Answers
How to set timer for WWW helper? 1 Answer
Execution manner of co routine 2 Answers