Game Development Community

Player Running Animation in Engine

by Jess Bermudes · in Torque Game Engine · 11/01/2008 (6:52 am) · 14 replies

I've been looking around through the player class, and I see how the animations are selected for things like jumping, falling, etc. But I don't see any code that says "if the player is running, play the running animation". I'd like to disable it for certain conditions. Any ideas?

#1
11/01/2008 (7:24 am)
Client/scripts/default.bind.cs ?
#2
11/01/2008 (7:32 am)
Unfortunately, it seems that all default bind does is tell the engine that you want to move, then the engine takes that move request and animates you moving.

The closest I can find is Player::pickActionAnimation() in the engine, but I don't understand how it picks the running animation.
#3
11/01/2008 (9:08 am)
Hi.


If my recollection of the earlier engine versions still apply, the player-class has only single animation for moving forward and its playing speed is altered according to the movement speed (vector). E.g. when you move slowly, the animation is run at 50% speed but if you run at full speed also the animation plays at 100% speed.

There are few different ways to approach disabling the running, but it would depend on your specific needs. There are few resources that will separate walking and running ("run on shift key" or something) in context of velocity while other resources add another animation to have different animations for walking and running (i.e. different speeds) as simply playing the running animation at low speed looks kind of funny.
#4
11/01/2008 (5:52 pm)
Well, here's my attempt at a solution but it doesn't seem to work. What I wanted to happen was that if the player is grinding on a railing (like when skateboarding), then not to play the running animation, but to play my own animation. In fact, for testing right now all I'd really like to happen is for the root animation to play so it just looks like he's sliding along. I've checked my conditions to make sure that the isGrinding() returns the correct values in the proper contexts, but it seems that somehow the player is still running, not standing still.

So basically, what I'd like to have happen is when the player is in this special grinding state, is to just stand still in the root animation and move along standing still.

player.cpp
void Player::pickActionAnimation()
{
   // Only select animations in our normal move state.
   if (mState != MoveState || mDamageState != Enabled)
      return;

   if (isMounted())
   {
      // Go into root position unless something was set explicitly
      // from a script.
      if (mActionAnimation.action != PlayerData::RootAnim &&
          mActionAnimation.action < PlayerData::NumTableActionAnims)
         setActionThread(PlayerData::RootAnim,true,false,false);
      return;
   }

   bool forward = true;
   U32 action = PlayerData::RootAnim;
   
   // Jetting overrides the fall animation condition
   if (mJetting)
   {
      // Play the jetting animation
      action = PlayerData::JetAnim;
   }
   else if (mFalling)
   {
      // Not in contact with any surface and falling
      action = PlayerData::FallAnim;
   }
   else if(isGrinding())
   {
	   action = PlayerData::RootAnim; // TODO: replace with GrindAnim
   }
   else
   {
      if (mContactTimer >= sContactTickTime) 
	  {
         // Nothing under our feet
         action = PlayerData::RootAnim;
      }
      else
      {
	     if(isGrinding())
		 {
			Con::errorf(ConsoleLogEntry::General, "this shouldn't happen because grinding was already checked above", 0);
		 }
         // Our feet are on something
         // Pick animation that is the best fit for our current velocity.
         // Assumes that root is the first animation in the list.
         F32 curMax = 0.1f;
         VectorF vel;
         mWorldToObj.mulV(mVelocity,&vel);
         for (U32 i = 1; i < PlayerData::NumMoveActionAnims; i++)
         {
            PlayerData::ActionAnimation &anim = mDataBlock->actionList[i];
            if (anim.sequence != -1 && anim.speed) 
			{
               F32 d = mDot(vel, anim.dir);
               if (d > curMax)
               {
                  curMax = d;
                  action = i;
                  forward = true;
               }
               else
               {
                  // Special case, re-use slide left animation to slide right
                  if (i == PlayerData::SideLeftAnim && -d > curMax)
                  {
                     curMax = -d;
                     action = i;
                     forward = false;
                  }
               }
            }
         }
      }
   }
   setActionThread(action,forward,false,false);
}
#5
11/01/2008 (11:39 pm)
for (U32 i = 1; i < PlayerData::NumMoveActionAnims; i++)
         {
            PlayerData::ActionAnimation &anim = mDataBlock->actionList[i];
            if (anim.sequence != -1 && anim.speed) 
			{
               F32 d = mDot(vel, anim.dir);
               if (d > curMax)
               {
                  curMax = d;
                  action = i;
                  forward = true;
               }
               else
               {
                  // Special case, re-use slide left animation to slide right
                  if (i == PlayerData::SideLeftAnim && -d > curMax)
                  {
                     curMax = -d;
                     action = i;
                     forward = false;
                  }


                  if (mGrinding&& mVelocity.len() > 3.0f) 
                  {
		            action = PlayerData::GrindAnim;
                  }
               }
            }
#6
11/02/2008 (12:58 am)
Picaso, thanks for the code, it's helping me understand what's going on a little better. Unfortunately, that modification doesn't seem to work. I actually do not have a GrindAnim at this point and am thus using the RootAnim, but that nor Land or Jump worked.

What is really confusing me is that in my original code example, this is the structure of the function:

void pickActionAnimation(...)
{
	if(falling)
	{
		action = fall;
	}
	else if(grinding)
	{
		action = root or land or jump or grind or whatever works;
	}
	else
	{
		if(grinding)
		{
			complain to console
		}

		if(nothing under our feet)
		{
			action = root
		}
		else
		{
			for(all the animations)
			{
				look for the one whose velocity and direction match the player's
			}
		}

	}

	setActionThread(action,...);
}


And by doing some testing, I found that my first grinding check worked and the action was supposedly set even though it's not showing and the player is still somehow running. Therefore, that must mean that it's somehow getting to that for loop. But yet my little catch of sending an error to the console isn't happening, which means that it's not actually getting to the for loop, yet somehow the player is still getting the instruction to do the running animation.

Is there somewhere else that this could be overridden from?
#7
11/02/2008 (2:48 am)
You need to register mGrinding within the input in my example. Do it in updatemove (yes, you need the move data).
Do it that way: if grinding(your way) and moving(move->y>0) then mGrinding = true

You need GrindAnim. That can be done in player.h
GrindAnim should not be dependent on dir.
If not, setactionthread overloads the grind animation within your velocity with Run.

You need to reorder your animation list, according to your constructor/ActionAnimationList/enums in player.h
#8
11/02/2008 (3:33 am)
Am I correct in thinking that as long as the for loop does not execute, the player will not do a running animation?

Also, I'm not sure what you mean by "registering with the input"

- Thanks!
#9
11/02/2008 (8:23 am)
If you disable the loop, you can play your own way.
But i think using the velocity is a good idea of playing basic dir animations as root/idle/walk/run/fast_run/side .....

Your problem is not the loop.
Your problem is to understand how exactly the animation playback works.

www.garagegames.com/mg/forums/result.thread.php?qt=73223
Read Plastic Gem animation tutorials.
Read the resources of climbing,jetting,swimming,walking,fast running ....

All is well explained.
#10
11/02/2008 (4:49 pm)
Wow, that thread was very helpful.

So if I understand correctly, pickActionAnimation and updateMove are the ONLY 2 places that the running animation is dealt with?

I tried right now to put at the end of pickActionAnimation a if(mGrinding) action = PlayerData::RootAnim
and at the end of updateMove another if(mGrinding) mActionAnimation = PlayerData::RootAnim, but the player is still running. And I know those segments are being executed because I put a console printout on there too.

This is doesn't seem like it would be all that difficult, yet I don't know why it's giving me such a hard time.
#11
11/03/2008 (8:18 am)
Your animation seems overlapped.
When skateboarding, your player is in air, so the contact timer can turn on fall or NULL animation.
You can play animations directly with setactionthread in updatemove.

How do you check skateboarding?
Oncollision, container ?
Is your player mounted ?
#12
11/03/2008 (8:28 am)
The game is similar to Jet Set Radio in that the player can "grind" on metal railings on the sides of staircases and such.

Currently what I have are meshes of rails (that have collision), and triggers that sit on top. When the player lands on such a trigger, I turn on a flag that is sent to the engine side (mGrinding), and on the script side I set the velocity of the player in the correct direction. The player is not mounted to anything, he's not even riding a skateboard, his player model has rollerskates on.

Therefore, what currently happens is that the player runs along the rail since his feet are touching the collision of the rail and thus counts as ground. What I would like to do is to turn off the running animation and play the Root animation instead (eventually I'll get a GrindAnim, but at this point I thought it would suffice to play the RootAnim).

My original solution was to attempt from the script side to playThread(0, "root"), or even setActionThread("root", true, true), but that didn't work. That is why I thought I should go into the engine and modify the ability to turn off the running animation at will (whenever my grinding variable has been set).
#13
11/03/2008 (9:04 am)
Your animation should not have direction. And should not be blend animation.
Now do not touch the root animation.
{ "root" },       // RootAnim

   { "run",  { 0.0f, 1.0f, 0.0f } },       // RunForwardAnim
   { "back", { 0.0f, -1.0f, 0.0f } },    // BackBackwardAnim
   { "side", { -1.0f, 0.0f, 0.0f } },     // SideLeftAnim


   { "grind" },   //GrindAnim
........ rest of code


Add the animation in player.h :
enum {
      // *** WARNING ***
      // These enum values are used to index the ActionAnimationList
      // array instantiated in player.cc
      // The first five are selected in the move state based on velocity
      RootAnim,
      RunForwardAnim,
      BackBackwardAnim,
      SideLeftAnim,
      GrindAnim,
..rest of code

Check the TSShapeConstructor. Follow the same order.

Now play the animation in updatemove.

if(mGrinding && mActionAnimation.action == PlayerData::RunForwardAnim && move->y && getControllingClient() && mActionAnimation.atEnd && mVelocity.len() > 0.0f)

{
    	   action = PlayerData::GrindAnim; 
}


Try to play with setactionthread also.

Problems occure when you have two or more animations in a moment.
Only blend animations can play that way. For the other ones you need a synchronization.
If on some reason there is a hard problem in playback synchronization, GarageGames did also a preority of animations and animation triggers.
#14
11/03/2008 (2:17 pm)
To be honest, I do not have a Grind Animation at this time, what I would like to do is to play the Root animation, but I guess that's not possible since the Root Animation has a direction, right?