- Home /
Adding a coroutine to an event?
Is it possible to add a coroutine to an event? I'm experimenting with events, and it seems like it would be extremely useful. However, I've got to run a coroutine for one of the functions that will be assigned to the event. Don't know if I'm explaining this correctly so here's some code:
Event declaration:
public delegate void BookSelectEventHandler();
public static event BookSelectEventHandler BookSelect;
public static void OnBookSelect()
{
BookSelectEventHandler bookSelect = BookSelect;
if (bookSelect != null)
bookSelect();
}
Basically what I want to do (but not a solution):
BookSelect += StartCoroutine(RenderWholeBook);
My function:
IEnumerator RenderWholeBook()
{
//snap
yield return null; //return next frame
//change text
yield return null; //return next frame
//snap next page
yield return null; //return next frame
//turn off
yield break;
}
Is this even a viable approach? Or should I just do the long work around of reference and call the function?
Also thought about this:
public delegate void BookSelectEventHandler();
public static event BookSelectEventHandler BookSelect;
public static void OnBookSelect()
{
BookSelectEventHandler bookSelect = BookSelect;
if (bookSelect != null)
StartCoroutine(bookSelect());
}
But it doesn't work either.
Answer by SirGive · Mar 07, 2012 at 07:35 PM
Surprising simple:
public delegate IEnumerator BookSelectEventHandler();
public static event BookSelectEventHandler BookSelect;
public void OnBookSelect()
{
BookSelectEventHandler bookSelect = BookSelect;
if (bookSelect != null)
StartCoroutine(bookSelect());
}
And you can only add functions with the return type of IEnumerator:
Only the last event handler to be added will be called -- Luiz
only use one, so it works for me*
And this is how you would add the function to the event:
BookSelect += RenderWholeBook;
Updated my answer. Everything works perfectly.
Can you remove that "everything works perfectly" part? Only the last event handler added runs, so it's far from perfect...
Answer by luizgpa · Mar 07, 2012 at 07:41 PM
I'm not sure what your intentions are, but you can't start a directly coroutine from a static method since StartCoroutine is a member method from MonoBehaviour. Another problem is that the event handlers must be a coroutine, so they must return IEnumerator.
public delegate IEnumerator BookSelectEventHandler();
public static event BookSelectEventHandler BookSelect;
public void OnBookSelect()
{
if (BookSelect != null)
StartCoroutine(BookSelect());
}
static IEnumerator RenderWholeBook()
{
//snap
yield return null; //return next frame
//change text
yield return null; //return next frame
//snap next page
yield return null; //return next frame
//turn off
yield break;
}
(...)
BookSelect += RenderWholeBook;
However there's a problem: only the last handler is called.
So the last function added would be the only one to ever be called? That would defeat the purpose of using events wouldn't it?
Answer by Syganek · Aug 17, 2017 at 10:48 AM
I know it's few years since question was asked but I had similiar problem.
My solution was to simply invoke every delegate manually.
public delegate IEnumerator PlannedActionsEventHandler();
private PlannedActionsEventHandler _plannedActions;
public void RegisterAction(PlannedActionsEventHandler plannedAction)
{
_plannedActions += plannedAction;
}
private IEnumerator InvokeActions()
{
if (_plannedActions != null)
{
//Invokes all planned actions and waits until they're done.
foreach (var @delegate in _plannedActions.GetInvocationList())
{
yield return @delegate.DynamicInvoke();
}
}
}
Please let me know if they're any problems with this method.
This is the only correct answer in this thread. Alternatively even simpler:
public event Func<IEnumerator> coroutineOnEvents;
OnEnable()
{
coroutineOnEvents += DoStuff;
coroutineOnEvents += DoStuff;
}
private IEnumerator InvokeActions()
{
if (coroutineOnEvents != null)
{
//This will play DoStuff twice
foreach (var @delegate in coroutineOnEvents.GetInvocationList())
{
yield return @delegate.DynamicInvoke();
}
}
}
private IEnumerator DoStuff()
{
yield return null;
}
Answer by Berenger · Mar 07, 2012 at 07:30 PM
Your delegate needs to return an IEnumerator, as well as BookSelect's function, which is probably assigned from elsewhere. Then, you call StartCoroutine(bookSelect());
Answer by $$anonymous$$ · Nov 23, 2013 at 12:12 AM
You can get past this with one more level of functionality.I actually hit the same problem //delegate public delegate void BookSelectEventHandler(); //event public event BookSelectEventHandler BookSelect; //Trigger Event Function public void OnBookSelect() { if (BookSelect != null) BookSelect(); }
//The function we want to run as a coroutine
IEnumerator RenderWholeBook()
{
//snap
yield return null; //return next frame
//change text
yield return null; //return next frame
//snap next page
yield return null; //return next frame
//turn off
yield break;
}
//Helper function that gets registered on the event and //calls the actual function we want to run as a coroutine //without waiting for it.
public void HelperFunction(){
StartCoroutine(RenderWholeBook);
}
(...)
//In a Start/Awake/OnEnable/WhateverEventRegistering code //you use
BookSelect += HelperFunction;
This way when you register listeners to the BookSelect event they won't be IEnumerators and won't stumble on the restriction of just using the last listener/handler whatever you want to call it.
The problem is when you want to wait for the coroutine to finish....if you do then you can't use a helper function because you'd need to yield return the RenderWholeBook which means you have to declare whatever function is going to call the "yield return ...." piece of code.Which is kind of a chain reaction towards the parent function. Calling StartCoroutine creates the coroutine and exits.Which means that if you need info about this you have to pass them through arguments.