Jump to content
  • entries
    10
  • comments
    21
  • views
    22,128

Moving without foot sliding


Chris Paulson

6,009 views

 Share

When your NPC moves it may move at different speeds, especially if you are using some sort of steering. This presents the problem of what speed should you play your walk/run animations. My solution is to analyse the animation and see how far the foot travels from start frame to end frame and when animating play the correct amount of frame(s) for the distance.

 

Here’s the code:-

 

#include "anim.h"

void animItem::calcAnimDist( TEntity e, TEntity bone )
{
TVec3 startPos;
TVec3 newPos;
TVec3 prevPos;
TVec3 rot;
float totalZ = 0;
float totalX= 0;

if (!bone) return;

rot = EntityRotation(e,1);
RotateEntity(e,Vec3(0),1);
startPos = EntityPosition(bone,1);
for(int i=startFrame; i<endFrame; i++)
{
	prevPos = EntityPosition(bone,1);
	Animate( e, i );
	newPos = EntityPosition(bone,1);
	totalZ += abs(newPos.Z - prevPos.Z);
	totalX += abs(newPos.X - prevPos.X);
}
RotateEntity(e,rot,1);
distanceZ = totalZ*2;
distanceX = totalX*2;
perFrameZ = totalZ / frameCount;
perFrameX = totalX / frameCount;

maxSpeed = perFrameZ / 1.8;
minSpeed = perFrameZ / 3.6;
}

 

 

Here’s it’s header file, it has loads more stuff in that I will touch on in later blog entries.

 

#ifndef ANIM_H
#define ANIM_H

#include <string>
#include <map>
#include <vector>
#include <algorithm>
#include "engine.h"
#include "actor/include/animbody.h"

using namespace std;

typedef enum animDirection
{
ANIM_OTHER
};

class keyFrame
{
int parentFrame;
int childFrame;
};

typedef enum animType
{
ANIM_NONE		= 0,
ANIM_IDLE		= 1,
ANIM_WALK		= 2,
ANIM_RUN		= 4,
ANIM_CROUCH		= 8,
ANIM_TURN		= 16,
ANIM_STRAFE		= 32,
ANIM_RIFLE		= 64,
ANIM_PISTOL		= 128,
ANIM_DEATH		= 256,
ANIM_FORWARD	= 512,
ANIM_BACKWARD	= 1024,
ANIM_PAIN		= 2048,
ANIM_LEFT		= 4096,
ANIM_RIGHT		= 8192,
ANIM_JUMP		= 16384
};

typedef enum animStatus
{
ANIM_DEFAULT,
ANIM_STARTING,
ANIM_PLAYING,
ANIM_STOPPING,
ANIM_STOPPED
};

class animItem
{
protected:
float distanceZ;
float distanceX;
   float rotationAngle;
map<std::string,keyFrame> keyFrames;
public:
animType animType;
std::string name;
int sequence;
int startFrame;
int endFrame;
   int frameCount;
float perFrameX;
float perFrameZ;
bool speedDepdent;
float maxSpeed;
float minSpeed;
animDirection direction;
animStatus m_status;

animItem() {};
   animItem( string pName, int pStart, int pEnd, animDirection pDirection = ANIM_OTHER );
~animItem() {};
inline int getFrameCount() { return frameCount; }
//void calcAnimDist( TEntity e, string boneName );
void calcAnimDist( TEntity e, TEntity bone );
};

typedef enum animPlayType
{
PLAY_REPEAT,
PLAY_ONCE,
PLAY_ONCE_RESET
};

class playingAnim
{
protected:
public:
animItem* myAnimItem;
float currentFrame;                                        // Zero based
float speed;
animStatus status;
animPlayType animType;
TEntity entity;
float transitionTime;
float startTime;
float stopTime;
int blendTime;
playingAnim() {};
playingAnim( animItem* pAnim, float pTransitionTime = 0, float pSpeed = 0, animPlayType pAnimType = PLAY_REPEAT): 
	currentFrame(0), blendTime(0), stopTime(0), startTime(0)
{
	myAnimItem = pAnim;
	speed = pSpeed;
	animType = pAnimType;
	transitionTime = pTransitionTime;
	status = ANIM_STARTING;
}
inline void setStartTime( float pTime ) { startTime = pTime; }
inline void setStopTime( float pTime ) { stopTime = pTime; }
inline void setEntity( TEntity ent ) { entity = ent; }
inline void setCurrentFrame( float pFrame ) { currentFrame = pFrame; }
inline float getStopTime() { return stopTime; }
inline float getStartTime() { return startTime; }
inline float getTransitionTime() { return transitionTime; }
inline animStatus getStatus() { return status; }
inline animPlayType getAnimType() { return animType; }
};

class entityAnimation
{
protected:
map<string,animItem*> items;
map<string,playingAnim*> animQueue;
playingAnim* activeItem;
TEntity entity;
public:
entityAnimation( TEntity pEnt );
//~entityAnimation();
animItem* addItem( string pName, int pStart, int pEnd );
playingAnim* start( string pName, float pTranTime = 0, float animSpeed = 1, animPlayType pAnimType = PLAY_REPEAT, string entityName = "");
void stop( playingAnim* anim, float transition);
void stopAll( float time);
void update( float dist = 0);

bool isPlayingAnimName( string pName );
bool animationActive( string pName);
bool animationStopped( string pName );

inline float startBlend( playingAnim* play );
inline float stopBlend( playingAnim* play );
};

class locomotion: public entityAnimation
{
public:
animType currentAnimType;
bool crouching;
bool holdingPistol;
bool holdingRifle;
bool movingForward;

bodyParts* m_bodyParts;
bodyControl* m_bodyControl;
float swapTime;

void update( float dist = 0);
animItem* locomotion::findItemForSpeed(  float dist );
animItem* locomotion::findItemForType(  animType pAnimType );
bool readAnimFile( string pFile );
void setCrouching( bool pCrouch = true );
void setHoldingPistol( bool pPistol = true );
void setHoldingRifle( bool pRifle = true );
void setMovingForward( bool pForward = true );

locomotion( TEntity e);
void render( TCamera cam);
};

#endif

 Share

8 Comments


Recommended Comments

Good job. I'm always impressed by your work, especially your recast integration work.

 

I was working on my own implementation of using animation data to drive the entity movement where everything is controlled by animation speed. This works for me because I'm using motion capture data that already has fairly well synchronized translation of the root bone translation and foot placement. The code is messy so I won't post it yet, but the basic idea is that every update:

 

Check how much time has passed since the last update.

Use this time to determine how many frames have passed.

Animate to the frame that was used last update, save the x,z position of the defined bone.

Animate to the frame that should be used this update, save the x,z position of the defined bone.

Calculate the difference between the positions.

Set the position of the model to the current model position + the difference calculated above.

Set the x,z position of the root bone to the x,z position of the model.

 

So you drive everything by setting the animation speed (last_frame + time_difference/scaling_factor) where a higher scaling_factor gives a slower playback speed. The movement speed is always in sync with the animation speed. It can be extended to properly handle blending as well by storing the different animations used and blend factor as well as the last frame. It handles lurching/non-linear movement very well, which is a bonus for some of my animations. And it ensures that the collision shape for the model is always at the same position (if not rotation) as the visual representation. Plus it's only a dozen or so Lua lines. Of course this requires that your animation has movement in it and is not locked to the same x/z location like most that you can purchase.

 

I'd love to see a simple to use system that would allow you to choose (in the editor) between:

Using animation driven movement/rotation for animations with translation data

Using your method of analyzing foot travel for animations w/o translation data

Manually moving object

 

Doing this on a per animation or per state basis would be cool.

 

Now I just have to make it account for rotation so instead of rotating your model, you could play X number of frames of a rotation animation to get it to the rotation you would like (mostly useful for smoothly turning >=90 degrees). I should also try pre-calculating and caching the x/z and rotation deltas per frame as I don't know how much overhead is involved with doing two calls to Animate each update per object. Moving it to C/C++ instead of Lua would help a little I'm sure.

 

Anyways I'm really looking forward to what you come up w/ next (especially in the recast integration area). Think I'll start working on IK foot placement for smooth stair/hill climbing next.

Link to comment

Thanks for the comments, you anim system sounds better than mine.

 

 

Anyways I'm really looking forward to what you come up w/ next (especially in the recast integration area). Think I'll start working on IK foot placement for smooth stair/hill climbing next.

 

I'd be interested in how you do this and how this works out, I get a feeling it something that needs to be tackled if you don't want "floating" NPCs. Check out:-

 

http://udn.epicgames.com/Three/UsingSkeletalControllers.html

Link to comment

Now I just have to make it account for rotation so instead of rotating your model, you could play X number of frames of a rotation animation to get it to the rotation you would like (mostly useful for smoothly turning >=90 degrees). I should also try pre-calculating and caching the x/z and rotation deltas per frame as I don't know how much overhead is involved with doing two calls to Animate each update per object. Moving it to C/C++ instead of Lua would help a little I'm sure.

 

I've done this before, one thing to look out for is the animation may rotate but you model isn't. This means at the end of the rotation anim you have to rotate the model the correct amount to match the animation otherwise the model will "snap" back the rotation.

Link to comment
Thanks for the comments, you anim system sounds better than mine.

 

Not really, it's just good if you have non-normalized data or lurching/back and forth animations. Yours is still better for all the normalized animations out there. Having both available is a good thing.

 

I've done this before, one thing to look out for is the animation may rotate but you model isn't. This means at the end of the rotation anim you have to rotate the model the correct amount to match the animation otherwise the model will "snap" back the rotation.

 

Right, it's basically the exact same thing I'm doing for the movement, if you don't keep the model position in sync w/ the animation then it'll snap back to it's starting frame when it loops.

 

Yeah, the UDK article and the Locomotion system in Unity is what I want to duplicate for foot placement. Not sure if I'll actually get it done any time soon though as I'm working on a top down RTS type game so it won't help me at all ;) Tried a couple quick methods but I need to rethink my math and global/local conversions. Main problem I ran into was that the bones sometimes have some odd rotations applied in my animations, so they weren't going where I thought they should when I rotated them. I'd prefer it work w/o having to have really clean animation data so I'll think of a more robust solution.

 

I did end up adding the pre-caching of movement deltas, couldn't get LODs to work properly without it. I'll post a cheesy video showing it in action sometime.

Link to comment

Have you got a link to the unity location system description?

 

I've seen your video - very nice. I did have one question though, why is the physics mesh so out of place relative to the visible mesh?

 

I've come to the conclusion that the physics mesh is next to useless on animation models, it would be better to create some sort of bounding physics volume.

Link to comment

Have you got a link to the unity location system description?

 

I've seen your video - very nice. I did have one question though, why is the physics mesh so out of place relative to the visible mesh?

 

I've come to the conclusion that the physics mesh is next to useless on animation models, it would be better to create some sort of bounding physics volume.

 

Yeah, his physics mesh isn't used at all, I just use a cylinder around it. Did you see the second video w/ him actually colliding w/ stuff? The reason his mesh is so far off is just because of how I exported him from the modeling program. He's off the origin in the frames I'm using. The code accounts for that as far as the animation, model position and collision cylinder, but the editor shows the auto-generated one off by that amount. He's also kinematic, so collisions on him are off, but his Lua script creates a collision cylinder that is updated to be centered around him every Update. This keeps him from being affected by collisions (which caused really jerky movements and sent him flying away sometimes) but allows him to affect other things.

 

You can download the Unity locomotion system at http://unity3d.com/support/resources/unity-extensions/locomotion-ik and view it in action at

Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...