The Basic Framework
Here's my solution to this common problem. First let's choose a name for an action that spans several frames, such as an animation. I'll call it a Process
which is the terminlogy I found in this book, but don't mistake it with the processes on your OS, it's a different concept. In this context, a process is anything that needs to be updated, possibly several times, before it completes.
I'll start by defining a base class for our process. Something as simple as this is enough (I kept the name as Process for consistency with the discussion above, but since .NET already has a class named like that, you might want to give it another name):
public abstract class Process
{
public abstract void Update(float elapsed);
public bool Finished { get; set; }
}
Now let's create a class that serves as a process queue. You add processes to this queue, and they get executed in turn. This is where my implementation differs from the one in the book above. In the book every process is updated every frame, but in this case I'll make them sequential instead, so that the second process only starts when the first one ends. Once again it's a simple class:
public class ProcessQueue
{
public void Add(Process process)
{
_processQueue.Add(process);
}
public void Update(float elapsed)
{
if (_processQueue.Count > 0)
{
_processQueue[0].Update(elapsed);
if(_processQueue[0].Finished)
{
_processQueue.RemoveAt(0);
}
}
}
private readonly List<Process> _processQueue = new List<Process>();
}
And that's it, although you're free to add more methods, for example to clear the queue or to know how many processes are currently queued. Now just make your Animation
class inherit from Process
and chain them like:
// On startup
var processQueue = new ProcessQueue();
// Chain a few animations
processQueue.Add(new Animation(...));
processQueue.Add(new Animation(...));
processQueue.Add(new Animation(...));
// On Update
processQueue.Update(elapsed);
Note that you can create and chain all the processes you want before the game starts, because they only really start being executed when you call update for the first time on the queue.
When implementing the Animation
class inheriting from Process
make sure to set the Finished
property to true when the animation ends, so that it will get removed from the queue.
I think that probably already answers your question, but if you're interested in the subject, keep reading.
Bonus - Some Useful Processes!
I might as well drop some useful types of processes for pretty much any type of game! Hopefully these will inspire you to make your own processes too.
First we have the SleepProcess
which as the name implies, does nothing for a certain duration of time:
public class SleepProcess : Process
{
public SleepProcess(float duration)
{
_duration = duration;
}
public override void Update(float elapsed)
{
_timer += elapsed;
if(_timer >= _duration)
Finished = true;
}
private float _timer;
private readonly float _duration;
}
And sometimes you don't need a process that spans several frames - a single frame is enough. The following GenericProcess
makes that easy:
public class GenericProcess : Process
{
public GenericProcess(Action action)
{
_action = action;
}
public override void Update(float elapsed)
{
_action();
Finished = true;
}
private readonly Action _action;
}
Mixing Everything Together - An Example
Here's how you can use these processes. Let's say for instance that you would like to play animation A, wait 2 seconds, play animation B, and finally write a message to the debugger. By using the processes above you can implement this entire sequence as four lines of code:
processQueue.Add(new AnimationProcess(...));
processQueue.Add(new SleepProcess(2));
processQueue.Add(new AnimationProcess(...));
processQueue.Add(new GenericProcess(() => Diagnostics.Debug("Message")));
And the only thing you need to do is remember to call Update
on the process queue. Can you see how powerful this concept can be when properly used? Pretty much everything in your game can be considered as a process!
What About Looping?
What if I wanted to take the example above and make it loop infinitely? Well, this too, is extremely simple to implement with this framework, using a sort of "delayed recursive" call. Just put the processes you want to loop inside a function, and then add a GenericProcess
at the end that adds that calls that function once more:
void LoopProcesses()
{
processQueue.Add(new AnimationProcess(...));
processQueue.Add(new SleepProcess(2));
processQueue.Add(new AnimationProcess(...));
processQueue.Add(new GenericProcess(() => Diagnostics.Debug("Message")));
// Call LoopProcesses again after all of the above are finished
processQueue.Add(new GenericProcess(LoopProcesses));
}
// Call once to start looping
LoopProcesses();