The problem here is that you’re trying to start a coroutine with the same enumerator each time. The enumerator gets ‘used up’ during the first execution and then becomes useless for starting another coroutine.
void Start()
{
new Script2().Event( MyMethod( 32 ) );
}
Here you are invoking MyMethod and creating a new enumerator. Since you haven’t done anything with it yet, the enumerator it going to point at the very beginning of the method (it isn’t going to execute up to the first yield or anything).
public Script2 Event( IEnumerator method )
{
myGameObject.GetComponent<UI.Button>.onClick.AddListener( delegate() { print("called") ; StartCoroutine( method ) ; } );
return this;
}
Here you’re starting a coroutine with the enumerator created above. Each time the onClick delegate executes, it’s using the same enumerator. And since enumerators are reference types, it can be affected by the previous executions of the delegate.
Remember, internally Unity is basically just calling MoveNext() on the enumerator each frame/step of the coroutine until it returns false, at which point it considers the coroutine finished and stops running it.
So what happens the first time the delegate is called? Unity starts a coroutine using the provided enumerator. That coroutine runs immediately by calling MoveNext() and executing code up to the first yield. This prints your message. Next frame, the coroutine is again executed by calling MoveNext(). This picks up where it left off, right after the yield return null. There’s no more code to executed and there’s an implicit yield break at the end of the method. This type of yield causes MoveNext() to return false, Unity realizes the coroutine is now finished, and the coroutine is removed.
Now, what happens the next time the button is clicked? A new coroutine is started with the same enumerator used before. Where is this enumerator currently pointing? The end of MyMethod still. Unity calls MoveNext(), it returns false, coroutine ends without having executed anything.
How do we fix it? Well, in theory you could Reset() the enumerator before starting the coroutine each time, but that’s actually not implemented with the way Unity does coroutines. So, instead we need to a get a fresh enumerator each time. Instead of passing in an enumerator to Event we need to give Event a way to get a new enumerator each time it wants to start coroutine. So, we’ll pass in a method that returns an enumerator (I’ll use a lambda for brevity). A shorthand for a “delegate with a return type of T” is Func, so we’ll use that.
void Start ()
{
new Script2().Event(() => {
return MyMethod(32);
});
}
public Script2 Event( Func<IEnumerator> method )
{
myGameObject.GetComponent<UI.Button>.onClick.AddListener(() => {
print("called");
StartCoroutine(method());
});
return this;
}
Notice we’re executing method when we start the coroutine in the onClick delegate. This returns a fresh enumerator pointing to the beginning of the MyMethod each time.
Also, there’s a secondary issue that I’m seeing here. You shouldn’t be creating a MonoBehaviour using the new keyword. You should actually be getting a warning from the Console because of this. Instead, create a new GameObject() and AddComponent() to it. Or have a dedicated object for running coroutines on if you need. If we fix this too, you’ll end up with something similar to this:
public class RegisterClickHandlers : MonoBehaviour
{
private void Start ()
{
ClickHandler clickHandler = gameObject.AddComponent<ClickHandler>();
clickHandler.Event(() => {
return MyMethodCoroutine(32);
});
}
private IEnumerator MyMethodCoroutine ( int parameter )
{
print("got= " + parameter);
yield break;
}
}
public class ClickHandler : MonoBehaviour
{
public ClickHandler Event ( Func<IEnumerator> method )
{
GetComponent<UI.Button>.onClick.AddListener(() => {
print("called");
StartCoroutine(method());
};
return this;
}
}
Thanks for you answer but this isn't what I'm trying to do. I don't want it to be called every frame. I want it to be called everytime a button ( 4.6 ) is clicked
– Diablo404