¿Qué estás buscando?
Hero background image
Utiliza el patrón de comando para sistemas de juego flexibles y extensibles.
Esta página se ha traducido automáticamente. Para ver la versión original para comprobar su exactitud y como fuente confiable

Implementar patrones de diseño comunes en la programación de juegos en tu proyecto de Unity puede ayudarte a construir y mantener de manera eficiente una base de código limpia, organizada y legible. Los patrones de diseño reducen el tiempo de refactorización y pruebas, acelerando los procesos de desarrollo y contribuyendo a una base sólida para hacer crecer tu juego, equipo y negocio.

Piensa en los patrones de diseño no como soluciones terminadas que puedes copiar y pegar en tu código, sino como herramientas adicionales que pueden ayudarte a construir aplicaciones más grandes y escalables.

Esta página explica el patrón de diseño de comando.

El contenido aquí se basa en el libro electrónico gratuito, Nivel up tu código con patrones de programación de juegos.

Consulta más artículos en la serie de patrones de diseño de programación de juegos de Unity en el hub de mejores prácticas de Unity o a través de estos enlaces:

Comprende el patrón de comandos

El patrón de diseño de programación de comandos es uno de los originales de la Pandilla de Cuatro, y es útil siempre que desees rastrear una serie específica de acciones. Es probable que hayas visto el patrón de comando en acción si has jugado a un juego que utiliza la funcionalidad de deshacer/rehacer o mantiene tu historial de entradas en una lista. Imagina un juego de estrategia donde el usuario puede planificar varios turnos antes de ejecutarlos realmente. Ese es el patrón de comando.

El patrón de comando permite que las acciones se representen como objetos. Encapsular acciones como objetos te permite crear un sistema flexible y extensible para controlar el comportamiento de los GameObjects en respuesta a la entrada del usuario. Esto funciona encapsulando una o más llamadas a métodos como un "objeto de comando" en lugar de invocar un método directamente. Entonces puedes almacenar estos objetos de comando en una colección, como una cola o una pila, que funciona como un pequeño búfer.

Almacenar objetos de comando de esta manera te permite controlar el momento de su ejecución al retrasar potencialmente una serie de acciones para su reproducción posterior. De manera similar, puedes rehacer o deshacerlos y añadir flexibilidad adicional para controlar la ejecución de cada objeto de comando.

Aquí hay algunas aplicaciones comunes del patrón en diferentes géneros de juegos:

  • En un juego de estrategia en tiempo real, el patrón de comando podría usarse para poner en cola acciones de unidades y edificios. El juego ejecutaría cada comando a medida que los recursos estuvieran disponibles.
  • En un juego de estrategia por turnos, el jugador podría seleccionar una unidad y luego almacenar sus movimientos o acciones en una cola u otra colección. Al final del turno, el juego ejecutaría todos los comandos en la cola del jugador.
  • En un juego de rompecabezas, el patrón de comandos podría permitir al jugador deshacer y rehacer acciones.
  • En un juego de lucha, leer las pulsaciones de botones o los movimientos del gamepad en una lista de comandos específica podría resultar en combos y movimientos especiales.
Patrón de comando en un proyecto de muestra
Patrón de comando en un proyecto de muestra

Prueba el proyecto de muestra en GitHub que demuestra diferentes patrones de diseño de programación en el contexto del desarrollo de juegos, incluido el patrón de comando.

En esta muestra, el jugador puede moverse por un laberinto haciendo clic en los botones del lado izquierdo. A medida que tu jugador se mueve, puedes ver un rastro de movimiento. Pero lo más importante es que puedes deshacer y rehacer tus acciones anteriores.

Para encontrar la escena correspondiente en el proyecto, ve a la carpeta llamada "9 Command."

El objeto de comando y el invocador de comando

Para implementar el patrón de comando, necesitarás un objeto general que contenga tu acción. Este objeto de comando contendrá la lógica a realizar y cómo deshacerla.

Hay varias formas de implementar esto, pero aquí hay una versión simple utilizando una interfaz llamada ICommand:

interfaz pública ICommand
{
void Ejecutar();
void Deshacer();
}

En este caso, cada acción de juego aplicará la interfaz ICommand (también podrías implementar esto con una clase abstracta).

Cada objeto de comando será responsable de sus propios Ejecutar y Deshacer métodos. Así que añadir más comandos a tu juego no afectará a ninguno de los existentes.

La CommandInvoker clase es entonces responsable de ejecutar y deshacer comandos. Además de los ExecuteCommand y UndoCommand métodos, tiene una pila de deshacer para mantener la secuencia de objetos de comando.

Ejemplo: Movimiento deshacer

En el proyecto de muestra, puedes mover a tu jugador por un pequeño laberinto. Una opción simple para cambiar la posición del jugador es crear un PlayerMover.

Para hacer esto, necesitarás pasar un Vector3 al método Move para guiar al jugador a lo largo de las cuatro direcciones de la brújula. También puedes usar un raycast para detectar las paredes en la LayerMask apropiada. Por supuesto, implementar lo que quieres aplicar al patrón de comando es independiente del patrón en sí.

El ComandoMover
EL INVOCADOR DE COMANDOS, ICOMMAND Y MOVECOMMAND
El ComandoMover

Para seguir el patrón de comando, captura el PlayerMover’s Move método como un objeto. En lugar de llamar a Move directamente, crea una nueva clase, MoveCommand, que implemente la interfaz ICommand.

comando de movimiento de clase pública : ICommand
{
MoverJugador playerMover;
Vector3 movimiento;
comandoMover(P jugador, Vector3 vectorMovimiento)
{
this.playerMover = player;
este.movimiento = moverVector;
}
public void Ejecutar()
{
jugadorMover.Mover(movimiento);
}
público vacío Deshacer()
{
jugadorMover.Mover(-movimiento);
}
}

Cualquier lógica que quieras lograr va aquí, así que invoca Move con el vector de movimiento.

ICommand también necesita un Deshacer método para restaurar la escena a su estado anterior. En este caso, la Deshacer lógica resta el vector de movimiento, empujando esencialmente al jugador en la dirección opuesta.

El MoveCommand almacena cualquier parámetro que necesite para ejecutarse. Configura estos con un constructor. En este caso, guardas el componente apropiado PlayerMover y el vector de movimiento.

Una vez que crees el objeto de comando y guardes sus parámetros necesarios, utiliza el InvocadorDeComandos estático EjecutarComando y DeshacerComando métodos para pasar tu ComandoDeMovimiento. Esto ejecuta el MoveCommand’s Execute o Undo y rastrea el objeto de comando en la pila de deshacer.

El InputManager no llama directamente al método Move del PlayerMover. En su lugar, añade un método extra, RunMoveCommand, para crear un nuevo MoveCommand y enviarlo al CommandInvoker.

Luego, configura los diversos eventos onClick de los botones de la interfaz de usuario para llamar a RunPlayerCommand con los cuatro vectores de movimiento.

Consulta el proyecto de muestra para detalles de implementación del InputManager. También puedes configurar tu propia entrada utilizando el teclado o el gamepad. Tu jugador ahora puede navegar por el laberinto. Haz clic en el botón Deshacer para que puedas retroceder al cuadrado de inicio.

Ventajas y desventajas

Implementar la rejugabilidad o la posibilidad de deshacer es tan simple como generar una colección de objetos de comando. También puedes usar el búfer de comandos para reproducir acciones en secuencia con controles específicos.

Por ejemplo, piensa en un juego de lucha donde una serie de clics de botones específicos desencadena un movimiento o ataque combinado. Almacenar las acciones de los jugadores con el patrón de comando hace que configurar estos combos sea mucho más sencillo.

Por otro lado, el patrón de comando introduce más estructura, al igual que los otros patrones de diseño. Tendrás que decidir dónde estas clases e interfaces adicionales proporcionan suficiente beneficio para implementar objetos de comando en tu aplicación.

Una vez que aprendas lo básico, puedes afectar el tiempo de los comandos y reproducirlos en sucesión o en reversa, dependiendo del contexto.

Considera lo siguiente al incorporar el patrón de comando:

  • Crear más comandos: El proyecto de muestra solo incluye un tipo de objeto de comando, el MoveCommand. Puedes crear cualquier número de objetos de comando que implementen ICommand y rastrearlos utilizando el CommandInvoker.
  • Agregar funcionalidad de rehacer es una cuestión de agregar otra pila: Cuando deshaces un objeto de comando, colócalo en una pila separada que rastree las operaciones de rehacer. De esta manera, puedes recorrer rápidamente el historial de deshacer o rehacer esas acciones. Limpia la pila de rehacer cuando el usuario invoca un movimiento completamente nuevo (puedes encontrar una implementación en el proyecto de muestra).
  • Utiliza una colección diferente para tu búfer de objetos de comando: Una cola podría ser más útil si deseas un comportamiento de primero en entrar, primero en salir (FIFO). Si usas una lista, sigue el índice activo actual; los comandos antes del índice activo son deshacibles. Los comandos después del índice son rehacibles.
  • Limitar el tamaño de las pilas: Las operaciones de deshacer y rehacer pueden salirse de control rápidamente. Limitar las pilas al menor número de comandos.
  • Pasa cualquier parámetro necesario al constructor: Esto ayuda a encapsular la lógica como se ve en el ejemplo de MoveCommand.
  • El CommandInvoker, al igual que otros objetos externos, no ve el funcionamiento interno del objeto de comando, solo invoca Ejecutar o Deshacer. Proporcione al objeto de comando los datos necesarios para funcionar al llamar al constructor.
Libro electrónico azul
Más recursos

Encuentra más consejos sobre cómo utilizar patrones de diseño en tus aplicaciones de Unity, así como los principios SOLID, en el libro electrónico gratuito Mejora tu código con patrones de programación de juegos.

Puedes encontrar todos los libros electrónicos y artículos técnicos avanzados de Unity en el mejores prácticas hub. Los libros electrónicos también están disponibles en la página de prácticas avanzadas en la documentación.

¿Te gustó este contenido?