This project is read-only.

Bug with TransitionToAnimation?

Jun 30, 2011 at 11:06 AM
Edited Jun 30, 2011 at 2:26 PM

I think it's a bug, but it could be that I'm just doing it wrong :p

 

Basically, when I call TransitionToAnimation("Idle", .2f)

and then later, StartAnimation("Running")

 

The "Running" animation takes .2 seconds to start playing, which doesn't look good...  if you move the character for less than .2 seconds, it looks like they're just skating across the ground without moving their legs (because it's still stuck on the "Idle" animation, for some reason -- the "Running" animation doesn't start playing until after .2 seconds have passed.)

 

I love the TransitionToAnimation function, and it looks amazing going from "Running" to "Idle."  But it seems like there should be a way to tell it to immediately start playing the "Running" animation -- I would think that would be the default when you simply call StartAnimation() and not TransitionToAnimation().

 

So, I'm assuming this is a bug?  It doesn't even interpolate the frames between Idle and Running during those .2 seconds, as far as I can tell.  It just stays on running for however long the transation time is -- set it to one second and it takes a whole second to start playing the next animation.

 

I just checked, and this is still happening even if I change the "running" animation call from StartAnimation("Running") to TransitionToAnimation("Running", 0.0f) -- weird!  Will investigate further.

 

 

Alex

 

 

 

*edit*

 

Ok, I seem to have fixed it with some simple noodling around, but I'm not entirely confident with the result.  Basically, I had written my player's update loop so StartAnimation("running") only gets called when it's not already the AnimationPlayer's current animation. 

I used the code:

if (Animator.CurrentAnimation != animation)

     Animator.StartAnimation(animation);

 

where Animator is my AnimationPlayer object and animation is a string of the animation to be played.

 

The rest of the time, I figured it would just run...  I didn't want to keep calling it every update loop.  And it does work, this way -- until you mix in calls to other animations with TransitionToAnimation, and then the above behavior manifests itself.

 

But, calling StartAnimation("Running") makes the running animation play just as I want it to, with no delay between the method call and the animation actually visibly playing.  (Even after using TransitionToAnimation("Idle")).

 

Are we supposed to call StartAnimation() every update frame?  StartAnimation is a misleading name if that's the case.  I'm looking into what causes this behavior in StartAnimation and in the AnimationPlayer update loop now.

Jun 30, 2011 at 3:03 PM

I'm not sure I understand what you're trying to do. Neither of these functions should be called every frame. They're only needed when you want to change what animation is playing.

StartAnimation is for when you want to start an animation immediately. It doesn't look pretty to jerk from one pose to another, but sometimes you can't delay. (I almost never call StartAnimation anymore, except during initialization.)

TransitionToAnimation is for when you want to switch animations, but you want it to look nice. Even having a small transition helps things look smooth.

Would it be useful for me to put together a simple platformer demo to show how I use it? I think I can rip something out of one of my games fairly quickly.

Jun 30, 2011 at 4:25 PM
Edited Jun 30, 2011 at 4:35 PM

I got it working, but I guess I was doing it pretty wrong :)

It was a problem in the short method I wrote to wrap StartAnimation().

 

here's the code that was causing me problems, if you're curious, which is called in the player's update loop:

 

        public void PlayDirectionalAnimation(string animation)
        {
            animation += Facing.ToString(); //This appends a string equivalent of the Facing value to the animation string

            if (Animator.CurrentAnimation != animation) // Condition 1: If the CurrentAnimation being played is not the one we're trying to play
                                                                       
            { 
                
                Animator.StartAnimation(animation);
            }
        }

 

 

Facing is an enum of type Direction -- it can be Right, Left, Up, or Down

basically I did this so the correct animation could be automatically played (I have four different animations for each action I animate, one in each direction, so the branching statements would get pretty repetitive)

 

basically, what was happening was that the currentAnimation field was staying as "RunningRight" even though I'd called TransitionToAnimation("IdleRight") -- because the transition was still taking place. 

currentAnimation was == "RunningRight" and transitionAnimation was == "IdleRight"

So, when I called the method above, it was waiting until the transition was over before it passed the if statement and called StartAnimation.

 

 

I finally got it working with this:

 

        public void PlayDirectionalAnimation(string animation)
        {
            animation += Facing.ToString(); //This appends a string equivalent of the Facing value to the animation string

            if (Animator.CurrentAnimation != animation || Animator.Transitioning) // Condition 1: If the CurrentAnimation being played is not the one we're trying to play
                                                         //Condition 2: If there's a Transition going on (this will cut off any transition, so the animation we've passed in starts ASAP                   
            { 
                Animator.TransitionToAnimation(animation, 0.0f); //this is just here to overwrite the Animator.transitionAnimation field
                Animator.StartAnimation(animation);
            }
        }

 

This was the first thing I wrote that helped me realize what was going on.  But I'll have to figure out a better way of only calling TransitionToAnimation() when the player's facing direction or action changes.  (I wanted to do it from the update loop for what I swear is a good reason (:P) but maybe I'll just stick it somewhere else.)

 

Sorry for the epic pile of confusion that the first post turned into :)

Don't worry about posting the demo for me...  the real issue is I need to nail down how I'm doing state management for my creature -- then I'll know the best place to stick animation changes.

 

 

Quick question: Why is Update(0) called from StartAnimation() in AnimationPlayer.cs?  I tried commenting it out and can't see any effects...

 

Hope you're having fun working on your game(s),

Alex

Jun 30, 2011 at 6:24 PM
Edited Jun 30, 2011 at 6:25 PM
alic44 wrote:

Quick question: Why is Update(0) called from StartAnimation() in AnimationPlayer.cs?  I tried commenting it out and can't see any effects...

I'm not 100% sure, but I think that was added to automatically take care of setting the bone transforms in cases where Update isn't being called. (For example, if you're loading an animation to show in an editor, but then you leave it at the default pose. Without that Update(0), all of the pieces were stacked because they didn't have proper transforms.) The Update(0) is confusing, but better than having a bunch of duplicate code in StartAnimation.

As for your actor state situation, that's been something I've struggled with too. I even blogged about that ( http://thirdpartyninjas.com/blog/2010/04/14/game-actor-state-management/ ) a long time ago. (I'm still using the second method.) If anybody reading this has a better suggestion, please. :)

Jun 30, 2011 at 10:34 PM
Edited Jul 1, 2011 at 3:26 AM

Hey Jesse,

yep, I got up and running with a case statement inside update as well, and I'm planning on switching to a class-based finite state machine, as you did.

I'm planning on making this type of state shared between players and enemies, which should at least somewhat cut down on the sprawling number of classes required for each state.  (Player and Enemy are both derived from Creature, which deals with basic states, movement, and animation).

 

Thanks for all your help!  As you might have suspected from my last post, I've been modifying Demina a lot to twist it to my own ends.  I added a deep cloning method to Animation.cs and a Flip() method -- with the DeepClone() method, I create a separate instance of every animation for each creature in my game.  With Flip(), I can create two Right facing animations (by loading one in with the content manager and then cloning it) and then flip one of them so it turns into a left facing animation.  I wanted to do that so I wouldn't have to use a separate SpriteBatch for every creature...  all the flipping is done in the Animation file itself, and not at draw time.

The main reason I wanted to create the cloning method, of course, is so I could set each creature's animation to use different textures without adding stuff to the draw call...  the Animation instances can be updated once, when a player changes equipment, and their animation permanently stores that change.

 

That's the direction I've headed in with Demina so far.  Let me know if you want to see the code sometime -- it's not nearly as neat as yours, but I've written a lot of comments.  I'll definitely post it when I get bone texture customization fully working :)

 

Alex

Jul 1, 2011 at 2:30 PM
alic44 wrote:I added a deep cloning method to Animation.cs and a Flip() method -- with the DeepClone() method, I create a separate instance of every animation for each creature in my game.  With Flip(), I can create two Right facing animations (by loading one in with the content manager and then cloning it) and then flip one of them so it turns into a left facing animation.  I wanted to do that so I wouldn't have to use a separate SpriteBatch for every creature...  all the flipping is done in the Animation file itself, and not at draw time.

I'm curious, why not just use the flipHorizontal and flipVertical options in AnimationPlayer.Draw, instead of copying the animation?

I look forward to seeing what you put together. If you like, toss up a video when you get it all working. :)

Jul 2, 2011 at 7:46 PM
Edited Jul 3, 2011 at 4:28 AM

   Oh, I'll definitely be putting a video or two up :)

 

   I decided not to use the built in flip options because... well, it's a tiny bit convoluted.  First of all, I'm making an action rpg style game where the player can move up/down in addition to left/right.  This means handling draw order in one way or another, so that objects further down the screen get drawn on top of objects further up the screen.  Personally, I like to use XNA's built-in layerDepth for this.  So, I wrote a new draw function for AnimationPlayer that takes in layerDepth.  The problem is, anything being sorted by layerDepth has to be part of the same spriteBatch.  Which meant I couldn't use the flipHorizontal function for animations, since that manipulates each animation by making it a separate SpriteBatch.

Then I realized that just by multiplying each bone's position.x and angle by -1 and flipping the texture, each animation would be perfectly mirrored. 

I decided that since I wanted to create a system where each creature could use different textures* for the same animations anyway, I might as well create a clone method, rather than trying to manage all this stuff every frame in the draw loop.

 

That's basically most of what I've done with Demina so far.  My goal is to have a simple playable level done in the next month...  with some kind of visual character customization.  So bug me if you don't see anything pretty soon :)

 

Obviously that's a really optimistic projection to make for a project I only started a week ago, not to mention my first major game project. 
But things are moving along pretty quickly.  I really could not be making this much progress without Demina.  Thanks again, Jesse.

 

*note that I'm not actually duplicating the textures with the clone method; just the values that point to which texture to use.

 

Alex