Cómo vincular un lanzamiento de bala con una animación de disparo

15

Digamos que tiene una animación que desea que suceda al disparar una bala. ¿Cómo conseguirías que aparezca la viñeta al final de la animación? Lo único que puedo averiguar es saber la duración de la animación y retrasar la configuración de la posición de las viñetas y activarla hasta que haya transcurrido ese tiempo. Me preguntaba si este es el mejor enfoque, ¿cómo manejan esto los demás?

EDITAR: Creo que estoy teniendo problemas para redactar la pregunta correctamente.

Mi pregunta

tp0w3rn
fuente
¡Gracias! Muchas buenas ideas de todos ustedes, me gusta la idea de usar una devolución de llamada. Creo que intentaré implementarlo, realmente no quería confiar en el seguimiento del tiempo.
tp0w3rn

Respuestas:

8

Básicamente está en el camino correcto: necesita saber cuánto dura una animación para hacer este tipo de cosas. Las animaciones son más que una simple colección de cuadros, hay todo tipo de información a su alrededor que necesita. Por ejemplo, ¿cuántos cuadros hay, se repite la animación, qué tan rápido se reproduce (por ejemplo, 10 cuadros de animación por segundo o 25, o 60?). Cada animación se puede definir en términos de unos pocos datos, que algunos códigos de animación generalizados pueden ver y reproducir. Debería encapsular la parte de animación en su propio código, que no conoce nada excepto estas definiciones de animación y cómo mostrar los cuadros de imagen individuales. Es decir, tenga un objeto de animación que pueda cargar, comenzar a reproducir, detener la reproducción y decirle que renderice en una ubicación particular en la pantalla.

Un enfoque flexible es utilizar una especie de definición de animación para encapsular este tipo de información. Entonces, en lugar de solo decir "la animación X es todos estos cuadros, solo juega a través de ellos", obtienes algo un poco más complejo.

Por ejemplo, con algún tipo de formato de datos simulado

animaciones =
{
  {name = "walk", files = "walk * .png", frameCount = "12", loop = "true"},
  {nombre = "fuego" archivos = "fuego * .png" frameCount = "6",
       eventos = {
           {name = "bulletLeavesGun", frame = "4", param1 = "43", param2 = "30"}
       }
  }
}

Entonces su código dice algo como:

currentAnimation = animations.Get("fire");
currentAnimation.Play();

La forma en que detecta eventos puede ser con el código de animación que le devuelve la llamada (es decir, cuando detecta un nuevo evento porque la animación se ha reproducido en un marco determinado, llama al código de su juego para informarle sobre el nuevo evento) o sondeando el animación así:

List<Event> events = currentAnimation.EventsSinceLastCheck();
foreach (AnimationEvent event in events)
{
    if (event.name == "bulletLeavesGun")
    {
        Vector2 bulletPosition = new Vector2(event.param1, event.param2);
        Vector2 actualBulletPosition = new Vector2(
                 character.x + bulletPosition.x, 
                 character.y + bulletPosition.y);
        CreateBulletAt(actualBulletPosition);
    }
}

Puntos a tener en cuenta:

  • El código de animación debe existir por separado del código del juego. Realmente no quieres que tu código de juego esté demasiado ligado a los detalles básicos de la reproducción de animación.
  • El código de animación sabe si hacer un bucle o no en función de la definición de animación
  • El código de animación sabe cuándo se realiza la animación, y puede volver a llamar a otro código para decir 'oye, la animación llamada "fuego" acaba de terminar, ¿qué quieres hacer ahora?
  • El código de animación no sabe nada sobre eventos aparte de que tienen un nombre y algunos datos arbitrarios asociados con ellos (param1 y param2)
  • El código de animación sabe en qué cuadro está actualmente, y cuando cambia a un nuevo cuadro, puede verificar y decir 'oh, estoy en el cuadro 4 ahora, eso significa que acaba de ocurrir un evento llamado "fuego", agrégalo a mi lista de eventos recientes para poder contarle a cualquiera que pregunte al respecto '.

Si no necesita que se dispare la bala dentro de la animación, pero solo una vez que haya terminado, puede salirse con la suya con un sistema mucho menos complejo sin la noción de eventos. Pero aún querrá un sistema donde las animaciones se reproduzcan por sí mismas, sepan cuánto duran y puedan volver a llamar al código del juego cuando se complete una animación.

MrCranky
fuente
No estoy de acuerdo con mantener las animaciones conscientes de la lógica (en el cuadro 4 ahora, eso significa que este evento llamado "fuego" acaba de suceder). Las animaciones deben ser ciegas y tontas. Tuve que hacer algunas lógicas del lado del servidor y animaciones de desgarro y la interfaz de usuario fuera de un juego es algo que no quiero volver a hacer. Realmente recomendaría usar animaciones muy cortas y segmentadas, reproducirlas en paralelo a las lógicas y dejar que las lógicas activen secuencias de animación a la velocidad definida por las lógicas. Nunca haga una prueba de lógica para el estado de una animación.
Coyote
Dividir la animación en partes parece bastante innecesario. Estoy de acuerdo con no sondear el estado de la animación, pero eso todavía deja su primera recomendación. No sé si se refería a un sistema de eventos separado para desacoplar el código de animación del resto del juego (¿patrón de observador?) Pero así es como lo haría. Ni las "lógicas" como las dice deben saber sobre el código de animación, o viceversa.
jhocking
@ Cooyote, diría que estás mezclando dos cosas separadas. Sí, la lógica del lado del servidor siempre debe ser independiente de las imágenes (porque no desea tener que ejecutar el sistema de animación solo para descubrir cuándo se dispara una bala), pero eso no lo ayudará a construir un sistema de animación en el cliente . En el cliente, no desea absolutamente que las imágenes se esclavicen sin pensar en el servidor, porque eso se vería horrible: las balas aparecen en momentos extraños y no están sincronizadas con el personaje porque hubo un aumento de retraso entre el juego y el servidor . No hay razón por la que no puedas tener ambas (cont ...)
MrCranky
@Coyote (cont ...), el juego puede ser conducido por el servidor desacoplado de las imágenes. Entonces, la bala se dispara en el momento X en el servidor, y el cliente refleja esa acción al comenzar a reproducir la animación de fuego inmediatamente, con la bala disparando visualmente unos pocos cuadros detrás de la simulación de juego de bala. Es necesario hacer todo tipo de compromisos entre la fidelidad visual y la simulación de juego, por lo que decir que "las animaciones deberían ser ciegas y tontas" es simplemente ingenuo. A veces, los eventos deben estar vinculados a cuadros de animación, porque ningún otro método los hará verse o sonar correctamente.
MrCranky
@Coyote En realidad, ahora que lo pienso, el disparo de bala es un terrible ejemplo para esto, principalmente debido a la respuesta de thedaian a continuación. El disparo debe ocurrir de inmediato. Un mejor ejemplo sería un disparo de efectos visuales cuando un personaje aterriza: el servidor y el cliente se sincronizarán cuando el personaje comience a saltar, pero la pantalla visual se deja al cliente. Y cuando la animación golpea el cuadro derecho donde el pie toca el suelo, el evento VFX debería dispararse. Del mismo modo, se necesitan eventos si se debe tomar una decisión sobre un determinado cuadro de animación si se ramifica a otra animación.
MrCranky
3

De alguna manera, tendrá que esperar hasta que se complete la animación y crear la viñeta en ese punto.

Solo establecer un temporizador funcionará, si está seguro de que su velocidad de animación es fija. Como una variación menor, podría tener un código interno en la viñeta que lo haga esperar invisible por un momento antes de aparecer y moverse.

Dependiendo de su plataforma de desarrollo, puede tener algún tipo de función de actualización de animación o devolución de llamada que le permitirá responder en el momento exacto en que la animación llega al punto deseado. Así es como lo haría con Flixel, por ejemplo.

Gregory Avery-Weir
fuente
1
El addAnimationCallbackmétodo de Flixel se puede usar en la entidad de disparo. En la función de devolución de llamada, puede ver si el cuadro actual de la animación de disparo es el cuadro que debería crear una entidad de viñeta. Si es así, puede agregar una viñeta en la pantalla.
Snow Blind
2

Respuesta directa: suponiendo que tienes una animación que quieres reproducir cuando el jugador presiona el botón 'disparar', y luego sale una bala después de que termine de jugar. Idealmente, debe evitar codificar el tiempo de animación y disparar la bala cuando la animación se complete (usando una función de devolución de llamada o algo, dependiendo de su plataforma). No puedo pensar en ningún otro método para hacer esto que no sea demasiado complejo.

Respuesta alternativa del diseño del juego: a menos que haya un muy, muy buena razón para hacerlo, evitaría tener un retraso al presionar el botón 'disparar' y hacer que aparezca la bala. A menos que la animación sea muy, muy corta (uno o dos cuadros, máximo, básicamente un destello de boca), hará que la respuesta del botón de disparo se sienta lenta, y será molesto para un jugador típico. Incluso si decide utilizar una animación antes de que salgan las balas (los juegos de rol y los juegos tácticos por turnos serían razones aceptables para hacerlo), pensaría en incluir una opción de "desactivar animaciones" en alguna parte, para permitir juego para moverse más rápido si el jugador quiere.

thedaian
fuente
Sí, no hagas que el fuego reaccione lentamente. Esto es como el problema común con el salto; los animadores hacen un gran avance, pero los jugadores esperan volar en cuanto tocan el botón.
jhocking
2

Como dice MrCranky; Mantenga la animación y la lógica separadas.

Pero lo más importante, la lógica debe seguir siendo la parte maestra .

  • Cuando se presiona el botón de disparo, debes activar la " acción " de sorteo en el estado de tu personaje (lógica).
  • Esta acción debería desencadenar la animación de dibujo con todos los parámetros (tiempo de vida, etc.).
  • Una vez que finaliza la acción de dibujar , puede activar la acción de disparo (podría disparar una o más veces según el arma)
  • Esta acción puede generar viñetas y activar la animación de fuego .

Tenga en cuenta que controlar la interfaz de usuario desde las lógicas es la única forma de garantizar que podrá mantener, reutilizar y compartir sus lógicas más tarde (nuevo procesador, nuevo juego, versión del servidor sin un procesador ...)

Coyote
fuente
1

En primer lugar, usaría un sistema de eventos (¿patrón de observador?) Para desacoplar partes del código. Esto no es solo para la animación, sino que ciertamente se aplica allí. Luego, el código de animación puede simplemente decir dispatchEvent (evento) y alguna otra parte del código escucha ese evento, sin que ninguna parte del código necesite conocerse entre sí.

Ahora, el código de animación debe tener una referencia al despachador de eventos (hecho fácilmente con inyección de dependencia) y debe tener algo de XML o JSON que realmente defina sus animaciones. Algo como:

{
  animation: {
    name: shoot,
    length: 12,
    spritesheet: shoot.png
    event: {
      frame: 4,
      name: bulletLeavesGun,
    },
  },
}

Lea los datos al cargar la animación y haga que el código de animación envíe el evento cuando esté en ese cuadro durante la reproducción.

jhocking
fuente