Changing animation frequency / getting frame count

edited October 2013 in Help request
I've been playing around with this problem all evening and I am completely stuck how to solve this. Jim's post was very handy as a reference:

https://forum.orx-project.org/discussion/6412#Comment_6428

Anyway here is my issue:

Say I have three animations:

HeroIdle
1 x frame

HeroJumpOver
20 x frames

HeroLanded
1 x frame

AnimLinks are: HeroIdle -> HeroJumpOver -> HeroLanded

So to have him jump over a gap, I would only need a:

orxObject_SetTargetAnim(heroObject, "HeroLanded");

So far so good. But if the gap is long, I want to slow down the animation or speed it up if the gap is short:

orxObject_SetAnimFrequency(heroObject, 0.9f);

And using SetAnimFrequency works great. But the problem is, I set the target as "HeroLanded", how can I possibly know how many frames there are between "HeroIdle" and "HeroLanded"?

I would like to take that amount of frames (20) and perform a calculation for the frequency, ie:

frequency = number-of-frames / distance-steps

Then use this on:

orxObject_SetAnimFrequency(heroObject, frequency);

So to recap, how to say:

* This is my current animation.
* This is my target animation.
* How many frames will play in total?

Thanks guys, this has me totally stumped.

Comments

  • jimjim
    edited October 2013
    I don't think that's possible with current setup and without using an extra layer of bookkeeping, like I tried to mention in my post.

    All we need to do, is read from .ini and get info about our animation frames, like how many frames are there in each animations, key durations, so that we can easily calculate the time it needs to play a certain frame, or start from a certain frame, specially if your key durations differs from frame to frame.
  • edited October 2013
    Yes, but the problem with that approach is that you'd need to read through sections, calculating how many frame lines there are, and then traverse the animation link config looking for links to other animations.

    Too cumbersome when the animation graph is already pre-loaded into memory. I am happy to write a routine to traverse the in-memory links to trace all my animations and get frame counts, but it is at this point I am lost as to where they are.

    Perhaps the root of the answer is here to get to the animationset from a particular object:

    https://forum.orx-project.org/discussion/6412#Comment_6428

    And then make my way though using anim links using things like orxAnimSet_GetLink to gather the info.

    Think I might be answering my own question.
  • edited October 2013
  • jimjim
    edited October 2013
    iarwain mentioned in my post that,
    You can access all the values for all the keys via the orxAnim API, but not their timestamps
    Also orxANIMPOINTER contains following data
    orxSTRUCTURE            stStructure;                /**< Public structure, first structure member : 32 */
      orxANIMSET             *pstAnimSet;                 /**< Referenced AnimationSet : 20 */
      orxANIMSET_LINK_TABLE  *pstLinkTable;               /**< Link table pointer : 24 */
      orxU32                  u32CurrentAnim;             /**< Current animation ID : 28 */
      orxU32                  u32TargetAnim;              /**< Target animation ID : 32 */
      orxFLOAT                fCurrentAnimTime;           /**< Current Time (Relative to current animation) : 26 */
      orxFLOAT                fTime;                      /**< Current Time (Absolute) : 40 */
      orxFLOAT                fFrequency;                 /**< Current animation frequency : 44 */
      orxU32                  u32CurrentKey;              /**< Current animation key : 48 */
      const orxSTRUCTURE     *pstOwner;
    

    So, I think there is no way to know which animset contains how many frames and their exact time frame for each frames. But it would have been really cool if those data were accessed or may be they are stored in some other place. Now, only iarwain can answer, where those data are stored.
  • edited October 2013
    Ah I see. Fortunately for me in my current case, I only need the frame count. I won't need timestamps.

    So far I have come up with this and it looks promising:
    orxANIMPOINTER *pstAnimPointer = orxOBJECT_GET_STRUCTURE(heroObject, ANIMPOINTER); 
    
    orxU32 animCurrentID = orxAnimPointer_GetCurrentAnim (pstAnimPointer);
    	
    orxANIMSET *pstAnimSet = orxAnimPointer_GetAnimSet(pstAnimPointer); 
    	
    orxU32 animDestinationID = orxAnimSet_GetAnimIDFromName (pstAnimSet, animRequired);
    
    orxFLOAT pfTime = 0;
    orxBOOL pbCut = false;
    orxBOOL pbClearTarget = false; 
    
    orxANIMSET_LINK_TABLE *linkTable;
    
    orxAnimSet_ComputeAnim(	pstAnimSet, animCurrentID, animDestinationID, &pfTime, linkTable, &pbCut, &pbClearTarget);
    
    

    The problem is, even though the linkTable parameter of the ComputeAnim method is in/out, my linkTable variable remains empty. I think this a limitation of my c++ skills. I thought I could declare an empty orxANIMSET_LINK_TABLE and the ComputeAnim would populate it.

    From here I hope to traverse the link table and get properties from each anim I pull out.
  • jimjim
    edited October 2013
    Number of frame is what you need, if your key duration is same or you are using same DefaultKeyDuration, if different frame has slightly different keyduration for animation purpose, it would not work imo.

    I think there is a more simple solution, all you need is the distance your player would normally cross during that animation period.

    For example, if you character goes 10 meters in 2 seconds (here 2 second is also the time it takes to complete a jump animation, but you wont need exact time of animation, as frequency is relative) then to cross 5 meters, you need to play that animation for 1 sec, so your animation frequency should be 2, if I am not wrong

    frequency = normal_distance / current_distance_to_cross

    If this is not what you want, then I am not clear enough about your problem :p
  • edited October 2013
    Hmmm what you suggest is true... I could do that, and that is a very good idea.

    But the more I think about it, I really have about 12 animations, not just one, and the distance time it takes normally, is the actual frame count.

    If I get totally desperate, I may have to have to "double handle" all the frame lengths as a lookup table inside my code and maintain the data in both the config and the table. So if an animation is ever rebuilt and the total frames changes, I'll have to maintain in the table. Not ideal.

    Oh well... if I can't get anywhere I'll have to make a table of frame lengths and just move on for now.
  • edited October 2013
    Hi!

    Sorry, I'm a bit late on that one. :)

    First of all, the number of frames shouldn't matter, the important metrics here are distance and time.

    Secondly, in your case the time spent in the air is only the duration of your HeroJumpOver, isn't it? As you do a SetTarget as soon as you take off, and the HeroLanded should play as soon as the jump is over.

    You can easily change the frequency to match the time spent in the air. If you spend X seconds in the air, the frequency should be Freq = AnimLength / X.

    You can set the Frequency when getting the orxANIM_EVENT_START for HeroJumpOver and reset it to 1 when getting the orxANIM_EVENT_STOP for it. As for the length of HeroJumpOver, in the event callback, you can get the orxANIM from the payload and simply have to call orxAnim_GetLength() on it.

    Now if you wanted to know the cumulated time of a few consecutive anims, your code snippet using orxAnimSet_ComputeAnim() is definitely going in the right direction.
    Something like this should do the trick:
    orxU32 u32AnimID, u32TargetAnimID;
    orxFLOAT fTotalLength = orxFLOAT_0, fLength;
    orxBOOL bCut, bClear;
    
    // Gets Target Anim ID
    u32TargetAnimID = orxAnimSet_GetAnimIDFromName(pstAnimSet, "MyTarget");
    
    // Gets current Anim ID
    u32AnimID = orxAnimSet_GetAnimIDFromName(pstAnimSet, "MyCurrent");
    
    // While destination isn't reached
    while((u32AnimID = orxAnimSet_ComputeAnim(pstAnimSet, u32AnimID, u32TargetID, &fLength, orxNULL, &bCut, &bClear)) != u32TargetID)
    {
      // Updates total length
      fTotalLength += fLength;
    }
    

    You might also want to handle the case where Target is unreachable from the current animation (failsafe) by comparing the result of the call with orxU32_UNDEFINED.

    You can now do the same Frequency manipulation.

    If I'm missing something, please let me know. :)

    As a side note, in most projects, jump animations are cut in more pieces: at least JumpStart, JumpLoop, JumpLand, and sometimes more cycles depending (JumpLoop would be cut into JumpRaise/JumpFall).

    In those case, when jumping, one would SetTarget(JumpStart) and have the ClearTarget flags in the link that leads to JumpStart, which means there wouldn't be any target anymore when JumpStart is reached, reverting to the regular non-contrived flow of the animset (probably going to JumpLoop and looping there forever).
    Then, when about to land, we'd set target with JumpLand, using the same ClearTarget flag on the link leading to it. From there it'll default probably to go back to a Idle looping animation.

    If something I just said isn't very clear, please let me know. :)
  • edited October 2013
    YESSSS!!!!!!!!!! This is totally it! Thank you, Iarwain.

    Either solution will do me fine... I didn't give the event triggers a single thought.

    And the ComputeAnim while loop, that's brilliant, I'll go for that first as it's in the area where I do my raycasting.

    Made my day. Thanks for your help too Jim. Got me thinking.
  • edited October 2013
    I have found a bit of an issue with the ComputeAnim routine. I expanded it so I could follow along as:
    while(animCurrentID != animDestinationID) { 		
       animCurrentID = orxAnimSet_ComputeAnim(pstAnimSet, animCurrentID, animDestinationID, &fLength, orxNULL, &pbCut, &pbClearTarget);
       fTotalLength += fLength; 
    }
    

    If the animID values were:
    HeroIdle = 0
    HeroJumpOver = 14
    HeroLanded = 15

    The I am getting an endless loop where the returned values are constantly 14.

    So the first time, 0 and 14 are compared. 14 returned
    Next loop, 14 and 15 are compared. 14 returned.
    Next loop, 14 and 15 are compared. 14 returned.
    ...and so on...

    Never makes it to 15, but the value isn't unreachable with
    orxU32_UNDEFINED.

    The animation links appear to be fine as the animation usually works nicely, no log warnings.

    I amended ComputeAnim to take in a link table but it made no difference.

    I'd really like to keep heading on this method.
  • edited October 2013
    Ah yes, I forgot an important part when I typed the code in the forum, sorry about that! :)

    In order to know which anim is next, the function needs to know how much time has passed since last call. That's why you're staying always on the same animation.
    Try this modification:
    while(animCurrentID != animDestinationID)
    {
     fLength = orxFLOAT_MAX;
     animCurrentID = orxAnimSet_ComputeAnim(pstAnimSet, animCurrentID, animDestinationID, &fLength, orxNULL, &pbCut, &pbClearTarget);
     fTotalLength += fLength;
    }
    
  • edited October 2013
    Actually, nevermind, that will not work either!

    I'll simply add a function orxAnimSet_GetNextAnim(orxU32 _u32SourceAnimID, orxU32 _u32TargetAnimID) for you that should do exactly what you need.

    You'll have to sync orx however, sorry!
  • edited October 2013
    Ok, orxAnimSet_FindNextAnim() has been added.

    If you sync the code, you can now do:
    for(animCurrentID = orxAnimSet_FindNextAnim(animCurrentID, animDestinationID);
        (animCurrentID != orxU32_UNDEFINED) && (animCurrentID != animDestinationID);
        animCurrentID = orxAnimSet_FindNextAnim(animCurrentID, animDestinationID))
    {
      fTotalLength += orxAnim_GetLength(orxAnimSet_GetAnim(animCurrentID));
    }
    

    Lemme know if you have any problem!
  • edited October 2013
    Oh gosh, thank you. Sorry for the fuss, didn't think you'd have to change the code base.

    Ok, I'll get the latest down and upgrade my project.

    I'll post back on how it goes.
  • edited October 2013
    No worries, it was an easy addition. Hopefully working as intended (I didn't exactly test it ;)).
  • edited November 2013
    Hi Iarwain, finally back on this after last weeks diversion! :)

    That function is working now as expected. Thanks so much for including that. The routine is currently:
    while (animCurrentID != orxU32_UNDEFINED && animCurrentID != animDestinationID){
    		
          orxU32 animNextID = orxAnimSet_FindNextAnim(pstAnimSet, animCurrentID, animDestinationID);
          orxAnimSet_ComputeAnim(pstAnimSet, animNextID, animDestinationID, &fLength, orxNULL, &pbCut, &pbClearTarget);
          fTotalLength += fLength; 
    		
          animCurrentID = animNextID;
    }
    

    Now the next hurdle is that orxAnimSet_ComputeAnim is only outputting 0's. Frame length is not available, so fTotalLength results in 0.
  • edited November 2013
    That's actually normal. :)

    You shouldn't be calling orxAnimSet_ComputeAnim at all!

    Instead, you should be using something similar to the snippet I wrote in this post.
  • edited November 2013
    orxAnim_GetLength(orxAnimSet_GetAnim(animCurrentID)) !!!!

    I'm so sorry, I totally skipped over that part of the snippet when I used it to construct my loop.
  • edited November 2013
    Things are working so sweet now. My little dude varies his timings based on distance.

    Thanks Iarwain & all. My final code is:
    orxFLOAT fTotalLength = 0;
    	
    while (animCurrentID != orxU32_UNDEFINED && animCurrentID != animDestinationID){
    	
       orxANIM *animCurrentAnim = orxAnimSet_GetAnim (pstAnimSet, animCurrentID);
    		
       fTotalLength += orxAnim_GetLength(animCurrentAnim); 
    
       animCurrentID = orxAnimSet_FindNextAnim(pstAnimSet, animCurrentID, animDestinationID);
    
    }
    
  • edited November 2013
    My pleasure, I'm glad you got it working! :)
Sign In or Register to comment.