Cómo construir un JRPG Un manual para desarrolladores de juegos

Este artículo es una descripción general de alto nivel para crear un JRPG (juego de rol japonés) como los primeros juegos de Final Fantasy. Veremos la arquitectura y los sistemas que conforman el esqueleto de un JRPG, cómo administrar los modos de juego, cómo usar los mapas de mapas para mostrar el mundo y cómo codificar un sistema de combate RPG..

Nota: Este artículo está escrito utilizando un lenguaje de pseudo código similar a Java, pero los conceptos son aplicables a cualquier entorno de desarrollo de juegos.


Contenido

  • El lugar de nacimiento poco probable de JRPGs
  • La charla de genero
  • Cinco razones por las que debes hacer un JRPG
  • Arquitectura
  • Manejando el estado del juego
  • Mapas
  • Combate
  • revisión


El lugar de nacimiento poco probable de JRPGs

El limo - uno de los enemigos icónicos de Dragon Warrior.

En 1983, Yuji Horii, Koichi Nakamura y Yukinobu Chida volaron a Estados Unidos y asistieron a AppleFest '83, una reunión de desarrolladores que muestra sus últimas creaciones para Apple II. Quedaron impresionados por la última versión de un juego de rol llamado Wizardry.

Al regresar a Japón, decidieron crear Dragon Warrior, un juego de rol que era similar pero simplificado para la NES. Fue un éxito masivo, definiendo el género JRPG. A Dragon Warrior no le fue tan bien en Estados Unidos, pero unos años más tarde, otro juego lo hizo..

En 1987, se lanzó Final Fantasy original, generando una de las franquicias de videojuegos más vendidas de la Tierra, que se convirtió, al menos en Occidente, en el icónico JRPG..



La charla de genero

Los géneros de juego nunca se definen con precisión, son más bien una colección difusa de convenciones. Los juegos de rol tienden a tener un sistema de nivelación, uno o varios personajes de jugador con habilidades y estadísticas, armas y armaduras, modos de combate y exploración y narrativas sólidas; el progreso del juego a menudo se logra avanzando a través de un mapa.

Los juegos de rol japoneses son juegos de rol creados en el molde de Dragon Warrior; son más lineales, el combate a menudo se basa en turnos y generalmente hay dos tipos de mapas: un mapa del mundo y un mapa local. Los JRPG arquetípicos incluyen Dragon Warrior, Final Fantasy, Wild Arms, Phantasy Star y Chrono Trigger. El tipo de JRPG del que hablaremos en este artículo es similar al de Final Fantasy..




Cinco razones por las que debes hacer un JRPG

1. Han pasado la prueba del tiempo.

Los juegos como Final Fantasy VI y Chrono Trigger son todavía muy divertidos de jugar. Si creas un JRPG, estás aprendiendo un formato de juego atemporal al que los jugadores modernos siguen siendo muy receptivos. Hacen un gran marco para agregar su propio toque y experimento, ya sea en la narrativa, la presentación o la mecánica. Es una gran cosa si puedes hacer un juego que aún se juegue y disfrutes décadas después de su primer lanzamiento.!

2. La mecánica del juego es ampliamente aplicable

Call of Duty, uno de los juegos FPS más populares del mundo, utiliza elementos de RPG; el auge del juego social que rodea a FarmVille fue básicamente un clon del RPG de SNES Harvest Moon; e incluso los juegos de carreras como Gran Turismo tienen niveles y experiencia..

3. Las restricciones fomentan la creatividad

Al igual que un escritor puede sentirse intimidado por una hoja de papel en blanco, un desarrollador de juegos puede verse paralizado por la gran cantidad de opciones posibles al diseñar un juego nuevo. Con un JRPG, se han decidido muchas opciones para usted, por lo que no tiene esa parálisis, es libre de seguir las convenciones para la mayoría de las decisiones y desviarse de la convención en los puntos que le interesan..

4. Es factible como un proyecto en solitario

Final Fantasy fue casi totalmente codificado por un solo programador, Nasir Gebelli, ¡y lo estaba haciendo en ensamblaje! Con herramientas e idiomas modernos es mucho más fácil crear este tipo de juego. La mayor parte de la mayoría de los juegos de rol no es la programación, es el contenido, pero este no tiene que ser el caso para tu juego. Si vuelve a marcar un poco el contenido y se enfoca en la calidad sobre la cantidad, un JRPG es un gran proyecto en solitario..

Tener un equipo puede ayudar con cualquier juego, y es posible que desee subcontratar el arte y la música, o usar algunos de los excelentes activos creativos de bienes comunes de lugares como opengameart.org. (Nota del editor: nuestro sitio hermano GraphicRiver también vende hojas de sprites).

5. Con fines de lucro!

Los JRPG tienen seguidores dedicados y varios JRPG independientes (como los que se muestran a continuación) han tenido un buen desempeño comercial y están disponibles en plataformas como Steam.



Arquitectura


Los JRPG comparten tantas convenciones y mecánicas que es posible dividir un JRPG típico en varios sistemas:

En el desarrollo de software, un patrón se ve una y otra vez: estratificación. Esto se refiere a cómo los sistemas de un programa se construyen uno encima del otro, con capas ampliamente aplicables en la parte inferior y capas que tratan el problema más cercano cerca de la parte superior. Los JRPG no son diferentes y se pueden ver como varias capas: las capas inferiores se ocupan de funciones gráficas básicas y las capas superiores se ocupan de misiones y estadísticas de personajes.

Propina: Al desarrollar un nuevo sistema, es mejor comenzar por crear primero las capas inferiores y luego moverlas capa por capa hacia la parte superior. El uso de middleware le ayuda a omitir varias de las capas inferiores comunes a muchos juegos. En el diagrama de arquitectura anterior, todas las capas debajo de la línea de puntos se manejan mediante un motor de juego 2D.

Como puede ver en el diagrama de arquitectura anterior, hay muchos sistemas que conforman un JRPG, pero la mayoría de los sistemas se pueden agrupar en grupos separados. modos del juego. Los JRPG tienen modos de juego muy distintos; tienen un mapa del mundo, mapa local, modo de combate y varios modos de menú. Estos modos son piezas de código autocontenidas casi por completo, lo que hace que cada una sea simple de desarrollar..

Los modos son importantes pero serían inútiles sin el contenido del juego. Un RPG contiene muchos archivos de mapas, definiciones de monstruos, líneas de diálogo, secuencias de comandos para ejecutar escenas de corte y código de juego para controlar el progreso del jugador. Cubrir cómo construir un JRPG en detalle llenaría todo un libro, así que nos concentraremos en algunas de las partes más importantes. El manejo limpio de los modos de juego es fundamental para producir un JRPG manejable, por lo que ese es el primer sistema que exploraremos.



Manejando el estado del juego


La imagen de abajo muestra el bucle del juego bombeando, llamando a una función de actualización cada fotograma. Este es el latido del juego y casi todos los juegos están estructurados de esta manera.

¿Alguna vez has iniciado un proyecto pero se ha estancado porque te resultaba demasiado difícil agregar nuevas funciones o te encontraban plagado de errores misteriosos? Tal vez intentaste meter todo tu código en la función de actualización con poca estructura y encontraste que el código se convirtió en un desorden secreto. Una solución excelente para este tipo de problema es separar el código en diferentes estados del juego, dando una visión mucho más clara de lo que está sucediendo.

Una herramienta de juego común es la máquina estatal; se utiliza en todas partes, para el manejo de animaciones, menús, flujo de juegos, IA ... es una herramienta esencial para tener en nuestro kit. Para el JRPG podemos usar una máquina de estados para manejar los diferentes modos de juego. Echaremos un vistazo a una máquina de estado normal y luego la mezclaremos un poco, para hacerla más adecuada para el JRPG. Pero primero tomemos un poco de tiempo para considerar el flujo general del juego que se muestra a continuación..

En un JRPG típico, es probable que comiences en el modo de juego de mapas locales, que puedas pasear por una ciudad e interactuar con sus habitantes. Desde la ciudad puedes irte, aquí entrarás en un modo de juego diferente y verás el mapa del mundo..

El mapa del mundo se parece mucho al mapa local pero a una escala mayor; Puedes ver montañas y pueblos, en lugar de árboles y cercas. Mientras esté en el mapa mundial, si regresa a la ciudad, el modo volverá al mapa local..

Ya sea en el mapa del mundo o en el mapa local, puedes abrir un menú para ver a tus personajes y, a veces, en el mapa del mundo serás lanzado al combate. El diagrama de arriba describe estos modos de juego y transiciones; Este es el flujo básico del juego JRPG y es lo que crearemos nuestros estados de juego a partir de.

Manejo de la complejidad con una máquina de estado

Una máquina de estado, para nuestros propósitos, es un fragmento de código que contiene todos los diversos modos de nuestros juegos, que nos permite movernos de un modo a otro, y que se actualiza y representa cualquiera que sea el modo actual..

Dependiendo del lenguaje de implementación, una máquina de estado generalmente consiste en un Máquina estatal clase y una interfaz, Yo declaro, que todos los estados implementan.

Propina: Una interfaz es solo una clase con definiciones de funciones miembro pero sin implementación. Las clases que heredan de una interfaz son necesarias para implementar sus funciones miembro. Esto significa que una interfaz no tiene código, solo especifica que otras clases proporcionan cierta funcionalidad. Esto permite que se usen diferentes clases de la misma manera porque sabemos que tienen un grupo de funciones miembro definidas por una interfaz común. Artículos Relacionados
  • Introducción a la programación orientada a objetos para el desarrollo de juegos

Una máquina de estados se describe mejor al esbozar un sistema básico en pseudocódigo:

clase StateMachine Mapa mStates = nuevo mapa(); IState mCurrentState = EmptyState; Public void Update (float elapsedTime) mCurrentState.Update (elapsedTime);  public void Render () mCurrentState.Render ();  Public void Change (String stateName, opcional var params) mCurrentState.OnExit (); mCurrentState = mStates [stateName]; mCurrentState.OnEnter (params);  Public void Add (nombre de cadena, estado IState) mStates [nombre] = estado; 

Este código anterior muestra una máquina de estado simple sin comprobación de errores.

Veamos cómo se usa el código de máquina del estado anterior en un juego. Al comienzo del juego un Máquina estatal se creará, se agregarán todos los diferentes estados del juego y se establecerá el estado inicial. Cada estado se identifica de forma única por un Cuerda Nombre que se utiliza al llamar a la función de cambio de estado. Solo hay un estado actual, mCurrentState, y se representa y actualiza cada bucle de juego.

El código podría verse así:

StateMachine gGameMode = new StateMachine (); // Un estado para cada modo de juego gGameMode.Add ("mainmenu", nuevo MainMenuState (gGameMode)); gGameMode.Add ("localmap", nuevo LocalMapState (gGameMode)); gGameMode.Add ("mapamundi", nuevo WorldMapState (gGameMode)); gGameMode.Add ("battle", nuevo BattleState (gGameMode)); gGameMode.Add ("ingamemenu", nuevo InGameMenuState (gGameMode)); gGameMode.Change ("mainmenu"); // Main Game Update Loop public void Update () float elapsedTime = GetElapsedFrameTime (); gGameMode.Update (elapsedTime); gGameMode.Render (); 

En el ejemplo, creamos todos los estados requeridos, los agregamos a la Máquina estatal y establecer el estado de inicio en el menú principal. Si ejecutamos este código el MainMenuState Sería renderizado y actualizado primero. Esto representa el menú que ves en la mayoría de los juegos cuando arrancas por primera vez, con opciones como Empezar juego y Juego de carga.

Cuando un usuario selecciona Empezar juego, la MainMenuState llama algo como gGameMode.Change ("localmap", "map_001") y el LocalMapState se convierte en el nuevo estado actual. Este estado luego actualizaría y renderizaría el mapa, permitiendo al jugador comenzar a explorar el juego.

El siguiente diagrama muestra una visualización de una máquina de estados que se mueve entre los WorldMapState y BattleState. En un juego, esto sería equivalente a un jugador deambulando por el mundo, siendo atacado por monstruos, entrando en modo de combate y luego regresando al mapa..

Vamos a echar un vistazo rápido a la interfaz de estado y una Estado vacío clase que lo implementa:

Interfaz pública IState Actualización pública virtual vacía (float elapsedTime); vacío virtual público Render (); vacío virtual público OnEnter (); vacío público virtual OnExit ();  public EmptyState: IState Public void Update (float elapsedTime) // No hay nada que actualizar en el estado vacío.  public void Render () // No hay nada que mostrar en el estado vacío public void OnEnter () // No se debe realizar ninguna acción cuando se ingresa el estado public void OnExit () // No se debe realizar ninguna acción cuando el estado se sale

La interfaz Yo declaro requiere que cada estado tenga cuatro métodos antes de que se pueda usar como estado en la máquina de estados: Actualizar(), Hacer(), OnEnter () y OnExit ().

Actualizar() y Hacer() se denominan cada cuadro para el estado activo actual; OnEnter () y OnExit () Se llaman al cambiar de estado. Aparte de eso, todo es bastante sencillo. Ahora que sabe esto, puede crear todo tipo de estados para todas las diferentes partes de su juego.

Esa es la máquina de estado básica. Es útil para muchas situaciones, pero cuando se trata de modos de juego, podemos mejorarlo. Con el sistema actual, el cambio de estado puede tener muchos gastos generales, a veces cuando se cambia a un BattleState vamos a querer dejar el Estado del mundo, correr la batalla, y luego volver a la Estado del mundo En la configuración exacta fue antes de la batalla. Este tipo de operación puede ser torpe utilizando la máquina de estado estándar que hemos descrito. Una mejor solución sería utilizar un apilar de estados.

Haciendo la lógica del juego más fácil con una pila de estados

Podemos convertir la máquina de estados estándar en una pila de estados, como se muestra en el diagrama a continuación. Por ejemplo, el MainMenuState Se empuja en la pila primero, al comienzo del juego. Cuando comenzamos un nuevo juego, el LocalMapState se empuja encima de eso. En este punto el MainMenuState ya no está renderizado o actualizado, pero está esperando, listo para que volvamos a.

A continuación, si empezamos una batalla, el BattleState se empuja en la parte superior; cuando termina la batalla, se extrae de la pila y podemos reanudar en el mapa exactamente donde lo dejamos. Si morimos en el juego entonces. LocalMapState Se salta y volvemos a MainMenuState.

El siguiente diagrama muestra la visualización de una pila de estados, que muestra la InGameMenuState siendo empujado en la pila y luego salido.

Ahora que tenemos una idea de cómo funciona la pila, veamos algo de código para implementarlo:

clase pública StateStack Mapa mStates = nuevo mapa(); Lista mStack = Lista(); Public void Update (float elapsedTime) IState top = mStack.Top () top.Update (elapsedTime) public void Render () IState top = mStack.Top () top.Render () public void Push (nombre de la cadena) Estado del estado = mStates [nombre]; mStack.Push (estado);  public IState Pop () return mStack.Pop (); 

Este código de pila de estado anterior no tiene comprobación de errores y es bastante sencillo. Los estados pueden ser empujados a la pila usando el Empujar() llama y salio con un Popular() llame, y el estado en la parte superior de la pila es el que se actualiza y representa.

El uso de un enfoque basado en la pila es bueno para los menús, y con una pequeña modificación también se puede utilizar para cuadros de diálogo y notificaciones. Si te sientes aventurero, puedes combinar ambos y tener una máquina de estados que también admita pilas.

Utilizando Máquina estatal, StateStack, o alguna combinación de los dos crea una estructura excelente para construir tu RPG sobre.

Acciones siguientes:

  1. Implemente el código de máquina del estado en su lenguaje de programación favorito.
  2. Crear un MenuMenuState y GameState heredando de Yo declaro.
  3. Establecer el estado del menú principal como estado inicial..
  4. Haz que ambos estados rendericen diferentes imágenes..
  5. Al presionar un botón, haga que el estado cambie del menú principal al estado del juego.


Mapas


Los mapas describen el mundo; desiertos, naves espaciales y selvas pueden representarse usando un tilemap. Un tilemap es una forma de usar un número limitado de imágenes pequeñas para construir una más grande. El siguiente diagrama muestra cómo funciona:

El diagrama anterior tiene tres partes: la paleta de mosaicos, una visualización de cómo se construye el mapa de páginas y el mapa final representado en la pantalla.

La paleta de mosaicos es una colección de todos los mosaicos utilizados para crear un mapa. Cada mosaico en la paleta se identifica de forma única por un entero. Por ejemplo, el azulejo número 1 es hierba; Observe los lugares donde se usa en la visualización de tilemap..

Un tilemap es solo una matriz de números, cada número relacionado con un mosaico en la paleta. Si quisiéramos hacer un mapa lleno de hierba, podríamos tener una gran variedad llena con el número 1, y cuando renderizamos esos mosaicos veríamos un mapa de hierba formado por muchos mosaicos pequeños de hierba. La paleta de mosaicos generalmente se carga como una textura grande que contiene muchos mosaicos más pequeños, pero cada entrada en la paleta podría ser fácilmente su propio archivo gráfico.

Propina: ¿Por qué no usar una matriz de matrices para representar el tilemap? La primera matriz podría representar por una matriz de filas de azulejos.

La razón por la que no hacemos esto es solo por simplicidad y eficiencia. Si tienes una matriz de enteros, eso es un bloque continuo de memoria. Si tiene una matriz de matrices, entonces ese es un bloque de memoria para la primera matriz que contiene punteros, con cada puntero apuntando a una fila de mosaicos. Esta indirección puede ralentizar las cosas, y como estamos dibujando el mapa en cada cuadro, cuanto más rápido mejor!

Veamos algunos códigos para describir un mapa de mosaicos:

// // Toma un mapa de textura de múltiples mosaicos y lo divide en // imágenes individuales de 32 x 32. // El arreglo final se verá así: // gTilePalette [1] = Imagen // Nuestro primer mosaico de césped // gTilePalette [2] = Imagen // Segunda variante del azulejo de césped //… // gTilePalette [15] = Imagen // Azulejo de roca y hierba // Array gTilePalette = SliceTexture ("grass_tiles.png", 32, 32) gMap1Width = 10 gMap1Height = 10 Array gMap1Layer1 = new Array () [2, 2, 7, 3, 11, 11, 11, 12, 2, 1, 1, 10, 11, 11, 4, 11, 12, 2, 2, 2, 1, 13, 5, 11, 11, 11, 4, 8, 2, 1, 2, 1, 10, 11, 11, 11, 11, 11, 9, 10, 11, 12, 13, 5, 11, 11, 11, 11, 4, 13, 14, 15, 1, 10, 11, 11, 11, 11, 2, 2, 2, 2, 13, 14, 11, 11, 11, 11, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, 2, 2, 2, 2, 2, 5, 11, 11, 11, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,];

Compare el código anterior con el diagrama y es bastante claro cómo se construye un tilemap a partir de una pequeña serie de mosaicos. Una vez que se describe un mapa como este, podemos escribir una función de renderización simple para dibujarlo en la pantalla. Los detalles exactos de la función cambiarán según la configuración de la ventana gráfica y las funciones de dibujo. Nuestra función de renderización se muestra a continuación..

static int TilePixelSize = 32; // Dibuja un tilemap desde la parte superior izquierda, en la posición del píxel x, y // x, y - la posición del píxel en que se representará el mapa desde // mapa - el mapa para representar // ancho - el ancho del mapa en mosaicos public void RenderMap (int x, int y, Array map, int mapWidth) // Comience por indexar la parte superior izquierda del mosaico int tileColumn = 1; int tileRow = 1; para (int i = 1; map.Count (); i ++) // Minus 1 para que el primer mosaico se dibuje en 0, 0 int pixelPosX = x + (tileColumn - 1) * TilePixelSize; int pixelPosY = y + (tileRow - 1) * TilePixelSize; RenderImage (x, y, gTilePalette [gMap1Layer1 [i]]); // Avanzar al siguiente azulejo tileColumn + = 1; if (tileColumn> mapWidth) tileColumn = 1; tileRow + = 1;  - Cómo se usa en el ciclo de actualización principal public void Update () // En realidad, dibuje un mapa en la pantalla RenderMap (0, 0, gMap1Layer1, gMap1Width)

El mapa que hemos usado hasta ahora es bastante básico; la mayoría de los JRPGs usarán varias capas de mapas en línea para crear escenas más interesantes. El siguiente diagrama muestra nuestro primer mapa, con tres capas más agregadas, lo que resulta en un mapa mucho más agradable.

Como vimos anteriormente, cada tilemap es solo una matriz de números y, por lo tanto, se puede hacer un mapa de capas completo a partir de una matriz de esas matrices. Por supuesto, renderizar el mapa de mapas es solo el primer paso para agregar exploración a tu juego; los mapas también deben tener información sobre colisiones, soporte para mover entidades y una interactividad básica usando desencadena.

Un disparador es un fragmento de código que solo se activa cuando el jugador lo "activa" realizando alguna acción. Hay muchas acciones que un disparador puede reconocer. Por ejemplo, mover el personaje del jugador a una ficha puede desencadenar una acción; esto ocurre comúnmente cuando se mueve hacia una puerta, un teletransportador o la ficha del borde del mapa. Se pueden colocar disparadores en estos mosaicos para teletransportar al personaje a un mapa interior, mapa mundial o mapa local relacionado.

Otro disparador puede depender de que se presione el botón "usar". Por ejemplo, si el jugador sube a un cartel y presiona "usar", se dispara un disparador y se muestra un cuadro de diálogo que muestra el texto del cartel. Los disparadores se utilizan en todo el lugar para ayudar a unir los mapas y proporcionar interactividad.

Los JRPG a menudo tienen muchos mapas bastante detallados y complicados, por lo que te recomiendo que no intentes hacerlos manualmente, es una mejor idea usar un editor de mapas de mapas. Puede utilizar una de las excelentes soluciones gratuitas existentes o rodar las suyas propias. Si desea probar una herramienta existente, definitivamente recomiendo revisar Tiled, que es la herramienta que utilicé para crear estos mapas de ejemplo..

Artículos Relacionados
  • Introducción a Tiled
  • Análisis de mapas en formato TMX en mosaico en tu propio motor de juego
  • Conociendo a Ogmo Editor

Acciones siguientes:

  1. Conseguir en mosaico.
  2. Consigue algunas fichas de opengameart.org.
  3. Crea un mapa y cárgalo en tu juego.
  4. Añadir un personaje de jugador.
  5. Mueve el personaje de azulejo a azulejo.
  6. Haz que el personaje se mueva suavemente de azulejo a azulejo.
  7. Agregar detección de colisión (puede usar una nueva capa para almacenar información de colisión).
  8. Agrega un disparador simple para intercambiar mapas.
  9. Agregue un disparador para leer signos: considere usar la pila de estado de la que hablamos anteriormente para mostrar el cuadro de diálogo.
  10. Cree un estado del menú principal con la opción "Iniciar juego" y un estado de mapa local y vincúlelas.
  11. Diseñe algunos mapas, agregue algunos NPC, intente una búsqueda sencilla: deje correr su imaginación!


Combate

¡Finalmente, a la lucha! ¿De qué sirve un JRPG sin combate? El combate es donde muchos juegos eligen innovar, introduciendo nuevos sistemas de habilidades, nuevas estructuras de combate o diferentes sistemas de hechizos: hay muchas variaciones.

La mayoría de los sistemas de combate usan una estructura por turnos con un solo combatiente permitido para realizar una acción a la vez. Los primeros sistemas de batalla basados ​​en turnos eran simples, y cada entidad tenía un turno en orden: el turno del jugador, el turno del enemigo, el turno del jugador, el turno del enemigo, etc. Esto rápidamente dio paso a sistemas más intrincados que ofrecen más margen de maniobra para tácticas y estrategias..

Vamos a echar un vistazo de cerca a Tiempo activo sistemas de combate basados ​​en, donde los combatientes no necesariamente obtienen todos un número igual de turnos. Las entidades más rápidas pueden obtener más turnos y el tipo de acción realizada también afecta la duración de un turno. Por ejemplo, un guerrero que corta con una daga puede tardar 20 segundos, pero un mago que convoca a un monstruo puede demorar dos minutos..


La captura de pantalla de arriba muestra el modo de combate en un JRPG típico. Los personajes controlados por el jugador están a la derecha, los personajes enemigos a la izquierda y un cuadro de texto en la parte inferior muestra información sobre los combatientes..

Al comienzo del combate, los monstruos y los sprites de los jugadores se agregan a la escena y luego hay una decisión sobre el orden en que las entidades toman su turno. Esta decisión puede depender parcialmente de cómo se lanzó el combate: si el jugador fue emboscado, los monstruos atacarán primero; de lo contrario, generalmente se basa en una de las estadísticas de la entidad, como la velocidad.

Todo lo que el jugador o los monstruos hacen es una acción: atacar es una acción, usar magia es una acción, ¡incluso decidir qué acción tomar a continuación es una acción! El orden de las acciones se rastrea mejor utilizando una cola. La acción en la parte superior es la acción que tendrá lugar a continuación, a menos que ninguna acción más rápida lo sustituya. Cada acción tendrá una cuenta regresiva que disminuye a medida que pasa cada cuadro..

El flujo de combate se controla mediante una máquina de estados con dos estados; un estado para marcar las acciones y otro estado para ejecutar la acción superior cuando llegue el momento. Como siempre, la mejor manera de entender algo es mirar el código. El siguiente ejemplo implementa un estado de combate básico con una cola de acción:

clase BattleState: IState Lista mActions = List(); Lista mEntities = Lista(); StateMachine mBattleStates = new StateMachine (); public static bool SortByTime (Acción a, Acción b) return a.TimeRemaining ()> b.TimeRemaining () public BattleState () mBattleStates.Add ("tick", nuevo BattleTick (mBattleStates, mActions)); mBattleStates.Add ("execute", nuevo BattleExecute (mBattleStates, mActions));  Public void OnEnter (var params) mBattleStates.Change ("tick"); // // Obtener una acción de decisión para cada entidad en la cola de acción // La ordenación para que las acciones más rápidas sean las // mEntities = params.entities superiores; foreach (Entidad e en mEntidades) if (e.playerControlled) PlayerDecide action = new PlayerDecide (e, e.Speed ​​()); mActions.Add (acción);  else AIDecide action = new AIDecide (e, e.Speed ​​()); mActions.Add (acción);  Ordenar (mActions, BattleState :: SortByTime);  Public void Update (float elapsedTime) mBattleStates.Update (elapsedTime);  public void Render () // Dibuja la escena, gui, personajes, animaciones, etc. mBattleState.Render ();  Public void OnExit () 

El código anterior demuestra el control del flujo del modo de batalla usando una máquina de estado simple y una cola de acciones. Para empezar, todas las entidades involucradas en la batalla tienen un decidir-acción añadido a la cola.

Una acción de decisión para el jugador abrirá un menú con las opciones estables de RPG Ataque, mágico, y ít; una vez que el jugador decide una acción, la acción de decisión se elimina de la cola y se agrega la acción recién elegida.

Una acción de decisión para la IA inspeccionará la escena y decidirá qué hacer a continuación (utilizando algo como un árbol de comportamiento, árbol de decisiones o técnica similar) y luego también eliminará su acción de decisión y agregará su nueva acción a la cola..

los BattleTick La clase controla la actualización de las acciones, como se muestra a continuación:

clase BattleTick: IState StateMachine mStateMachine; Lista mAcciones; BattleTick público (StateMachine stateMachine, List acciones): mStateMachine (stateMachine), mActions (action)  // Es posible que ocurran cosas en estas funciones, pero no nos interesa nada. public void OnEnter ()  public void OnExit ()  public void Render ()   Actualización de void público (float elapsedTime) foreach (Acción a en mActions) a.Update (elapsedTime);  if (mActions.Top (). IsReady ()) Action top = mActions.Pop (); mStateMachine: Change ("execute", top); 

BattleTick es un estado secundario del estado BattleMode y solo marca hasta que la cuenta regresiva de la acción superior es cero. Luego saca la acción superior de la cola y cambia a la ejecutar estado.


El diagrama de arriba muestra una cola de acción al comienzo de una batalla. Nadie ha tomado una acción todavía y todos están ordenados por su tiempo para tomar una decisión..

La planta gigante tiene una cuenta regresiva de 0, por lo que en el siguiente tick ejecuta su AIDecide acción. En este caso el AIDecide La acción hace que el monstruo decida atacar. La acción de ataque es casi inmediata y se agrega nuevamente a la cola como la segunda acción.

En la siguiente iteración de BattleTick, el jugador podrá elegir qué acción debe realizar su enana "Marca", lo que cambiará la cola nuevamente. La siguiente iteración de BattleTick después de eso, la Planta atacará a uno de los enanos. La acción de ataque se eliminará de la cola y se pasará a la BattleExecute Estado, y animará a la planta a atacar, así como a hacer todos los cálculos de combate necesarios..

Una vez finalizado el ataque del monstruo, otro. AIDecide La acción se agregará a la cola del monstruo. los BattleState Continuará de esta manera hasta el final del combate..

Si algu