Я не думаю, що існує один прийнятий спосіб реалізації цієї концепції, але я дуже хотів би поділитися тим, як я зазвичай маю справу з цим у своїх іграх. Це трохи поєднання шаблону дизайну Command та композиційного шаблону дизайну.
У мене є абстрактний базовий клас для дій, який є не що інше, як обгортку навколо Update
методу, який називається кожен кадр, і Finished
прапор, який вказує, коли дія закінчиться.
abstract class Action
{
abstract void Update(float elapsed);
bool Finished;
}
Я також використовую композитний шаблон дизайну, щоб створити тип дій, який здатний приймати та виконувати інші дії. Це теж абстрактний клас. Зводиться до:
abstract class CompositeAction : Action
{
void Add(Action action) { Actions.Add(action); }
List<Action> Actions;
}
Тоді у мене є дві реалізації складених дій, одна для паралельного виконання та одна для послідовного виконання . Але краса полягає в тому, що оскільки паралельність і послідовність є самими діями, їх можна поєднувати , щоб створити більш складні потоки виконання.
class Parallel : CompositeAction
{
override void Update(float elapsed)
{
Actions.ForEach(a=> a.Update(elapsed));
Actions.RemoveAll(a => a.Finished);
Finished = Actions.Count == 0;
}
}
І той, що керує послідовними діями.
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;
}
}
З цим на місці просто питання створення конкретних реалізацій дій та використання операцій Parallel
та Sequence
дій для управління потоком виконання. Я закінчу прикладом:
// 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);
Раніше я успішно використовував цю систему для керування всім ігровим процесом у графічній пригоді, але це, мабуть, мало б працювати майже все. Також було досить просто додати інші типи складених дій, які використовувались для створення циклів виконання та умовних умов.