This is an old revision of the document!
Veasé los anteriores tutoriales básicos para más información acerca de creación básica de objetos, manejo del reloj y fotogramas.
Este tutorial solo cubre un uso muy básico de las animaciones en orx.
Todas las animaciones son guardadas en grafo dirigido.
Este gráfico define todas las posibles transisiones entre animaciones. Una animación es referenciada usando un único caracter de cadena. Todas las transiciones y animaciones son creadas via ficheros de configuración.
Cuando se solicita una animación, el motor evaluará la cadena que lo traerá a esta animación desde la actual.
Si esa cadena existe, a continuación se procesará automáticamente. El usuario será notificado cuando se inician las animaciones, se detienen, se corten o estén en un bucle de eventos.
Si no especificamos ninguna animación como objetivo, el motor va a seguir los enlaces naturalmente, de acuerdo a sus propiedades 1).
También hay una manera de saltarse este procedimiento de encadenamiento y forzar una animación inmediatamente.
El Código-sabio de este sistema es muy fácil de usar con dos funciones principales para manejar todo. La mayoría del trabajo se realiza no en el código, sino en los archivos de configuración cuando se definen las animaciones y enlaces. 2)
Como de costumbre, comenzamos por la carga de nuestro archivo de configuración, creando una ventana, obteniendo el reloj principal y registrando nuestra función Update
a la misma y, por último, mediante la creación de nuestro objeto principal.
Por favor, consulte los tutoriales anteriores para más detalles.
Ahora comencemos con el codigo, veamos como organizar los datos hasta el final de esta página.
En nuestra función Update
solo tendremos que activar la animación WalkLeft
cuando la entrada GoLeft
está activa y la animación WalkRight
cuando la entrada GoRight
está activa.
Cuando ninguna entrada está activa, simplemente removemos el objetivo de animación y dejamos que el gráfico sea evaluado normalmente3).
if(orxInput_IsActive("GoRight")) { orxObject_SetTargetAnim(pstSoldier, "WalkRight"); } else if(orxInput_IsActive("GoLeft")) { orxObject_SetTargetAnim(pstSoldier, "WalkLeft"); } else { orxObject_SetTargetAnim(pstSoldier, orxNULL); }
¡Así es! Cómo se llega desde cualquier animación actual al objetivo que será evaluado usando el gráfico. Si las transiciones son necesarias se van a reproducir automáticamente 4).
PD: Si hubiéramos querido ir inmediatamente a otra animación sin respetar las transiciones definidas por datos (en el caso de las animaciones de golpe o muerte, por ejemplo), podríamos haber hecho esto.
orxObject_SetCurrentAnim(pstSoldier, "DieNow");
PD: Hay mas funciones para el control avanzado de animaciones (como pausando, cambiando frecuencia, …), pero el 99% del tiempo, esas dos funciones (orxObject_SetCurrentAnim()
y orxObject_SetTargetAnim()
) son las únicas que necesitarías.
Veamos ahora como podemos estar informados de que sucede con nuestras animaciones (como sincronizar sonidos, por ejemplo).
Primero, necesitamos registrar nuestra devolución de llamada(callback) EventHandler
para los eventos de animación.
orxEvent_AddHandler(orxEVENT_TYPE_ANIM, EventHandler);
Hecho! Veamos que podemos hacer con esto ahora.
Digamos que desea imprimir que animaciones son reproducidas, se detuvieron, cortaron o continuaron en nuestro objeto. A continuación, tendríamos que escribir la siguiente devolución de llamada(callback).
orxSTATUS orxFASTCALL EventHandler(const orxEVENT *_pstEvent) { orxANIM_EVENT_PAYLOAD *pstPayload; pstPayload = (orxANIM_EVENT_PAYLOAD *)_pstEvent->pstPayload; switch(_pstEvent->eID) { case orxANIM_EVENT_START: orxLOG("Animation <%s>@<%s> has started!", pstPayload->zAnimName, orxObject_GetName(orxOBJECT(_pstEvent->hRecipient))); break; case orxANIM_EVENT_STOP: orxLOG("Animation <%s>@<%s> has stoped!", pstPayload->zAnimName, orxObject_GetName(orxOBJECT(_pstEvent->hRecipient))); break; case orxANIM_EVENT_CUT: orxLOG("Animation <%s>@<%s> has been cut!", pstPayload->zAnimName, orxObject_GetName(orxOBJECT(_pstEvent->hRecipient))); break; case orxANIM_EVENT_LOOP: orxLOG("Animation <%s>@<%s> has looped!", pstPayload->zAnimName, orxObject_GetName(orxOBJECT(_pstEvent->hRecipient))); break; } return orxSTATUS_SUCCESS; }
En primer lugar, obtener la capacidad de carga de nuestro evento. Como sabemos sólo manejamos los eventos de animación aquí, podemos con seguridad emitir la capacidad de carga de orxANIM_EVENT_PAYLOAD
definido en orxAnim.h.
Si estamos usando la misma rutina para diferentes tipos de eventos, lo primero que deberíamos ver es si estábamos recibiendo un evento animación. Esto puede hacerse con el código siguiente.
if(_pstEvent->eType == orxEVENT_TYPE_ANIM)
Finalmente, nuestro evento receptor (_pstEvent→hRecipient
) es en realidad el objeto sobre el que se reproduce la animación. Lo emitimos como un orxOBJECT
usando la macro de ayuda orxOBJECT()
. 5)
Let's now have a peek a the data side.
First, we need to define an animation set that will contain the whole graph for our specific object's animations.
The animation set won't ever be duplicated in memory and will contain all the animations and links for the corresponding graph.
In our case we have 4 animations and 10 possible links for all the transitions.
[AnimSet] AnimationList = IdleRight#WalkRight#IdleLeft#WalkLeft LinkList = IdleRightLoop#IdleRight2Left#IdleRight2WalkRight#WalkRightLoop#WalkRight2IdleRight#IdleLeftLoop#IdleLeft2Right#IdleLeft2WalkLeft#WalkLeftLoop#WalkLeft2IdleLeft
Now let's define our animations!
Previous to that, so as to reduce the amount of text we need to write, we'll use orx's config system inheritance.
We'll begin to define a section for the position of our pivot 6).
As you may have seen in the object tutorial config file, the pivot is which position will match the world coordinate of your object in the world space. If it's not specified, the top left corner will be used by default.
The pivot can be defined literally using keywords such as top
, bottom
, center
, left
and right
, or by giving an actual position, in pixels.
[Pivot] Pivot = (15.0, 31.0, 0.0)
Now we'll define our graphic object that will inherit from this pivot. In our case it's a bitmap that contains all the frames for our object.
The common properties are thus the name of the bitmap file and the size of one frame 7).
[FullGraphic@Pivot] Texture = ../../data/anim/soldier_full.png TextureSize = (32, 32, 0)
Ok, everything is setup for creating all the frames.
Let's define all our frames for both right-oriented animations: we have 6 of them.
[AnimRight1@FullGraphic] TextureCorner = (0, 0, 0) [AnimRight2@FullGraphic] TextureCorner = (0, 32, 0) [AnimRight3@FullGraphic] TextureCorner = (0, 64, 0) [AnimRight4@FullGraphic] TextureCorner = (32, 0, 0) [AnimRight5@FullGraphic] TextureCorner = (32, 32, 0) [AnimRight6@FullGraphic] TextureCorner = (32, 64, 0)
As you can see, they all inherit from FullGraphic
and the only property that tells them apart is the TextureCorner
.
Ok, now we have defined all those graphic objects (they'll become orxGRAPHIC
structures when loaded), let's define the animations themselve.
Let's begin with the IdleRight
which contains a single frame that lasts for 0.1 second.
[IdleRight] KeyData1 = AnimRight6 KeyDuration1 = 0.1
Easy enough, let's try the second one: WalkRight
[WalkRight] DefaultKeyDuration = 0.1 KeyData1 = AnimRight1 KeyData2 = AnimRight2 KeyData3 = AnimRight3 KeyData4 = AnimRight4 KeyData5 = AnimRight5 KeyData6 = AnimRight6
Not really harder as we define the same time for all the frames using the DefaultKeyDuration
property.
We can override it for any frame by specifying a key duration like we did for the IdleRight
animation.
We'll do the exact same thing for the left-oriented animations. Actually as we're using flipped graphic objects, we could just have flipped the object at runtime in the code.
But that wouldn't have served our didactic purposes! Let's pretend these left animations are completely different from the right ones!
There are only the links missing now and we're done!
The basic link structure is easy: we specify the source and the destination animation.
[IdleRightLoop] Source = IdleRight Destination = IdleRight
This link goes from IdleLoop
animation to the IdleLoop
animation. No wonder we called this link IdleRightLoop
!
So basically, when we are in the IdleRight
animation and we asked IdleRight
animation as a target, we just stay there, looping.
Also, if no target is defined when we are there, this link will keep us looping as there isn't any higher priority link starting from IdleRight
.
Let's see now how we go from IdleRight
to WalkRight
.
[IdleRight2WalkRight] Source = IdleRight Destination = WalkRight Property = immediate
Here we have the same basic info as before but we also have the immediate
value for the key Property
.
This means that when we are in IdleRight
animation and we target WalkRight
, we won't wait till IdleRight
is over to follow the link, we'll go there directly: that gives us a way to cut animations.
As we've seen in the code, we don't explicitely ask for the idle animation when we are already walking. How do this work then??
Let's see the link that goes from WalkRight
to IdleRight
.
[WalkRight2IdleRight] Source = WalkRight Destination = IdleRight Property = immediate; <= If you remove this property, the animation won't be cut to go immediately back to idle Priority = 9
When we are in WalkRight
and we remove our target, the engine will have to follow the links in a natural way. That means it'll favorise the higher priority links.
By default a link Priority
is 8. It can range from 0 to 15. Having here a link at priority 9 means that this will be the one taken when we have no target.
It will bring us back to IdleRight
.
We also have added the immediate Property
so that we won't wait the end of the walk cycle to go back to idle.
NB: This is a very basic graph that shows only basic transitions, but the system is very expandable.
Let's say you want to begin walking from a sitting pause without transition.
But, later in the game development, you want to add a standing up transition for it to look nicer.
You'll only have to add this extra step (with the associated links) in the config file! Your code will remain unchanged:
orxObject_SetTargetAnim(MyObject, "Walk");
HotSpot
in some engines