Nie sądzę, aby istniał jeden zaakceptowany sposób wdrożenia tej koncepcji, ale naprawdę chciałbym podzielić się tym , jak zwykle sobie z tym radzę w moich grach. To trochę kombinacja polecenia wzorca projektowego i wzorca projektowego Composite .
Mam abstrakcyjną klasę bazową dla akcji, która jest niczym więcej niż opakowaniem wokół Updatemetody, która jest wywoływana dla każdej ramki, iFinished flagą wskazującą, kiedy akcja się zakończyła.
abstract class Action
{
abstract void Update(float elapsed);
bool Finished;
}
Używam również wzorca projektowania złożonego do tworzenia rodzaju akcji, które są w stanie hostować i wykonywać inne akcje. To także jest klasa abstrakcyjna. Sprowadza się do:
abstract class CompositeAction : Action
{
void Add(Action action) { Actions.Add(action); }
List<Action> Actions;
}
Następnie mam dwie implementacje akcji złożonych, jedną do wykonywania równoległego, a drugą do wykonywania sekwencyjnego . Ale piękno polega na tym, że ponieważ równoległość i sekwencja są działaniami, można je łączyć , aby tworzyć bardziej złożone przepływy wykonania.
class Parallel : CompositeAction
{
override void Update(float elapsed)
{
Actions.ForEach(a=> a.Update(elapsed));
Actions.RemoveAll(a => a.Finished);
Finished = Actions.Count == 0;
}
}
I ten, który rządzi sekwencyjnymi działaniami.
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;
}
}
W tym miejscu wystarczy po prostu stworzyć konkretne implementacje akcji i użyć ParalleliSequence do kontrolowania przepływu wykonania. Zakończę przykładem:
// 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);
Z powodzeniem używałem tego systemu do prowadzenia całej rozgrywki w graficznej przygodzie, ale prawdopodobnie powinien on działać praktycznie na wszystko. Łatwo było również dodać inne typy akcji złożonych, które były używane do tworzenia pętli wykonawczych i warunkowych.