Code/PlayerControllerPlus/Modes/MoveModePlus.Animation.cs
using System;
namespace Sandbox.MovementPlus;
partial class MoveModePlus
{
public virtual void UpdateAnimator( SkinnedModelRenderer renderer )
{
OnUpdateAnimatorVelocity( renderer );
OnUpdateAnimatorState( renderer );
OnUpdateAnimatorLookDirection( renderer );
UpdateRotationSpeed( renderer );
OnRotateRenderBody( renderer );
}
Vector3.SmoothDamped smoothedMove = new Vector3.SmoothDamped( 0, 0, 0.5f );
Vector3.SmoothDamped smoothedWish = new Vector3.SmoothDamped( 0, 0, 0.5f );
Vector3.SmoothDamped smoothedSkid = new Vector3.SmoothDamped( 0, 0, 0.5f );
protected virtual void OnUpdateAnimatorVelocity( SkinnedModelRenderer renderer )
{
var rot = renderer.WorldRotation;
var vel = Controller.Velocity;
var wishVel = Controller.WishVelocity;
// skid
{
var skidAmount = 0.5f;
var pushSkidAmount = 1.0f;
var skidDelay = 0.2f;
var smoothed = vel;
{
smoothedMove.Target = smoothed;
smoothedMove.SmoothTime = skidDelay;
smoothedMove.Update( Time.Delta );
smoothed = smoothedMove.Current;
}
var skid = (smoothed - vel) * skidAmount;
skid = Vector3.Lerp( skid, vel * pushSkidAmount, wishVel.Length.Remap( 100, 0, 0, 1 ) );
smoothedSkid.Target = skid;
smoothedSkid.SmoothTime = 0.5f;
smoothedSkid.Update( Time.Delta );
skid = smoothedSkid.Current;
skid = GetLocalVelocity( rot, skid );
renderer.Set( "skid_x", (skid.x / 400.0f) );
renderer.Set( "skid_y", (skid.y / 400.0f) );
}
// move the legs
{
var smoothed = (Controller.ExtendedFeaturesEnabled && Controller.UseWorldVelocityForAnimation) ? vel : wishVel;
{
smoothedWish.Target = smoothed;
smoothedWish.SmoothTime = Controller.ExtendedFeaturesEnabled ? Controller.AnimationSmoothTime : 0.6f;
smoothedWish.Update( Time.Delta );
smoothed = smoothedWish.Current;
smoothed = ApplyDeadZone( smoothed, 10 );
}
smoothed = GetLocalVelocity( rot, smoothed );
renderer.Set( "move_direction", GetAngle( smoothed ) );
renderer.Set( "move_speed", smoothed.Length );
renderer.Set( "move_groundspeed", smoothed.WithZ( 0f ).Length );
renderer.Set( "move_x", smoothed.x );
renderer.Set( "move_y", smoothed.y );
renderer.Set( "move_z", smoothed.z );
}
// the player's wish
{
var local = GetLocalVelocity( rot, wishVel );
renderer.Set( "wish_direction", GetAngle( local ) );
renderer.Set( "wish_speed", wishVel.Length );
renderer.Set( "wish_groundspeed", wishVel.WithZ( 0f ).Length );
renderer.Set( "wish_x", local.x );
renderer.Set( "wish_y", local.y );
renderer.Set( "wish_z", local.z );
}
}
#region OnUpdateAnimatorVelocity Helpers
private static Vector3 ApplyDeadZone( Vector3 velocity, float minimum )
{
return velocity.IsNearlyZero( minimum ) ? 0f : velocity;
}
private static Vector3 GetLocalVelocity( Rotation rotation, Vector3 worldVelocity )
{
var forward = rotation.Forward.Dot( worldVelocity );
var sideward = rotation.Right.Dot( worldVelocity );
return new Vector3( forward, sideward, worldVelocity.z );
}
private static float GetAngle( Vector3 localVelocity )
{
return MathF.Atan2( localVelocity.y, localVelocity.x ).RadianToDegree().NormalizeDegrees();
}
#endregion
float _smoothedDuck;
protected virtual void OnUpdateAnimatorState( SkinnedModelRenderer renderer )
{
renderer.Set( "sit", 0 );
renderer.Set( "b_swim", Controller.IsSwimming );
renderer.Set( "b_climbing", Controller.IsClimbing );
renderer.Set( "b_grounded", Controller.IsOnGround || Controller.IsClimbing );
var duck = Controller.Headroom.Remap( 25, 0, 0, 0.5f, true );
if ( Controller.IsDucking )
{
duck *= 3.0f;
duck += 1.0f;
}
if ( Controller.ExtendedFeaturesEnabled )
{
_smoothedDuck = _smoothedDuck.LerpTo( duck, Time.Delta * Controller.CrouchAnimationSpeed );
}
else
{
_smoothedDuck = duck;
}
renderer.Set( "duck", _smoothedDuck );
}
protected virtual void OnUpdateAnimatorLookDirection( SkinnedModelRenderer renderer )
{
var eyesForward = Controller.EyeTransform.Forward;
renderer.SetLookDirection( "aim_eyes", eyesForward, Controller.AimStrengthEyes );
renderer.SetLookDirection( "aim_head", eyesForward, Controller.AimStrengthHead );
renderer.SetLookDirection( "aim_body", eyesForward, Controller.AimStrengthBody );
}
protected virtual void OnRotateRenderBody( SkinnedModelRenderer renderer )
{
var velocity = Controller.WishVelocity.WithZ( 0 );
var oldRotation = renderer.WorldRotation;
bool unbound = Controller.ExtendedFeaturesEnabled && Controller.DisableCameraBinding;
Rotation targetAngle;
if ( unbound )
{
if ( velocity.Length > 10 )
targetAngle = Rotation.LookAt( velocity.Normal, Vector3.Up );
else
targetAngle = renderer.WorldRotation;
}
else
{
var eyeAngles = Controller.EyeTransform.Rotation.Angles();
targetAngle = Rotation.FromYaw( eyeAngles.yaw );
}
var rotateDifference = renderer.WorldRotation.Distance( targetAngle );
if ( rotateDifference > Controller.RotationAngleLimit )
{
var delta = 0.999f - Controller.RotationAngleLimit / rotateDifference;
var newRotation = Rotation.Lerp( renderer.WorldRotation, targetAngle, delta );
renderer.WorldRotation = newRotation;
}
if ( velocity.Length > 10 )
{
renderer.WorldRotation = Rotation.Slerp( renderer.WorldRotation, targetAngle,
Time.Delta * 2.0f * Controller.RotationSpeed * velocity.Length.Remap( 0, 100 ) );
}
AddRotationSpeed( oldRotation, renderer.WorldRotation );
}
private const float RotationSpeedUpdatePeriod = 0.1f;
private float _animRotationSpeed;
private TimeSince _timeSinceRotationSpeedUpdate;
private void AddRotationSpeed( Rotation oldRotation, Rotation newRotation )
{
var oldYaw = oldRotation.Angles().yaw;
var newYaw = newRotation.Angles().yaw;
var deltaYaw = MathX.DeltaDegrees( newYaw, oldYaw );
_animRotationSpeed = (_animRotationSpeed + deltaYaw).Clamp( -90f, 90f );
}
private void UpdateRotationSpeed( SkinnedModelRenderer renderer )
{
if ( _timeSinceRotationSpeedUpdate < RotationSpeedUpdatePeriod ) return;
renderer.Set( "move_rotationspeed", _animRotationSpeed * 5f );
_timeSinceRotationSpeedUpdate = 0f;
_animRotationSpeed = 0f;
}
}