|
|
|
public partial class AnimationController : Node3D, IInitalizable<Player>
|
|
|
|
{
|
|
|
|
[Export] public Skeleton3D Skeleton { get; set; }
|
|
|
|
[Export] public BoneAttachment3D RootBone { get; set; }
|
|
|
|
|
|
|
|
// Contains all the bones in the skeleton, keyed by name (e.g. "LowerArm_L").
|
|
|
|
Dictionary<string, BoneAttachment3D> _bones = [];
|
|
|
|
|
|
|
|
// Whether the player's body is currently turning to match up with the camera rotation.
|
|
|
|
bool _isTurning = false;
|
|
|
|
|
|
|
|
// Current amount the body is turned due to walking sideways.
|
|
|
|
float _bodyYaw = 0.0f;
|
|
|
|
|
|
|
|
Player _player;
|
|
|
|
Transform3D _cameraDefaultTransform;
|
|
|
|
AnimationTree _animTree;
|
|
|
|
Animation _walkForwardAnim;
|
|
|
|
Animation _walkBackwardAnim;
|
|
|
|
public void Initialize(Player player)
|
|
|
|
{
|
|
|
|
_player = player;
|
|
|
|
_cameraDefaultTransform = _player.Camera.Camera.Transform;
|
|
|
|
|
|
|
|
_animTree = GetNode<AnimationTree>("AnimationTree");
|
|
|
|
_walkForwardAnim = _animTree.GetAnimation("walk_forward");
|
|
|
|
_walkBackwardAnim = _animTree.GetAnimation("walk_backward");
|
|
|
|
|
|
|
|
void AddBone(BoneAttachment3D bone)
|
|
|
|
{ bone.OverridePose = true; _bones[bone.Name] = bone; }
|
|
|
|
AddBone(RootBone);
|
|
|
|
foreach (var child in RootBone.FindChildren("*", "BoneAttachment3D"))
|
|
|
|
AddBone((BoneAttachment3D)child);
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void _Process(double delta)
|
|
|
|
{
|
|
|
|
ResetTransforms();
|
|
|
|
HandleTurning(delta);
|
|
|
|
HandleLookingAnimation(delta);
|
|
|
|
HandleWalkingAnimation(delta);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ResetTransforms()
|
|
|
|
{
|
|
|
|
foreach (var bone in _bones.Values)
|
|
|
|
bone.Transform = Skeleton.GetBonePose(bone.BoneIdx);
|
|
|
|
_player.Camera.Camera.Transform = _cameraDefaultTransform;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HandleTurning(double delta)
|
|
|
|
{
|
|
|
|
const float TurnBegin = 60.0f; // Start turning when camera is rotated this much.
|
|
|
|
const float TurnEnd = 5.0f; // Stop turning when body is this close to camera rotation.
|
|
|
|
const float TurnSpeed = 6.0f;
|
|
|
|
|
|
|
|
var yaw = _player.Camera.CurrentYaw; // Camera yaw relative to player yaw.
|
|
|
|
var isMoving = _player.Movement.RealMoveSpeed > 0.01f;
|
|
|
|
_isTurning = isMoving || (Abs(yaw) > DegToRad(TurnBegin));
|
|
|
|
|
|
|
|
if (_isTurning) {
|
|
|
|
var yawDelta = Sign(yaw) * Min(Abs(yaw), Abs(yaw) * TurnSpeed * (float)delta);
|
|
|
|
_player.Camera.CurrentYaw -= (float)yawDelta;
|
|
|
|
_player.RotateY(yawDelta);
|
|
|
|
|
|
|
|
if (Abs(_player.Camera.CurrentYaw) < DegToRad(TurnEnd))
|
|
|
|
_isTurning = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HandleLookingAnimation(double delta)
|
|
|
|
{
|
|
|
|
const float PitchFactorLowerBody = 0.05f;
|
|
|
|
const float PitchFactorUpperBody = 0.20f;
|
|
|
|
const float PitchFactorNeck = 0.25f;
|
|
|
|
const float PitchFactorHead = 0.35f;
|
|
|
|
|
|
|
|
var pitch = _player.Camera.CurrentPitch;
|
|
|
|
_bones["LowerBody"].RotateX(pitch * PitchFactorLowerBody);
|
|
|
|
_bones["UpperBody"].RotateX(-pitch * PitchFactorUpperBody);
|
|
|
|
_bones["Neck"].RotateX(-pitch * PitchFactorNeck);
|
|
|
|
_bones["Head"].RotateX(-pitch * PitchFactorHead);
|
|
|
|
_bones["UpperArm_L"].RotateX(pitch * (PitchFactorLowerBody + PitchFactorUpperBody) / 2);
|
|
|
|
_bones["UpperArm_R"].RotateX(pitch * (PitchFactorLowerBody + PitchFactorUpperBody) / 2);
|
|
|
|
_player.Camera.Camera.RotateX(pitch * (1 - PitchFactorLowerBody - PitchFactorUpperBody - PitchFactorNeck - PitchFactorHead));
|
|
|
|
|
|
|
|
const float YawFactorLowerBody = 0.06f;
|
|
|
|
const float YawFactorUpperBody = 0.18f;
|
|
|
|
const float YawFactorNeck = 0.2f;
|
|
|
|
const float YawFactorHead = 0.3f;
|
|
|
|
|
|
|
|
var yaw = _player.Camera.CurrentYaw;
|
|
|
|
_bones["LowerBody"].GlobalRotate(Vector3.Up, yaw * YawFactorLowerBody);
|
|
|
|
_bones["UpperBody"].GlobalRotate(Vector3.Up, yaw * YawFactorUpperBody);
|
|
|
|
_bones["Neck"].GlobalRotate(Vector3.Up, yaw * YawFactorNeck);
|
|
|
|
_bones["Head"].GlobalRotate(Vector3.Up, yaw * YawFactorHead);
|
|
|
|
_player.Camera.Camera.GlobalRotate(Vector3.Up, yaw * (1 - YawFactorLowerBody - YawFactorUpperBody - YawFactorNeck - YawFactorHead));
|
|
|
|
|
|
|
|
// How much of the "ideal" camera rotation (rather than animation rotation) should be applied.
|
|
|
|
const float CameraFactorIdealPitch = 0.7f;
|
|
|
|
const float CameraFactorIdealYaw = 0.8f;
|
|
|
|
const float CameraFactorIdealRoll = 0.9f;
|
|
|
|
|
|
|
|
var global_yaw = _player.Rotation.Y + yaw;
|
|
|
|
var cameraRotation = _player.Camera.Camera.GlobalRotation;
|
|
|
|
cameraRotation.X = LerpAngle(cameraRotation.X, pitch, CameraFactorIdealPitch);
|
|
|
|
cameraRotation.Y = LerpAngle(cameraRotation.Y, global_yaw, CameraFactorIdealYaw);
|
|
|
|
cameraRotation.Z = LerpAngle(cameraRotation.Z, 0, CameraFactorIdealRoll);
|
|
|
|
_player.Camera.Camera.GlobalRotation = cameraRotation;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HandleWalkingAnimation(double delta)
|
|
|
|
{
|
|
|
|
var input = Input.GetVector("move_strafe_left", "move_strafe_right", "move_forward", "move_back");
|
|
|
|
var isOnFloor = _player.Movement.TimeSinceOnFloor < 0.25f;
|
|
|
|
var isMoving = _player.Movement.RealMoveSpeed > 0.01f;
|
|
|
|
var isMovingForward = input.Y <= 0;
|
|
|
|
|
|
|
|
var walkState = (isOnFloor && isMoving) ? "move" : "idle";
|
|
|
|
var walkDirection = isMovingForward ? "forward" : "backward";
|
|
|
|
var walkSpeed = _player.Movement.RealMoveSpeed / _player.Movement.MaxSpeed;
|
|
|
|
var targetBodyYaw = -(isMovingForward ? Vector2.Up : Vector2.Down).AngleTo(input);
|
|
|
|
|
|
|
|
_animTree.Set("parameters/walk_state/transition_request", walkState);
|
|
|
|
_animTree.Set("parameters/walk_direction/transition_request", walkDirection);
|
|
|
|
|
|
|
|
var prevWalkSpeed = (float)_animTree.Get("parameters/walk_speed/blend_amount");
|
|
|
|
_animTree.Set("parameters/walk_speed/blend_amount", Lerp(prevWalkSpeed, walkSpeed, 10 * (float)delta));
|
|
|
|
|
|
|
|
const float YAW_FACTOR_LOWER_BODY = 0.25f;
|
|
|
|
const float YAW_FACTOR_UPPER_BODY = 0.25f;
|
|
|
|
const float YAW_FACTOR_NECK = 0.50f;
|
|
|
|
|
|
|
|
_bodyYaw += (targetBodyYaw - _bodyYaw) * (float)delta * 6;
|
|
|
|
|
|
|
|
_bones["Root"].GlobalRotate(Vector3.Up, _bodyYaw);
|
|
|
|
_bones["LowerBody"].GlobalRotate(Vector3.Up, -_bodyYaw * YAW_FACTOR_LOWER_BODY);
|
|
|
|
_bones["UpperBody"].GlobalRotate(Vector3.Up, -_bodyYaw * YAW_FACTOR_UPPER_BODY);
|
|
|
|
_bones["Neck"].GlobalRotate(Vector3.Up, -_bodyYaw * YAW_FACTOR_NECK);
|
|
|
|
}
|
|
|
|
}
|