¿La mejor manera de gestionar eventos en el juego?

13

Estoy trabajando en un juego donde algunos eventos dentro del juego deben ocurrir de vez en cuando. Un buen ejemplo sería un tutorial. Comienzas el juego, y en varios puntos del juego ocurre un evento:

  • Te encuentras con tu primer enemigo, el juego se detiene y recibes una explicación sobre cómo matarlo.
  • Has matado al primer enemigo, recibes un mensaje de "buen trabajo".
  • Obtienes un nuevo elemento, un menú con la ventana emergente de estadísticas de elementos.
  • etcétera etcétera.

El juego en el que estoy trabajando es un juego de rompecabezas en el que las reglas del juego son más o menos iguales, por lo que parece ineficaz codificar todos estos eventos en niveles separados.

¿Debo definir de alguna manera estos eventos en una fuente externa, como XML? Luego, escriba un intérprete que lea el XML y establezca los requisitos de eventos para el nivel. No estoy seguro de cómo podría definir un evento que debería ocurrir cuando mataste a dos enemigos, por ejemplo.

Para ser claros, no estoy buscando el mejor lenguaje de programación o lenguaje de script para hacer esto, sino más bien el mejor método para manejar esto.

¡Gracias!


Editar: un segundo ejemplo, ya que mi pregunta era bastante difícil de entender:

El problema que tengo es poner algunas acciones adicionales en el juego en un procedimiento que siempre es prácticamente el mismo. Al igual que una batalla RPG, todos tienen un turno, eligen una habilidad, etc., siempre es lo mismo. Pero, ¿y si hubiera un caso en el que me gustaría mostrar una escena en algún punto intermedio? Hacer que toda la estructura del juego pase en una clase de batalla alterada con la escena incluida parece muy ineficiente. Me pregunto cómo se hace esto generalmente.

omgnoseat
fuente
8
No intente generalizar demasiado las cosas, por ejemplo, los tutoriales son muy específicos y vienen con muchos desencadenantes / eventos diferentes. No hay nada malo con hardcoding / scripting.
Maik Semder 01 de
1
@Maik Si pones eso en una respuesta Id +1 it ... Simple y resuelto es mejor que bonito cualquier día.
James
Su segundo ejemplo deja mucho más claro que un sistema de mensajería abstracta sería una gran victoria. Para un tutorial, puede simplemente codificar las cosas ya que solo suceden una vez al principio, pero para los eventos en curso que pueden ocurrir en cualquier momento durante toda la duración del juego, bueno, eso es diferente.
jhocking
Todavía es un poco vago, enumere al menos 3 disparadores para 3 escenas diferentes. Es muy difícil de responder en general. Básicamente, debe encontrar un patrón común para comprender cómo implementarlo mejor.
Maik Semder
¿Qué deseas? ¿Desea pausar las acciones y hacer el extra, luego deshacer las acciones?
user712092

Respuestas:

7

Esto depende mucho de cómo se comunican los eventos entre los objetos de tu juego. Por ejemplo, si está utilizando un sistema de mensajería central, entonces podría tener un módulo tutorial que escuche ciertos mensajes y cree ventanas emergentes tutoriales cada vez que escuche ciertos mensajes. Luego, puede configurar qué mensaje escuchar, junto con la ventana emergente que se mostrará, en un archivo XML o algo analizado por el módulo tutorial. Al tener un objeto de tutorial separado que monitorea el estado del juego y muestra ventanas emergentes de tutorial cuando nota cosas en el juego, puede cambiar el objeto de tutorial a voluntad sin necesidad de cambiar nada más sobre su juego. (¿Es este el patrón Observador? No estoy familiarizado con todos los patrones de diseño).

En general, depende de la complejidad de su tutorial si vale la pena preocuparse por esto. Codificar los eventos en su código y / o niveles no me parece un gran problema para solo un puñado de ventanas emergentes de tutoriales. Tengo curiosidad por saber qué es exactamente lo que tiene en mente que le hace pensar que será ineficiente, ya que todo lo que debe hacer con cada disparador es enviar un mensaje al módulo tutorial, algo así como TutorialModule.show ("1st_kill");

jhocking
fuente
Creo que, dado que es un juego de rompecabezas, su lógica está en una ubicación para múltiples niveles y tal, hacer las verificaciones en caso de que hagamos un tutorial para esto es algo que dura todo el tiempo. Honestamente, si se trata de un juego de rompecabezas, no creo que esto vaya a tener un gran éxito, incluso si no es el código más bonito, y al final del día, el código que funciona en un juego que se envía siempre es -siempre- 100% mejor que un código bonito que nunca ve la luz del día;)
James
Nunca pensé en algo como el patrón de observación, parece una buena solución. Lo intentaré, gracias :)
omgnoseat
7

Aquí están las restricciones de diseño tal como las entiendo:

  1. El código de juego básico no se preocupa por los requisitos de nivel y no debe estar acoplado al código que se ocupa de ellos.

  2. Al mismo tiempo, es el código de juego central que sabe cuándo ocurren los eventos específicos que cumplen con esos requisitos (obtener un objeto, matar a un enemigo, etc.)

  3. Los diferentes niveles tienen diferentes conjuntos de requisitos y esos deben describirse en alguna parte.

Teniendo en cuenta eso, probablemente haría algo como esto: Primero, crea una clase que represente un nivel de juego. Encapsulará el conjunto de requisitos específicos que tiene un nivel. Tiene métodos a los que se puede llamar cuando ocurren eventos del juego.

Dale al código de juego central una referencia al objeto de nivel actual. Cuando se producen eventos de juego, le dirá el nivel llamando a métodos en él: enemyKilled, itemPickedUp, etc.

Internamente, Levelnecesita algunas cosas:

  • Estado para rastrear qué eventos ya han ocurrido. De esta forma, puede distinguir al primer enemigo asesinado de los demás, y sabe la primera vez que recoges un objeto determinado.
  • Una lista de LevelRequirementobjetos que describen el conjunto específico de objetivos que necesita para ese nivel.

Cuando ingresas a un nivel, crearás un Levelcon el LevelRequirements correcto , configurarás el código de juego y le darás ese nivel.

Cada vez que ocurre un evento de juego, el juego lo pasa Level. Eso a su vez calcula los datos agregados (número total de enemigos asesinados, enemigos de ese tipo asesinados, etc.) Luego recorre sus objetos requeridos, dando a cada uno los datos agregados. Un requisito prueba para ver si se cumple, y si es así, se genera el comportamiento resultante apropiado (que muestra el texto del tutorial, etc.)

LevelRequirement básicamente necesita dos cosas:

  1. Una descripción de una prueba para saber si se ha cumplido el requisito. Esto solo puede ser una función si su idioma lo facilita, de lo contrario puede modelarlo en datos. (Tengo una RequirementTypeenumeración con cosas como FIRST_KILLy luego una gran switchque sabe cómo verificar cada tipo).
  2. Una acción a realizar cuando se cumple el requisito.

Todavía queda la cuestión de dónde se describen esos conjuntos de requisitos. Podría hacer algo como XML u otro formato de archivo de texto. Eso es útil si:

  1. Los no programadores serán niveles de autoría.
  2. Desea poder cambiar los requisitos sin tener que volver a compilar y / o reiniciar.

Si ninguno de esos es el caso, probablemente los construya directamente en código. Más simple siempre es mejor.

munificente
fuente
Los primeros 3 puntos son una descripción muy cercana del método que estoy usando ahora, ¡impresionante! Sí, lo que más me cuesta es dónde describir el requisito y cómo traducirlo al juego (ya que lo más probable es que sea algo externo). Gracias por la explicación detallada :)
omgnoseat
5

Pensé que necesita saber cómo hacer estos eventos y el resto de la publicación es sobre eso. Si desea almacenar estos eventos, use una base de datos relacional o descríbalos por texto y use un lenguaje de scripting (analizará y evaluará Tú). :)

Lo que desea es reconocer los eventos ocurridos (1) y luego realizar algunas acciones que estos eventos exigen (imprimir mensaje, verificar pulsación de tecla ...) (2). También desea que estos eventos sucedan solo una vez (3).

Básicamente, desea verificar las condiciones y luego programar un comportamiento.

Cómo reconocer eventos (1)

  • Desea reconocer eventos como estos "primer enemigo encontrado", "nuevo elemento ganado"
  • si ocurre una parte genérica, " enemigo encontrado ", " elemento ganado " Usted verifica la parte específica " primero ...", " nuevo elemento ganado"

¿De qué están hechos los eventos?

En una visión más general, cada evento está compuesto de:

  • precondiciones , las revisas
  • acciones que se realizarán cuando se cumplan las condiciones previas (diga "" ¡Has matado al primer enemigo! ", di" "haz combos presionando los botones A y B", di "presiona 'enter' para continuar", requiere la tecla "enter")

Cómo almacenar estos eventos

En alguna estructura de datos:

  • tener una lista de condiciones previas (cadenas o código si lo está escribiendo en algún lenguaje de alto nivel)
  • tener una lista de acciones (pueden ser cadenas, el motor Quake usa cadenas para eventos)

También puede almacenarlo en una base de datos relacional, aunque parece que no es necesario, si desea hacer este juego en grande, es posible que necesite hacer uno.

Luego tienes que analizar estas cadenas / cosas. O puede usar un lenguaje de script como Python o LUA o un lenguaje como LISP, todos pueden analizarlo y ejecutarlo por usted. :)

Cómo usar estos eventos en el bucle del juego (2)

Necesitará estas dos estructuras de datos:

  • cola de eventos (los eventos que están programados para ejecutarse se colocan aquí)
  • cola de acciones (acciones programadas, eventos implican qué acciones se realizan)

Algoritmo:

  • Si reconoces que se cumplen algunas de las condiciones previas del evento , lo pones en cola del evento
  • (3) Entonces debe asegurarse de que este evento ocurra solo una vez si lo desea :) (por ejemplo, con la matriz booleana has_this_event_happened ["primer enemigo encontrado"])
  • (si la cola de acciones está vacía, entonces) Si hay un evento en la cola de eventos Usted pone sus acciones en la cola de acciones y lo elimina de la cola de eventos
  • Si hay acción en la cola de acciones, comienza a hacer lo que exige
  • Si se realiza dicha acción, la elimina de la cola de acciones

Cómo realizar estas acciones por sí mismo (2)

Haces una lista de objetos, que tienen la función "actualizar". A veces se les llama entidades (en el motor Quake) o actores (en el motor Unreal).

  1. Usted inicia estos objetos cuando se les exige que comiencen en la cola de acciones.
  2. estos objetos se pueden usar para otras cosas, como algunos otros temporizadores. En Quake, estas entidades se usan para la lógica del juego completo, te recomiendo que leas algo de material al respecto .

Acción "decir algo"

  1. Imprimes algo en la pantalla
  2. Desea que este mensaje aparezca durante unos segundos.
  3. en "actualizar":
    • hacer variable remove_me_after y disminuirla por el tiempo que pasó
    • cuando variable es 0 Eliminas esta acción de cola de acciones
    • También elimina este objeto (o programa que se elimine ...)

La acción "requiere clave"

  1. Depende de cómo quieras hacerlo, pero creo que tú haces un mensaje
  2. en "actualizar" ":
    • Simplemente verifica el evento de pulsación de tecla
    • Probablemente necesite algo de matriz / cola para contener eventos de pulsación de teclas
    • luego puede eliminarlo de la cola de acciones y eliminar el objeto

Que métodos aprender

usuario712092
fuente
-1 bien, o simplemente llama a una función, en serio, OP solo quiere un cuadro de mensaje cuando se cumple una determinada condición
Maik Semder
Esa es una muy buena redacción, pero no es exactamente lo que estaba buscando. Me costó mucho explicar lo que quiero. Agregué una segunda explicación: el problema que tengo es poner algunas acciones adicionales en el juego en un procedimiento que siempre es prácticamente el mismo. Al igual que una batalla RPG, todos tienen un turno, eligen una habilidad, etc., siempre es lo mismo. Pero, ¿y si hubiera un caso en el que mostrara una escena en algún punto intermedio? Modyfing para que toda la estructura del juego pase en una clase de batalla alterada con el cutsene incluido parece muy ineficiente. Me pregunto cómo se hace esto generalmente.
omgnoseat