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ół Update
metody, 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ć Parallel
iSequence
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.