In NextOrder()
I am starting a coroutine depending on what i
is ( i
is cycling through 0, 1, 2), and it works fine the first time, but when i
is set back to 0, the coroutine won’t start. The example code has NextOrder()
and the first coroutine in the array. the two print statements in NextOrder()
are displayed correctly, but print(SuppressionFires.Count + " sf orders");
and the equivalent lines in the other coroutines are not.
.
public void NextOrder(int i)
{
print("this is working");
print(orderList*);*
StartCoroutine(orderList*);*
}
public IEnumerator ExecuteSuppressionFires()
{
print(SuppressionFires.Count + " sf orders");
foreach (SuppressionFireInfo order in SuppressionFires)
{
DiceType(order.target, order.initiator);
SuppressionFire(order.initiator, order.targetObject, order.target, order.initiatorLives);
yield return new WaitForSeconds(1);
}
}
Here is a sample code that works fine. Tap the space bar to move i through its values. All coroutines are called OK. Does this help?
using System.Collections;
using UnityEngine;
public class Test3 : MonoBehaviour
{
string[] coro = {"CoRo0", "CoRo1", "CoRo2"};
int i = 9999;
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
i++;
if (i > 2)
i = 0;
StartCoroutine(coro*);*
}
}
IEnumerator CoRo0()
{
print(“CoRo0”);
yield return null;
}
IEnumerator CoRo1()
{
print(“CoRo1”);
yield return null;
}
IEnumerator CoRo2()
{
print(“CoRo2”);
yield return null;
}
}
Coroutines can not be restarted. When you invoke your actual generator method that returns an IEnumerator object, this object that is returned is a statemachine that represents the code in your coroutine. By passing that object to StartCoroutine you tell the Unity coroutine scheduler to store that object internally and to start iterating the statemachine until it reaches its finished state. Once such a coroutine / state machine has finished, it can not be started again. Well you can pass it again to StartCoroutine, but since the state machine is already finished, the coroutine would end immediately.
There are two ways how you can solve this problem:
- Instead of storing the IEnumerator object that is returned by your actual coroutine in that array, you could store a
Func<IEnumerator>
instead.
- The other option is to use IEnumerable instead of IEnumerator for your coroutine and store IEnumerable instances in your List.
For option 1, when you want to add a coroutine to the List / Array, you would pass a lambda expression like this:
orderList.Add(()=>YourCoroutine());
The difference is, we don’t actually invoke the YourCoroutine method here but we store a function that returns an IEnumerator in that list. When you want to start the coroutine, you would do
StartCoroutine( orderList*() );*
Note the parenthesis after the orderList to invoke the delegate we stored which would in turn invoke the coroutine and return a fresh new IEnumerator.
For option2 you would need to change the signature of your coroutine so it returns an IEnumerable instead of IEnumerator. You can store the coroutine like you most likely currently do
orderList.Add(YourCoroutine());
When you want to actually start the coroutine you have to do:
StartCoroutine( orderList*.GetEnumerator() );*
Depending on if you need to pass arguments to the coroutine or not, the second solution is usually the cleaner solution as it does not potentially capture any outside variables like a closure could do.
For more information on coroutines, IEnumerator and IEnumerable, see [my coroutine crash course][1].
_*[1]: https://github.com/Bunny83/Unity-Articles/blob/master/CoroutineCrashCourse.md*_