No creo que haya una forma aceptada de la aplicación de este concepto, pero me gusta mucho compartir cómo me suelen tratar con esto en mis juegos. Es una combinación del patrón de diseño Command y el patrón de diseño Composite .
Tengo una clase base abstracta para acciones que no es más que un contenedor alrededor de un Update
método que se llama cada cuadro, y un Finished
indicador para indicar cuándo la acción ha terminado de ejecutarse.
abstract class Action
{
abstract void Update(float elapsed);
bool Finished;
}
También utilizo el patrón de diseño compuesto para crear un tipo de acciones que sea capaz de alojar y ejecutar otras acciones. Esta también es una clase abstracta. Se reduce a:
abstract class CompositeAction : Action
{
void Add(Action action) { Actions.Add(action); }
List<Action> Actions;
}
Luego tengo dos implementaciones de acciones compuestas, una para ejecución paralela y otra para ejecución secuencial . Pero la belleza es que, dado que el paralelo y la secuencia son acciones en sí mismas, pueden combinarse para crear flujos de ejecución más complejos.
class Parallel : CompositeAction
{
override void Update(float elapsed)
{
Actions.ForEach(a=> a.Update(elapsed));
Actions.RemoveAll(a => a.Finished);
Finished = Actions.Count == 0;
}
}
Y el que gobierna las acciones secuenciales.
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;
}
}
Con esto en su lugar, se trata simplemente de crear implementaciones de acciones concretas y usar las acciones Parallel
y Sequence
para controlar el flujo de ejecución. Terminaré con un ejemplo:
// 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);
He utilizado con éxito este sistema para conducir todo el juego en una aventura gráfica antes, pero probablemente debería funcionar para casi cualquier cosa. También fue lo suficientemente simple como para agregar otros tipos de acciones compuestas, que se utilizaron para crear bucles de ejecución y condicionales.