2009-03-25 7 views
5

Tengo una pregunta no necesariamente específica para ninguna plataforma o API, pero más específica para las interacciones en el código entre animaciones.Animación y lógica

Un juego es un buen ejemplo. Digamos que el jugador muere, y hay una animación de muerte que debe terminar antes de que se elimine el objeto. Esto es típico en muchos casos donde alguna animación tiene que terminar antes de continuar con la acción que normalmente seguiría. ¿Cómo harías para hacer esto?

Mi pregunta es sobre el control y la lógica de la animación. ¿Cómo diseñarías un sistema que sea capaz de conducir la animación pero al mismo tiempo implementar un comportamiento personalizado?

El problema que suele surgir es que la lógica del juego y los datos de animación pasan a ser codependientes. Es decir, la animación debe devolver el código o contener metadatos para la duración de las secuencias de animación. Lo que es aún más un problema, es cuando una animación, que tiene que desencadenar algún otro código, digamos después de que 1.13s genere un sprite personalizado, esto tiende a dar como resultado una anidación profunda de código y animación. Una bomba con un temporizador sería un ejemplo de lógica y animación donde ambas cosas interactúan, pero quiero mantenerlas lo más separadas posible.

Pero, ¿qué harías para mantener la animación y el código en dos cosas separadas?

Recientemente he estado probando mgrammar, y estoy pensando, un DSL podría ser el camino a seguir. Eso le permitiría a la animación o al animador, expresar ciertas cosas de una manera presumiblemente segura que luego entraría en la cadena de contenido ...

+0

me gustaría responder a esta pregunta, pero estoy teniendo problemas para entender lo que se quiere separar - hacer desea separar el código de la lógica general del juego y el código de gráficamente renderizar el juego? – CiscoIPPhone

+0

la animación es impulsada por algo, pero las animaciones son complejas, tienen que volver a llamar en código en algún momento. A querer minimizar esto y desacoplar tanto como sea posible para averiguar si hay un conjunto mínimo de características que uno necesita para que funcione. –

Respuesta

5

La solución depende del juego que estés buscando. Si la jugabilidad está 100% impulsada por el código, el anim es impulsado por el estado de la entidad (animación impulsada por el estado). Si se trata de gráficos/animación, la longitud anim determina cuánto tiempo la entidad está en ese estado (estado anim-driven).

Esto último suele ser más flexible en un entorno de producción comercial, ya que el diseñador solo puede decir "necesitamos que la animación de la muerte sea más corta" y se negocia. Pero cuando tienes reglas muy precisas en mente o un sistema simulado como la física, la animación impulsada por el estado puede ser preferible. No existe una solución 100% ortogonal y limpia para el caso general.

Una cosa que ayuda a evitar que se demasiado desordenado es considerar juego patrones AI:

juego AI normalmente se implementa como una forma de máquina de estados finitos, posiblemente varias máquinas de estado o en capas de alguna manera (la división más común es un formato de scripting de alto nivel con acciones/transiciones de bajo nivel).

En el nivel bajo, puede decir cosas como "en el estado de reacción, reproducir mi animé de reacción hasta que finalice, luego averiguar de qué estado continuar desde la lógica de alto nivel". En el nivel superior hay una multitud de formas de definir la lógica, pero simples bucles de repetición como "aproximación/ataque/retirada" son una buena forma de comenzar.

Esto ayuda a mantener los dos tipos de comportamientos, las actividades planificadas y las reacciones a los nuevos eventos, de estar demasiado entremezclados. De nuevo, no siempre funciona de esa manera en la práctica, por las mismas razones que a veces quiere que el código maneje datos o viceversa.Pero eso es IA para ti. ¡No hay soluciones generales aquí!

0

Supongo que realmente no veo el problema.

Si tiene algún nido de devolución de llamada, puedo ver por qué es difícil seguir, pero si solo tiene una llamada para todos los eventos de animación y una función de actualización que inicia animaciones, es bastante fácil seguir el código.

Entonces, ¿qué ganas al separarlos?

+0

Para mí, se siente mal que la animación se entrelaza con el código. Quiero que estas cosas se separen por completo para que los datos de animación sean modificables y de valor. Puede sonar extraño, pero una vez que ingresas a la parte de codificación, es realmente evidente que es un problema. –

+0

Escribo este tipo de código con bastante frecuencia. Parece que estás preguntando cómo puedes desacoplar un sistema impulsado por datos de los datos que lo manejan. No creo que puedas. Creo que tu problema es anidar, pero puedes refactorizar tu código para eliminarlo. Si publica el código con su problema, lo refactorizaré. – BigSandwich

+0

No hay código para publicar, pero estoy tratando de separar los datos de un sistema basado en datos. Como dices, puede que no sea posible, pero soy optimista, quiero hacerlo dos cosas. –

0

Creo que debería separar la renderización de la lógica del juego.

Usted tiene al menos dos tipos diferentes de objetos:

  • Una entidad que posee los datos de la unidad (puntos de golpe, posición, velocidad, fuerza, etc.) y la lógica (la forma en que debe moverse, lo que sucede si se queda sin puntos de golpe, ...).
  • Su representación, que es el sprite, los colores, las partículas que se le atribuyen, los sonidos, lo que sea. La representación puede acceder a los datos de la entidad, por lo que conoce la posición, los puntos de ataque, etc.
  • Quizás un controlador si la entidad puede ser controlada directamente por un ser humano o una IA (como un automóvil en una simulación de automóvil).

Sí, eso suena como la arquitectura Model-View-Controller. Hay muchos recursos sobre esto, vea this article from deWiTTERS o The Guerrilla Guide to Game Code por Jorrit Rouwé, para ejemplos específicos del juego.

Acerca de sus problemas de animación: cuando el Modelo actualiza su entidad y se da cuenta de que no tiene más puntos de golpe, puede establecer una bandera para decir que está muerta y eliminar la entidad del juego (y de memoria) Luego, más tarde, cuando la Vista se actualice, lee la bandera e inicia la animación de la muerte. Pero puede ser difícil decidir dónde almacenar esta bandera ya que el objeto de la entidad debería desaparecer.

Hay una mejor manera en mi humilde mi humilde opinión. Cuando su entidad muere, puede enviar un evento a todos los oyentes que están registrados en el UnitDiedEvent que pertenece a esta entidad específica, luego eliminar la entidad del juego. El objeto de representación de entidad está escuchando ese evento y su manejador inicia la animación de extinción. Cuando la animación termina, la representación de la entidad finalmente puede eliminarse.

El observer design pattern puede ser útil, aquí.

+0

Eso resuelve el problema de la muerte pero no el genérico. Por ej. Juegue anim animado de espada, cuando el anim alcanza el marco 25 encienda el cofre de daño de espada. Quieres que la animación continúe. Una devolución de llamada es realmente la única buena manera de hacer esto. – BigSandwich

+0

Además, ¿quién dice que quieres deshacerte del tipo cuando muere? Tal vez quiero un montón de muñecos de trapo para patear. Tal vez se convierta en un zombi y vuelva a subir. Estás haciendo muchas suposiciones. – BigSandwich

+0

Supongo que agregar más eventos y manejadores de eventos haría el truco. Los manejadores de eventos también pueden alterar o crear objetos nuevos (por ejemplo, una nueva animación en el cuadro 25). – Splo

0

Durante un par de juegos que he hecho para resolver este problema he creado dos clases de animación

asyncAnimation - Para activar y olvidar tipo animaciones

syncAnimation - Si quería esperar a que la animación resolver antes de devolver el control

Dado que los juegos por lo general tienen un bucle principal se veía algo como esto C# del estilo de pseudo código

while(isRunning) 
{ 

    renderStaticItems(); 
    renderAsyncAnimations(); 

    if (list_SyncAnimations.Count() > 0) 
    { 
     syncAnimation = list_SyncAnimations.First(); 
     syncAnimation.render(); 

     if (syncAnimation.hasFinished()) 
     { 
      list_SyncAnimations.removeAt(0); 
      // May want some logic here saying if 
      // the sync animation was 'player dying' update some variable 
      // so that we know the animation has ended and the 
      // game can prompt for the 'try again' screen 
     } 

    } 
    else 
    { 
     renderInput(); 
     handleOtherLogic(); // Like is player dead, add sync animation if required. 
    } 
} 

Entonces, lo que hace el código es mantener una lista de animaciones de sincronización que deben resolverse antes de continuar con el juego. Si necesita esperar varias animaciones, simplemente apílelas.

Además, puede ser una buena idea buscar en el patrón de comando o proporcionar una devolución de llamada para cuando la animación de sincronización haya terminado de manejar su lógica, realmente está como usted quiere hacerlo.

En cuanto a su "freza en 1,13 seg", tal vez la clase SyncAnimation debe tener un método .OnUpdate reemplazable(), lo que puede hacer un poco de lógica personalizada (o llamar a un script)

que depende de lo que pueden ser sus necesidades .

+0

Entonces, en lugar de una devolución de llamada cuando llega al cuadro 25, ¿tiene que sondear la animación con una función de actualización? ¿Cómo es eso mejor? Además, ¿dónde almacenarás los 1.13 segundos? Si está en la animación, todo lo que has hecho es mover las cosas. – BigSandwich

+0

Los juegos tienen una función de votación de todos modos, y sí, está cambiando las cosas. La lógica tiene que ir a algún lado, ponerlo donde le parezca más lógico. – JSmyth

0

La animación puede llamar a una función de devolución de llamada que usted proporcione, o enviar un evento genérico de nuevo al código. No necesita nada más que eso, lo que mantiene toda la lógica en el código. Simplemente inserte la devolución de llamada o conecte el evento cuando se crea la animación.

1

Su enemigo debe tener múltiples estados. Vivo y muerto no son suficientes. Vivo, moribundo y muerto podría ser. Su lazo de procesamiento enemigo debe verificar su estado y realizar diferentes operaciones:

if(state == alive and hp == 0) 
{ 
    state = dying 
    currentanimation = animation(enemy_death) 
    currentanimation.play() 
} 
elseif(state == dying and currentanimation.isPlaying == true) 
{ 
    // do nothing until the animation is finished. 
    // more efficiently implemented as a callback. 
} 
elseif(state == dying and currentanimation.isPlaying == false) 
{ 
    state = dead 
    // at this point either the game engine cleans up the object, or the object deletes itself. 
} 
0

Intento tanto como sea posible para mantener las devoluciones de llamadas de las animaciones secundarias. Las animaciones deben indicar que están completas, las acciones tomadas en una finalización de animaciones deben llamarse desde el nivel de controlador de la aplicación.

En Actionscript esta es la belleza del envío/escucha de eventos: el objeto controlador puede crear la estimación y luego asignar un controlador para un evento que la animación distribuye cuando se completa.

He utilizado el patrón para varias cosas en proyectos de Flash y ayuda a mantener el código independiente mucho mejor que las devoluciones de llamada.

Especialmente si escribe objetos de eventos personalizados que extienden Evento para llevar el tipo de información que necesita. como el evento MouseEvent que incluye localX, localY y stageX y stageY. Utilizo una función personalizada que denominé NumberEvent para transmitir cualquier tipo de información numérica en torno a mis aplicaciones.

en el objeto controladora de ActionScript:

var animObj:AwsomeAnim = AwsomeAnim(); 
animObj.start(); 
animObj.addEventListener(AwsomeAnim.COPLETE,_onAnimFinish); 

function _onAnimFinish():void 
{ 
    // actions to take when animation is complete here 
} 

en JavaScript, donde no existen eventos personalizados. Solo tengo una variable booleana en el objeto de animación, y la compruebo en un temporizador desde el controlador.

en javascript objeto controlador:

var animObj = new animObj();// among other things must set this.isComplete = false 
animObj.start(); 

function checkAnimComplete() 
{ 
    if(animObj.isComplete == true) 
    { 
     animCompleteActions(); 

    }else{ 
     setTimeout(checkAnimComplete,300); 
    } 
} 
checkAnimComplete(); 


function animCompleteActions() 
{ 
    // anim complete actions chere 
} 
Cuestiones relacionadas