This project is read-only.

Animation Question

Jul 15, 2010 at 3:24 PM

How do I stop an animations after a certain time? I'm using blended animations, when I press a key, a stance animations is weighted to 0, and a punch animation is weighted to one. My problem is that the punch animation needs to be played once and only once per keypress. I'm wondering if there is some way to use  Punch.AnimationDuration to check if a certain time has elapsed, say one second, and set the punch weight back to 0. I'm not sure how to get this working though, and it still leaves me with another problem, if I quickly press and release the punch key, the animation does not play through to the end, rather it just plays the frames equal to the duration of the keypress.

This is the code used

if (keyboardState.KeyState.Space.OnPressed)
            {
                Stance.Weighting = 0;
                Punch.Weighting = 1;
            }
            else if (keyboardState.KeyState.Space.OnReleased)
            {
                Punch.Weighting = 0;
                Stance.Weighting = 1;
            }

Jul 17, 2010 at 12:21 AM
Edited Jul 17, 2010 at 12:22 AM

In this case, it would be best to play the punch animation as non-looping.
Keep the handle to the animation (AnimationInstance struct), and call Stop() to make sure an existing punch animation isn't already playing.

Punch.Stop(); // stop any existing punch anim
Punch = model.PlayAnimation(...);  // play a new one non-looping...

You can also query the 'Time' value of the animation, which is the current play position of the animation.

Sorry for my slow reply, I've been really busy the last few days.

Jul 20, 2010 at 12:18 AM

I tried your suggestion, but it causes the model to go into the bind pose for the duration of the button press, rather than play an animation.

Jul 20, 2010 at 6:05 PM
Edited Jul 26, 2010 at 1:24 AM

I'd suggest something more along the lines of:

if (keyboardState.KeyState.Space.OnPressed)
{
    StopPunchAnimation(); //make sure the animation isn't still playing
    PlayPunchAnimation();
}


......


if (IsPlayingPunch()) { Punch.Weighting = 1; Stance.Weighting = 0; } else { Punch.Weighting = 0; Stance.Weighting = 1; }
Jul 20, 2010 at 6:46 PM

This code seems better

public UpdateFrequency Update(UpdateState state)
        {
            KeyboardInputState keyboardState = state.KeyboardState;
            //Movement code
            Vector3 playerPositionAdd = Vector3.Zero;

            int punchAnimationIndex = animationController.AnimationIndex("Punch");

            if (keyboardState.KeyState.Space.OnPressed)
            {
                Punch.StopAnimation();
                Punch = animationController.PlayAnimation(punchAnimationIndex);
                Punch.Weighting = 1;
                Stance.Weighting = 0;               
            }

            if (Punch.AnimationFinished == false)
            {
                Punch.Weighting = 1;
                Stance.Weighting = 0;
            }
            else if (Punch.AnimationFinished == true) 
            {
                Punch.Weighting = 0;
                Stance.Weighting = 1;
            }           

            if (keyboardState.KeyState.A.OnPressed )
            {
                worldMatrix = leftFacing * Matrix.CreateTranslation(Position);
            }
            if (keyboardState.KeyState.A.IsDown )
            {
                Stance.Weighting = 0;
                Punch.Weighting = 0;
                Walk.Weighting = 1;
               
                playerPositionAdd.Y -= 0.2f;
                
            }
            else if (keyboardState.KeyState.A.OnReleased )
            {
                Walk.Weighting = 0;
                Stance.Weighting = 1;               
            }

            if (keyboardState.KeyState.D.OnPressed )
            {
                worldMatrix = rightFacing * Matrix.CreateTranslation(Position);
            }
            if (keyboardState.KeyState.D.IsDown )
            {
                Stance.Weighting = 0;
                Punch.Weighting = 0;
                Walk.Weighting = 1;    
          
                playerPositionAdd.Y -= 0.2f;               
            }
            else if (keyboardState.KeyState.D.OnReleased)
            {
                Walk.Weighting = 0;
                Stance.Weighting = 1;
            }
            
            worldMatrix = Matrix.CreateTranslation(playerPositionAdd) * worldMatrix;
            return UpdateFrequency.FullUpdate60hz;
        }

But there is still a frame or so between when the punch finishes and the stance begins, where the model reverts to it's bind pose.

Jul 23, 2010 at 4:12 PM
Also, I update the .dll's to the 1.8.2 version. I saw the other thread, thought this might help me too, but no luck.
Jul 26, 2010 at 1:23 AM
Edited Jul 26, 2010 at 1:26 AM

Hi.

Sorry for the late reply, internet issues.

1.8.2 patch fixes a number of issues with animation interpolation and playback. However the issue you are seeing is expected behavior.

I would highly recommend breaking off the code that calculates weights. Doing this sort of logic, eg changing weights when buttons are pressed, etc, is generally a bad idea. It'll get unmaintainable very quickly.
The logic for what the weights need to be should be fairly simple to calculate and have a very fixed pattern. Ie, walk is (1-punch) * walking_speed, idle is (1-walk) * (1-punch). etc. It should be pretty easy to get the maths exactly right so they always add up to exactly 1.0.

Which brings me to the way to solve this problem:

Set the weights in the Draw method for your model!
It's the reason for the single frame popping. You setup the animations that will be played in Update, but this doesn't mean they have been updated yet. They will be updated as they are needed - eg when the model is on screen and the frame is drawn or for the next call to Update. This is intended behavior for a number of reasons - eg, the animations can be processed as thread tasks asynchronously.

I hope that makes sense.
Cheers

Jul 26, 2010 at 2:01 AM

I'm not entirely sure what you mean by the logic for the weights. Should it be like

 if (keyboardState.KeyState.A.IsDown )
            {
               
                Walk.Weighting = 1 - Punch.Weighting;
               
                playerPositionAdd.Y -= 0.2f;
                
            }

With the weighting set in the draw method like this?

public void Draw(DrawState state)
        {
            Stance.Weighting = 1;
            Walk.Weighting = 0;      
      
            state.PushWorldMatrix(ref worldMatrix);
            if (model.CullTest(state))
                model.Draw(state);

            state.PopWorldMatrix();
        }

Jul 26, 2010 at 10:19 AM

I think you should not be doing that at all.

The problem is, if you have several places where you are changing weight values then it gets exponentially harder to track the changes.

The simple way to get around this is to have a single method that sets the weights in a consistent way, based on fixed logic that you can verify is going to always produce the right result. Example:

void SetupWeights()
{
	Punch.Weight = Punch.IsPlaying ? 1 : 0;
	Walk.Weight = (1 - Punch.Weight) * WalkSpeed;
	Idle.Weight = (1 - Walk.Weight - Punch.Weight);
}

Something like this can be called at the beginning of the Draw method once. It can be setup to gurantee that the weights will always add up to one, and as you add new features, it won't explode in complexity.

Jul 26, 2010 at 9:12 PM
Punch.IsPlaying

How do I check that? I dont see that in the AnimationInstance class.

Also, where should I be changing the weighting values, I assume I still need to change the weights to play the animations.
Finally, you reference walkspeed, whereas I use playerPositionAdd.Y -= 0.2f; to move the player, should I change this too?
Jul 27, 2010 at 5:39 PM

I was writing pseudo code. While there isn't an 'IsPlaying' there is AnimationFinished, which is the opposite.

The same with walkspeed, I'm just guessing how you internally represent your gamestate by giving an example.

You should make sure the weights are set before drawing, or before reading bone transformed positions back (which is less common)

Jul 29, 2010 at 9:35 PM

This is the current weight setup

public void SetupWeights()
        {
            Punch.Weighting = !Punch.AnimationFinished ? 1 : 0;
            Walk.Weighting = (1 - Punch.Weighting - Stance.Weighting);
            Stance.Weighting = (1 - Walk.Weighting - Punch.Weighting);
        }
And this is the input code.

 

 public UpdateFrequency Update(UpdateState state)
        {
            KeyboardInputState keyboardState = state.KeyboardState;
            //Movement code
            Vector3 playerPositionAdd = Vector3.Zero;

            int punchAnimationIndex = animationController.AnimationIndex("Punch");
            int stanceAnimationIndex = animationController.AnimationIndex("Stance");
            int walkAnimationIndex = animationController.AnimationIndex("Walk");
            
            if (keyboardState.KeyState.Space.OnPressed)
            {
                Punch.StopAnimation();
                Punch = animationController.PlayAnimation(punchAnimationIndex);                            
            }
            
            if (keyboardState.KeyState.A.OnPressed )
            {
                Stance.StopAnimation();
                Walk.StopAnimation();
                Walk = animationController.PlayLoopingAnimation(walkAnimationIndex); 
                worldMatrix = leftFacing * Matrix.CreateTranslation(Position);
            }
            if (keyboardState.KeyState.A.IsDown )
            {
                playerPositionAdd.Y -= 0.2f;
            }
            else if (keyboardState.KeyState.A.OnReleased )
            {
               Walk.StopAnimation();
               Stance.StopAnimation();
               Stance = animationController.PlayLoopingAnimation(stanceAnimationIndex);
            }

            if (keyboardState.KeyState.D.OnPressed )
            {
                Stance.StopAnimation();
                Walk.StopAnimation();
                Walk = animationController.PlayLoopingAnimation(walkAnimationIndex); 
                worldMatrix = rightFacing * Matrix.CreateTranslation(Position);
            }
            if (keyboardState.KeyState.D.IsDown )
            {
                playerPositionAdd.Y -= 0.2f;               
            }
            else if (keyboardState.KeyState.D.OnReleased)
            {
                Walk.StopAnimation();
                Stance.StopAnimation();
                Stance = animationController.PlayLoopingAnimation(stanceAnimationIndex);
            }

            worldMatrix = Matrix.CreateTranslation(playerPositionAdd) * worldMatrix;
            return UpdateFrequency.FullUpdate60hz;
        }
I'm still getting a glitch on the key releases.

Aug 2, 2010 at 11:52 PM
I've tried a few different ways, but I cant tweak this code into playing the stance animation properly. It needs to be playing by default, but when the game begins, the model is in the bind pose. Also when Ihit the punch key, there is a weird visual glitch, a frame or two where the arms look warped. Finally, as I said above, when I release the key, the model goes back into it's bind pose. I've observed it under two different conditions, If I press the punch key before any other, the model goes from punch, back to bind pose, but if I press a movement key, it goes pucnh, briefly into bind pose, then plays the stance animation. Does this mean the weighting code I've posted above is wrong?
Aug 10, 2010 at 7:24 PM
Edited Aug 10, 2010 at 7:28 PM

I'm worried about how this code is going to grow, I'm planning on adding a lot of animations, they're central to the game.
Ideally the system needs to be able to support multiple animations, with some way of implementing combos, as it is a fighting game.

with that in mind, is the method to set up weights I use above a good long term option?

I've also noticed that repeatedly pressing the two direction buttons causes the model to lock into the stance animation, rather that the correct walk animation.