These will start all of them(c1, c2, c3) simultaneously. But I want c2 to start only after c1. So a chain of tasks forms like c1->c2->c3 such. I will DO NOT want to chain them inside coroutine:
IMHO, I think this way is already fairly elegant. (Not in all ways but some)
if you could get c# async to work then you could use them but it would just get a similar result anyway.
The array gets allocated, though, so it’s not free. I tried some tricks replacing the array with a call to a method with params, but that just increased the allocation, so I don’t think there’s a way to do a generic “run these coroutines in order” method without allocating something. So it’s probably best to stick to just writing them out like you’re already doing.
I like to use behaviour trees for implementing any process that runs over several frames, including task sequences. I’ve made a little scripting framework based on behaviour tree (see Panda BT), it could become an alternative to coroutines for you.
First, you need to implement some “tasks” (e.g: Task1, Task2 and Task3). They are just functions (no coroutines) that get called at the right time by the framework, and will be running on each frame as long as they are doing something:
using UnityEngine;
using Panda;
public class SomeMonoBehaviour : MonoBehaviour
{
[Task]
void Task1()
{
if (Task.current.isStarting)
{
// Use this for initalization
}
//Do some work here on every frame.
if ( /*Some ending condition*/ )
{
Task.current.Complete( /*Either fail (false) or succeed (true)*/ );
}
}
[Task]
void Task2()
{
// ...
}
[Task]
void Task3()
{
// ...
}
}
Then you just define your sequence in a BT script:
sequence
Task1
Task2
Task3
Note that you can do more than just sequences with behaviour trees:
Organizing your tasks into hierarchies
Running tasks under guard conditions
Running tasks in parallel
Conditional branching
Alternative running (fallback to other tasks in case of failure)
Repetitions
…
Trying to implement anything from the above using coroutines, would en up pretty soon into an unmanageable nested-coroutines-hell. Whereas with this framework your process is described in a straight forward little script. Furthermore, the execution of the script can be visualized at runtime, so you can live-inspect at a glance exactly what’s going on (which is valuable for debugging).
We are still on .Net 3.5 (aproximately, it’s really a bastard version of mono. But in most cases it behaves like .Net 3.5). So newer language features like await, async and dynamic don’t work.
There are plans to upgrade at some point in the indefinite future.
Async/await would be a great addition. It would basically render Couroutine as deprecated (which is great imho). Turning a coroutine (for instance the one provided in the coroutine documenation) into an asynchronous method is straight forward:
IEnumerator Fade()
{
for (float f = 1f; f >= 0; f -= 0.1f)
{
Color c = renderer.material.color;
c.a = f;
renderer.material.color = c;
yield return null;
}
}
Using async/await, that coroutine would become:
async Task Fade()
{
for (float f = 1f; f >= 0; f -= 0.1f)
{
Color c = renderer.material.color;
c.a = f;
renderer.material.color = c;
await NextUpdate(); // BTW that won't allocate GC, contrary to yield return new WaitForSomething ...
}
}
I would second behaviour trees as being good for complex, sequential logic. And I’d recommend trying out PandaBehaviour.
It wouldn’t be that simple. Async and Await are both functions for dealing with different threads. So it would require UnityEngine to support multi threading. Which it doesn’t.
Coroutines have the advantage of being asynchronous but on the same thread, in a way that’s not possible with await and async.
At a first glance, It’s easy to assume that async and await are some new multi-threading facilities. But they are not. Asynchronous methods run on the same thread. Of course, you could add multi-threading into the sauce, but that’s not mandatory. Using async/await is very similar to using call-back methods. In fact they have been created as syntactic sugar to decrease usage of callbacks (as well as avoiding usage of thread as a solution to avoid blocking function calls). But behind the curtains the compiler will turn an asynchronous method into a state machine and callbacks without involving new threads.
So, the UnityEngine does not have to be thread-safe to support async and await.
I stand corrected then. They would be useful. Since they can’t be used in Unity (without hoop jumping), I’d never bothered to dig into the details.