¡Hola! Mi nombre es Vladimir Popov y soy un desarrollador de cliente en el proyecto War Robots.
War Robots ha existido durante varios años: durante este tiempo, han aparecido docenas de nuevos mechs en el juego. Y, por supuesto, ninguno de ellos sería único sin su propio conjunto de habilidades.
Te contaré en este artículo cómo funciona el sistema de habilidades de nuestro juego y cómo evolucionó, de forma sencilla y sin ningún detalle técnico.
Primero, profundicemos en la historia y veamos la implementación anterior; ahora ya no se usa en el proyecto.
Las antiguas habilidades eran muy triviales: tenían un componente que estaba colgado del robot. Era una construcción monolítica en la que el programador describía completamente cómo funciona la habilidad: su flujo, cómo y con qué interactúa. Toda la lógica se describe dentro de un componente, que el diseñador del juego podría simplemente colgar del robot y ajustar los parámetros. No había forma de cambiar las habilidades de flujo: los diseñadores de juegos solo podían cambiar los parámetros y los tiempos.
La antigua habilidad solo podía existir en dos estados: activo e inactivo. A cada estado se le podría asignar su propia acción.
Considere el ejemplo de la habilidad Jammer. En un tiempo, por ejemplo, fue el robot Stalker. Trabajó de la siguiente manera:
- Si la habilidad está activa, se reproduce la animación y el robot entra en el estado Jammer. En este estado, no se puede apuntar al robot.
- Si la habilidad está inactiva, no pasa nada.
- Cuando intentas activar la habilidad, se comprueba si han pasado más de n segundos desde la última activación.
- La desactivación se produce automáticamente después de m segundos.
Durante mucho tiempo, esta funcionalidad fue suficiente para nosotros. Pero con el tiempo, todo cambió: tanto los diseñadores como los programadores de juegos ya no estaban satisfechos con este enfoque. Era difícil para los programadores mantener tales habilidades, porque el código se volvió monstruoso, con una cadena de herencia muy larga, donde cada situación tenía que describirse. Los diseñadores de juegos carecían de flexibilidad en el sistema. Es decir, por el bien de cualquier cambio en una habilidad, tenían que ordenar la revisión a los programadores, incluso si existía exactamente la misma funcionalidad en la habilidad vecina.
Entonces nos dimos cuenta de que necesitábamos cambiar algo. Y desarrollaron un nuevo sistema. En él, cada habilidad comenzó a representarse como un conjunto de varios objetos relacionados. La función se dividió en estados, habilidades y componentes de estados.
¿Cómo funciona?
Cualquier habilidad tiene un maestro . Este es su objeto central. Conecta el resto de los objetos de habilidad con el mundo exterior y viceversa. Y también toma todas las decisiones principales. Puede haber varios
estados . En esencia, el estado aquí no es muy diferente del estado "activo" / "inactivo" en la versión anterior. Pero ahora puede haber muchos de ellos, y su propósito se ha vuelto más abstracto. Solo un estado puede estar activo a la vez.
La principal innovación sobre el sistema antiguo fueron los componentes . El componente describe algún tipo de acción. Cada estado puede tener cualquier número de componentes.
¿Cómo funcionan las nuevas habilidades?
La habilidad solo puede estar en uno de los estados a la vez. El maestro se dedica a cambiarlos. Los componentes que se vinculan al estado reaccionan a la activación / desactivación del estado y, dependiendo de esto, pueden comenzar a realizar una acción o dejar de realizarla.
Todos los objetos ahora son personalizables. El diseñador del juego puede mezclar estados y componentes entre sí de cualquier manera y así obtener una nueva habilidad de los bloques preinstalados. Los programadores ahora solo son necesarios para crear un nuevo componente o estado, lo que facilita enormemente la escritura de código. Ahora trabajan con entidades pequeñas, describen algunos elementos simples y no ensamblan la habilidad ellos mismos; los diseñadores de juegos han comenzado a hacer esto.
El flujo se volvió así:
- El maestro activa el primer estado;
- ;
- ;
- ;
- ;
- ;
- .
Posteriormente, este procedimiento se repite una y otra vez. Para facilitar su uso, un estado no es solo un contenedor de componentes, sino que también determina cuándo cambiar a otro estado y le pide al maestro que cambie.
Con el tiempo, esto se volvió insuficiente para nosotros y el esquema de la habilidad se transformó en la siguiente forma:
Maestro, estado y componentes permanecieron en sus lugares, pero se les agregaron nuevos elementos.
Lo primero que llama la atención es que hemos agregado condiciones a cada estado y componente. Para los estados, definen requisitos adicionales para salir del estado. Para los componentes, determinan si el componente puede realizar su acción.
Un contenedor de cargos (cargos) contiene cargos, los recarga, detiene la recarga cuando es necesario y proporciona cargos a los estados por su uso.
El temporizador se utiliza cuando varios estados deben tener un tiempo de ejecución común, pero su propio tiempo de ejecución no está definido.
Es importante tener en cuenta que todos los objetos de habilidad son opcionales. Técnicamente, solo el maestro y un estado son suficientes para la capacidad de trabajar.
No hay tantas habilidades que se ensamblen completamente sin la participación de los programadores, pero el desarrollo en general se ha vuelto notablemente más barato, porque los programadores ahora escriben cosas muy pequeñas: por ejemplo, un nuevo estado o dos componentes, el resto se reutiliza.
Resumamos qué partes constituyentes de las habilidades que tenemos y cuáles son:
- El maestro actúa como una máquina de estados. Proporciona a los estados y componentes información sobre el mundo y el mundo: información sobre una capacidad. El maestro sirve como enlace entre los estados, componentes y partes de servicio de una habilidad: cargos y temporizadores externos.
- El estado escucha los comandos de activación y desactivación del maestro y, en consecuencia, activa y desactiva los componentes, y también le pide al maestro que cambie a otro estado. El estado decide cuándo debe cambiar al siguiente. Para hacer esto, usa su condición interna: si el jugador hizo clic en el botón de habilidad, si ha pasado un cierto tiempo desde que se activó el estado, etc., y las condiciones externas vinculadas al estado.
- : . : , , .
- , , . . , . , . — , .
- Un contenedor de cargos contiene cargos, los recarga, deja de recargar cuando es necesario y otorga cargos a los estados. Se usa en habilidades de carga múltiple cuando necesitas darle al jugador la oportunidad de usarlo varias veces, pero no más de n veces seguidas.
- El temporizador se utiliza cuando varios estados tienen una duración común, pero no se sabe cuánto tiempo es válido cada uno de ellos. Cualquier estado puede iniciar un temporizador durante n segundos. Todos los estados interesados se suscriben al evento sobre el final del temporizador y hacen algo cuando finaliza.
Ahora volvamos al diagrama de habilidades. ¿Cómo empezó a actuar?
- Al comienzo del juego, el maestro elige el primer estado y lo activa;
- El estado activa todos sus componentes;
- ;
- ;
- , ;
- ;
- .
Los estados pueden utilizar los cargos como una condición de transición adicional. Si se produce tal transición, el número de cargas disminuye. Además, los estados pueden usar un temporizador común. En este caso, el tiempo total de su ejecución será determinado por el temporizador, y cada estado individualmente puede durar cualquier tiempo.
No se nos ocurrió nada completamente nuevo para la interfaz de usuario. Así se arregla con nosotros.
El maestro tiene su propia interfaz de usuario. Define algunos elementos que siempre deben estar en la interfaz de usuario y no dependen del estado que esté activo actualmente.
Cada estadohay un par en la interfaz de usuario. La IU de estado se muestra solo cuando su estado está activo. Recibe datos sobre su estado y puede mostrarlos de una forma u otra. Por ejemplo, los estados de duración suelen tener una barra y un texto en su interfaz de usuario que representan el tiempo restante.
En el caso de que el estado esté esperando un comando externo para continuar con la capacidad, su interfaz de usuario muestra un botón. Y presionando envía el comando al estado.
Ahora veamos el trabajo de las habilidades usando ejemplos específicos. Comencemos con un robot llamado Inquisidor.
Tenemos cuatro estados que cambian uno tras otro. Por encima de los estados, puede ver su visualización en la interfaz de usuario. En dos de ellos puedes ver los componentes que se refieren a ellos. Los otros dos estados simplemente no tienen componentes.
Flujo de trabajo de habilidades:
- WaitForClick. .
- , . WaitForGrounded.
- . , . , , Jammer, .
- .
- : Sound Jammer, Shake, n.
- Duration, n , .
- Duration, : .
- Una vez completada, la habilidad vuelve al primer estado.
Otro ejemplo es Phantom. Mucho sucede aquí de manera similar a Inquisitor, pero todavía hay algunos matices:
- Comenzamos con WaitForClick.
- Luego, la Duración, en la que se establece el teletransporte, se cambian las estadísticas del mech, se reproducen el sonido y la animación.
- Después de eso, DurationOrClick, en el que se cambian las estadísticas de pieles, se reproducen la animación y los efectos.
- Si se hizo un clic, pasamos a otra Duración, en la que se teletransporta el pelaje, cambian las estadísticas, se reproducen la animación, FX y sonidos.
- Después de este estado o una vez finalizado el tiempo de DurationOrClick, vamos a Duration.
La principal diferencia es que aquí aparecen los estados de ramificación. DurationOrClick va al estado a si ha pasado el tiempo especificado, o al estado b , si el jugador tuvo tiempo de hacer clic en el botón de habilidad antes.
Por lo tanto, parecería que nuestro sistema ha evolucionado de simple a complejo, pero de ese modo simplificó la vida tanto de los programadores como de los diseñadores de juegos. La ayuda del primero ahora se necesita principalmente al agregar componentes pequeños, mientras que el segundo ha recibido una mayor autonomía y ahora puede ensamblar de forma independiente nuevas habilidades a partir de estados y componentes existentes. Al mismo tiempo, los jugadores también obtuvieron ganancias en forma de habilidades más diversas y complejas de los mechs.