- Home /
Pass delegate to coroutine as a variable
Hi guys,
I want to be able to add various behaviours to my player from various triggers. I figured the best way to do this would be through the use of delegates. I want to be able to externally trigger adding a delegate to the player for a duration. For Example:
Player walks on reverse direction item
Reverse direction item gets a reference to the player and adds a "ReverseDirection()" method to the OnMovement delegate
After a duration the reverse direction effect wears off
I would imagine being able to do something like that like this:
public delegate void PlayerBehaviour();
public MissileBehaviour onMovementBehaviour;
public void InvertMovement()
{
moveDir *= -1;
}
public void AddBehaviour(PlayerBehaviour behaviour, System.Action method, float duration = 0f)
{
StartCoroutine(AddBehaviourEnumerator(behaviour, method, duration));
}
IEnumerator AddBehaviourEnumerator(ref System.Action behaviour, System.Action method, float duration)
{
behaviour += method;
if(duration == 0f) return;
yield return new WaitForSeconds(duration);
behaviour -= method;
}
and then I could add the behaviour externally like so
Player p = playerObject.GetComponent<Player>();
p.AddBehaviour(p.onMovementBehaviour, p.InvertMovement, 5f);
but the issue is that I can't pass a ref to a coroutine so I'm not quite sure how to pass the delegate as a variable to make this work.
Any help would be appreciated. Thanks!
Answer by Bunny83 · Apr 26, 2017 at 10:29 AM
That's completely impossible. ref parameters actually use pointers to the actual memory location where the variable is stored. That's why you can not "store" the content of a ref parameter in another variable for later use. It's only valid within the current chain of execution.
A coroutine is not a method but a class instance which you create when you invoke the coroutine. The coroutine scheduler of Unity handles the scheduling of the coroutine when necessary.
The parameters to a coroutine / iterator function are translated into member variables of the hidden auto generated state machine class that the compiler generates out of your coroutine / iterator. However since we can't "store" ref parameters in variables, ref parameters are not allowed at all in iterator functions / coroutines.
What you can do, however, is handling the adding / subtracting in a closure:
IEnumerator AddBehaviourEnumerator(System.Action aBefore, System.Action aAfter, float duration)
{
aBefore();
if(duration == 0f) return;
yield return new WaitForSeconds(duration);
aAfter();
}
And call it like this:
p.AddBehaviour(()=>p.onMovementBehaviour+= p.InvertMovement,()=>p.onMovementBehaviour-= p.InvertMovement , 5f);
Closures work completely different. They may create a closure class that will house the actual variable. Every bit of code that accesses the variable will be changed to use the closure class instance. Since this is just an ordinary class, it can be stored inside the closure and allows access to the variable.
Another way is to create a wrapper object for your delegate that includes the coroutine method:
[System.Serializable]
public class DelegateWrapper
{
public System.Action onDelegate;
public IEnumerator AddBehaviour(System.Action aCallback, float aDuration)
{
onDelegate += aCallback;
if (aDuration == 0f) yield break;
yield return new WaitForSeconds(aDuration);
onDelegate -= aCallback;
}
}
So when declaring your event you would use the wrapper instead:
public DelegateWrapper onYourEvent;
You could implement the + and - operators in the wrapper so you could use the wrapper almost like a normal delegate.
This looks amazing, exactly what I was looking for! Thank you! (I'll mark as accepted once I've implemented it).
Your answer
Follow this Question
Related Questions
How to get a variable out of a coroutine? 3 Answers
Start a Coroutine with a delegate that has a parameter 1 Answer
How to use a float value from coroutine 1 and use in coroutine 2? 1 Answer
How Return Or Restart A Coroutine When A Variable Increase? 0 Answers
How Return Or Restart A Coroutine When A Variable Increase? 1 Answer