Skip to content

Instantly share code, notes, and snippets.

@JSandusky
Created September 30, 2015 01:55
Show Gist options
  • Save JSandusky/340b09e9d104bae4c16b to your computer and use it in GitHub Desktop.
Save JSandusky/340b09e9d104bae4c16b to your computer and use it in GitHub Desktop.
Animation motion controller
//=====================================
// MotionControl.as
//
// Controls animations based on the movements of a node:
// Automatically switches between strafing/forward/reverse/run/fall/jump
//=====================================
//=====================================
// class: AnimSet
//
// Helper class that let's us quickly swap out entire animation sets, mostly for "state" purposes
//=====================================
class AnimSet
{
String walkForward;
String walkBackward;
String walkLeft;
String walkRight;
String fall;
String jump;
String run;
String idle;
void Serialize(Serializer& stream)
{
stream.WriteString(walkForward);
stream.WriteString(walkBackward);
stream.WriteString(walkLeft);
stream.WriteString(walkRight);
stream.WriteString(fall);
stream.WriteString(jump);
stream.WriteString(run);
stream.WriteString(idle);
}
void Deserialize(Deserializer& stream)
{
walkForward = stream.ReadString();
walkBackward = stream.ReadString();
walkLeft = stream.ReadString();
walkRight = stream.ReadString();
fall = stream.ReadString();
jump = stream.ReadString();
run = stream.ReadString();
idle = stream.ReadString();
}
}
//=====================================
// class: MotionController
//
// Controls a Node's AnimationController based on
// how the position of the node changes
// Selects and blends between Cross direction walks and running
// Running + jumping
//=====================================
class MotionController : ScriptObject
{
private Vector3 lastPosition;
private Quaternion lastRotation;
private Vector3 relativeMotion;
float animSpeed;
String walkForward;
String walkBackward;
String walkLeft;
String walkRight;
String fall;
String jump;
String run;
String idle;
int DefaultLayer = 0;
float runThreshold;
float moveThreshold;
float fallThreshold;
float jumpThreshold;
float rotationOffset = 0;
bool active = true;
private float nextUpdate = 0.0f;
AnimSet@ GetAnimSet()
{
AnimSet@ ret = AnimSet();
ret.walkForward = walkForward;
ret.walkBackward = walkBackward;
ret.walkLeft = walkLeft;
ret.walkRight = walkRight;
ret.fall = fall;
ret.jump = jump;
ret.run = run;
ret.idle = idle;
return ret;
}
void SetAnimSet(AnimSet@ anims)
{
walkForward = anims.walkForward;
walkBackward = anims.walkBackward;
walkLeft = anims.walkLeft;
walkRight = anims.walkRight;
fall = anims.fall;
jump = anims.jump;
run = anims.run;
idle = anims.idle;
}
void FixedUpdate(float td)
{
if (!active)
return;
nextUpdate -= td;
if (nextUpdate > 0)
return;
nextUpdate = 0.02;
Vector3 dist = node.worldPosition - lastPosition;
lastPosition.x = node.worldPosition.x;
lastPosition.y = node.worldPosition.y;
lastPosition.z = node.worldPosition.z;
const float verticalSpeed = 1 * dist.y * td;
dist.y = 0;
float linearSpeed = 1 * dist.length * td;
if (linearSpeed < 0)
linearSpeed = -linearSpeed;
const float pi3rd = M_PI/3.0;
const float pi2 = M_PI*2.0;
//we're not sufficiently moving vertical
if (verticalSpeed > -fallThreshold && verticalSpeed < jumpThreshold)
{
float direction = Atan2( -dist.x, -dist.z ) - (node.worldRotation.eulerAngles.y + rotationOffset);
direction *= M_DEGTORAD;
// Normalize direction angle to [-PI, +PI]
// To get less if cases
if( direction > M_PI )
direction = direction - pi2;
if( direction < -M_PI )
direction = pi2 + direction;
if (linearSpeed > 0 && linearSpeed > moveThreshold)
{
if (direction > pi3rd && direction < 2*pi3rd)
{ //play strafe left
if (walkLeft.length > 0 && walkLeft.length > 0)
PlayAnimation(walkLeft, false, false, true, DefaultLayer);
else if (walkRight.length > 0 && walkRight.length > 0) //reverse right walk to use as left walk
PlayAnimation(walkRight,true, false, true, DefaultLayer);
}
else if (direction > -2*pi3rd && direction < -pi3rd)
{ //play strafe right
if (walkRight.length > 0 && walkRight.length > 0)
PlayAnimation(walkRight, false, false, true, DefaultLayer);
else if (walkLeft.length > 0 && walkLeft.length > 0) //reverse left walk to use as right walk
PlayAnimation(walkLeft,true, false, true, DefaultLayer);
}
else if (direction >= 2*pi3rd || direction <= -2*pi3rd)
{ //play walk backwards
if (walkBackward.length > 0 && walkBackward.length > 0)
PlayAnimation(walkBackward, false, false, true, DefaultLayer);
else //reverse forward anim to walk backward
PlayAnimation(walkForward,true, false, true, DefaultLayer);
}
else if (linearSpeed > runThreshold && run.length > 0)
{ //play run forward
PlayAnimation(run, false, false, true, DefaultLayer);
}
else
{ //play move forward
PlayAnimation(walkForward, false, false, true, DefaultLayer);
}
}
else if (idle.length > 0)
{
PlayAnimation(idle);
}
}
else if (-verticalSpeed > fallThreshold && fall.length > 0)
{
//play fall animation
PlayAnimation(jump, false, true, true, DefaultLayer);
}
else if (verticalSpeed > jumpThreshold && jump.length > 0)
{
//play jump animation
PlayAnimation(fall, false, true, false, DefaultLayer);
}
else if (idle.length > 0)
{
PlayAnimation(idle);
}
}
private void PlayAnimation(const String&in animName, bool reverse = false, bool restart = false, bool loop = true, int layer = 0)
{
AnimationController@ animCtrl = node.GetComponent("AnimationController");
if (animCtrl !is null)
{
const float animDir = reverse ? -1.0 : 1.0;
if (restart)
{
animCtrl.Play(animName, layer, loop, 0.2);
animCtrl.SetTime(animName, 0.0); // play from beginning
}
else
animCtrl.PlayExclusive(animName, layer, loop, 0.2);
animCtrl.SetSpeed(animName, animSpeed*animDir);
}
else
{
String msg = "Missing Animation controller in node for MotionController script: ";
msg += node.id;
log.Error(msg);
}
}
// Update our history of positions
void TransformChanged()
{
//lastPosition = node.position;
lastRotation = node.rotation;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment