Eu não acho que há uma maneira aceitável de implementar este conceito, mas eu realmente gostaria de compartilhar como eu geralmente lidar com isso em meus jogos. É um pouco de uma combinação do padrão de design Command e do padrão Composite .
Eu tenho uma classe base abstrata para ações que nada mais é do que um invólucro em torno de um Update
método chamado cada quadro e um Finished
sinalizador para indicar quando a ação terminou a execução.
abstract class Action
{
abstract void Update(float elapsed);
bool Finished;
}
Também uso o padrão de design composto para criar um tipo de ação capaz de hospedar e executar outras ações. Esta também é uma classe abstrata. Tudo se resume a:
abstract class CompositeAction : Action
{
void Add(Action action) { Actions.Add(action); }
List<Action> Actions;
}
Então, eu tenho duas implementações de ações compostas, uma para execução paralela e outra para execução seqüencial . Mas o mais interessante é que, como paralelo e sequência são ações, elas podem ser combinadas para criar fluxos de execução mais complexos.
class Parallel : CompositeAction
{
override void Update(float elapsed)
{
Actions.ForEach(a=> a.Update(elapsed));
Actions.RemoveAll(a => a.Finished);
Finished = Actions.Count == 0;
}
}
E aquele que governa ações seqüenciais.
class Sequence : CompositeAction
{
override void Update(float elapsed)
{
if (Actions.Count > 0)
{
Actions[0].Update(elapsed);
if (Actions[0].Finished)
Actions.RemoveAt(0);
}
Finished = Actions.Count == 0;
}
}
Com isso, é simplesmente uma questão de criar implementações de ações concretas e usar as ações Parallel
e Sequence
para controlar o fluxo de execução. Vou terminar com um exemplo:
// Create a parallel action to work as an action manager
Parallel actionManager = new Parallel();
// Send character1 to destination
Sequence actionGroup1 = new Sequence();
actionGroup1.Add(new MoveAction(character1, destination));
actionGroup1.Add(new TalkAction(character1, "Arrived at destination!"));
actionManager.Add(actionGroup1);
// Make character2 use a potion on himself
Sequence actionGroup2 = new Sequence();
actionGroup2.Add(new RemoveItemAction(character2, ItemType.Potion));
actionGroup2.Add(new SetHealthAction(character2, character2.MaxHealth));
actionGroup2.Add(new TalkAction(character2, "I feel better now!"));
actionManager.Add(actionGroup2);
// Every frame update the action manager
actionManager.Update(elapsed);
Eu usei com sucesso esse sistema para conduzir toda a jogabilidade em uma aventura gráfica antes, mas provavelmente deve funcionar para praticamente qualquer coisa. Também foi simples o suficiente para adicionar outros tipos de ações compostas, que foram usadas para criar loops e condicionais de execução.