235 lines
8.4 KiB
C#
235 lines
8.4 KiB
C#
using Godot;
|
|
using Movementtests.player_controller.Scripts;
|
|
using RustyOptions;
|
|
|
|
namespace Movementtests.systems;
|
|
|
|
public partial class MoveSystem : Node3D
|
|
{
|
|
public enum JumpTypes
|
|
{
|
|
SIMPLE_JUMP,
|
|
DOUBLE_JUMP,
|
|
JUMP_FROM_DASH
|
|
}
|
|
|
|
public record MoveSystemParameters(
|
|
CharacterBody3D Parent,
|
|
Gravity Gravity,
|
|
MantleSystem MantleSystem,
|
|
TweenQueueSystem TweenQueueSystem,
|
|
HeadSystem HeadSystem,
|
|
CapsuleCollider CapsuleCollider);
|
|
|
|
public record MoveAroundParameters(
|
|
double Delta,
|
|
Vector3 MovementDirection,
|
|
bool IsOnFloor,
|
|
bool IsDead,
|
|
bool IsHeadTouchingCeiling,
|
|
bool isHanging,
|
|
bool isWallHugging
|
|
);
|
|
|
|
[Export(PropertyHint.Range, "0,20,0.1,or_greater")]
|
|
public float WalkSpeed { get; set; } = 5.0f;
|
|
[Export(PropertyHint.Range, "0,20,0.1,or_greater")]
|
|
public float SprintSpeed { get; set; } = 7.2f;
|
|
[Export(PropertyHint.Range, "0,10,0.1,or_greater")]
|
|
public float CrouchSpeed { get; set; } = 2.5f;
|
|
[Export(PropertyHint.Range, "0,100,0.1,or_greater")]
|
|
|
|
public float _currentSpeed;
|
|
[Export(PropertyHint.Range, "0,10,0.1,or_greater")]
|
|
public float AccelerationSpeedFactorFloor = 5.0f;
|
|
[Export(PropertyHint.Range, "0,10,0.1,or_greater")]
|
|
public float DecelerationSpeedFactorFloor = 5.0f;
|
|
[Export(PropertyHint.Range, "0,10,0.1,or_greater")]
|
|
public float DecelerationSpeedFactorAir = 1.0f;
|
|
[Export(PropertyHint.Range, "0,1,0.01,or_greater")]
|
|
public float ApexHoldTime = 0.0f;
|
|
private float _timeLeftAtApex = 0.0f;
|
|
private bool _wasGoingUpLastFrame = false;
|
|
|
|
public float CrouchTransitionSpeed { get; set; } = 20.0f;
|
|
[Export(PropertyHint.Range, "0,5,0.1,or_greater")]
|
|
public float WallHugGravityReducingFactor { get; set; } = 0.1f;
|
|
|
|
|
|
private Gravity _gravity;
|
|
private CharacterBody3D _parent;
|
|
private MantleSystem _mantleSystem;
|
|
private TweenQueueSystem _tweenQueueSystem;
|
|
private CapsuleCollider _capsuleCollider;
|
|
private HeadSystem _headSystem;
|
|
public void Init(MoveSystemParameters parameters)
|
|
{
|
|
_parent = parameters.Parent;
|
|
_gravity = parameters.Gravity;
|
|
_mantleSystem = parameters.MantleSystem;
|
|
_tweenQueueSystem = parameters.TweenQueueSystem;
|
|
_capsuleCollider = parameters.CapsuleCollider;
|
|
_headSystem = parameters.HeadSystem;
|
|
|
|
_currentSpeed = WalkSpeed;
|
|
}
|
|
|
|
private bool IsGoingUp()
|
|
{
|
|
return _parent.Velocity.Y > 0;
|
|
}
|
|
|
|
public void MoveAround(MoveAroundParameters param)
|
|
{
|
|
var (delta, movementDirection, isOnFloor, isDead, isHeadTouchingCeiling, isHanging, isWallHugging) = param;
|
|
|
|
var doesCapsuleHaveCrouchingHeight = _capsuleCollider.IsCrouchingHeight();
|
|
var doesCapsuleHaveDefaultHeight = _capsuleCollider.IsDefaultHeight();
|
|
|
|
if (IsGoingUp() || isOnFloor)
|
|
{
|
|
_wasGoingUpLastFrame = true;
|
|
_timeLeftAtApex = ApexHoldTime;
|
|
}
|
|
|
|
if (isHanging)
|
|
{
|
|
_parent.Velocity = Vector3.Zero;
|
|
_parent.MoveAndSlide();
|
|
return;
|
|
}
|
|
if (isWallHugging)
|
|
{
|
|
_parent.Velocity = new Vector3(
|
|
x: _parent.Velocity.X,
|
|
y: _parent.Velocity.Y - _gravity.CalculateGravityForce() * (float)delta * WallHugGravityReducingFactor,
|
|
z: _parent.Velocity.Z);
|
|
return;
|
|
}
|
|
|
|
// Adding the gravity
|
|
if (!isOnFloor)
|
|
{
|
|
if (!IsGoingUp() && _wasGoingUpLastFrame && _timeLeftAtApex > 0)
|
|
{
|
|
_parent.Velocity = new Vector3(
|
|
x: _parent.Velocity.X,
|
|
y: 0,
|
|
z: _parent.Velocity.Z);
|
|
_timeLeftAtApex -= (float) delta;
|
|
}
|
|
else
|
|
{
|
|
_parent.Velocity = new Vector3(
|
|
x: _parent.Velocity.X,
|
|
y: _parent.Velocity.Y - (_gravity.CalculateGravityForce() * (float)delta),
|
|
z: _parent.Velocity.Z);
|
|
}
|
|
}
|
|
|
|
// The code below is required to quickly adjust player's position on Y-axis when there's a ceiling on the
|
|
// trajectory of player's jump and player is standing
|
|
if (isHeadTouchingCeiling && doesCapsuleHaveDefaultHeight)
|
|
{
|
|
_parent.Velocity = new Vector3(
|
|
x: _parent.Velocity.X,
|
|
y: _parent.Velocity.Y - 2.0f,
|
|
z: _parent.Velocity.Z);
|
|
}
|
|
|
|
if (!isDead)
|
|
{
|
|
|
|
// Used both for detecting the moment when we enter into crouching mode and the moment when we're already
|
|
// in the crouching mode
|
|
if (Input.IsActionPressed("crouch") ||
|
|
(doesCapsuleHaveCrouchingHeight && isHeadTouchingCeiling))
|
|
{
|
|
_capsuleCollider.Crouch((float)delta, CrouchTransitionSpeed);
|
|
_currentSpeed = CrouchSpeed;
|
|
}
|
|
// Used both for the moment when we exit the crouching mode and for the moment when we just walk
|
|
else
|
|
{
|
|
_capsuleCollider.UndoCrouching((float)delta, CrouchTransitionSpeed);
|
|
_currentSpeed = WalkSpeed;
|
|
}
|
|
}
|
|
|
|
// Each component of the boolean statement for sprinting is required
|
|
if (Input.IsActionPressed("sprint") && !isHeadTouchingCeiling &&
|
|
!doesCapsuleHaveCrouchingHeight && !isDead)
|
|
{
|
|
_currentSpeed = SprintSpeed;
|
|
}
|
|
|
|
// Basis is a 3x4 matrix. It contains information about scaling and rotation of head.
|
|
// By multiplying our Vector3 by this matrix we're doing multiple things:
|
|
// a) We start to operate in global space;
|
|
// b) We're applying to Vector3 the current rotation of "head" object;
|
|
// c) We're applying to Vector3 the current scaling of "head" object;
|
|
Vector3 direction = _headSystem.Transform.Basis * movementDirection;
|
|
|
|
if (isDead)
|
|
{
|
|
direction = Vector3.Zero;
|
|
}
|
|
|
|
var accelerationFloorFactor = direction.Length() > 0 ? AccelerationSpeedFactorFloor : DecelerationSpeedFactorFloor;
|
|
var accelerationFactor = isOnFloor ? accelerationFloorFactor : DecelerationSpeedFactorAir;
|
|
|
|
float xAcceleration = Mathf.Lerp(_parent.Velocity.X, direction.X * _currentSpeed,
|
|
(float)delta * accelerationFactor);
|
|
float zAcceleration = Mathf.Lerp(_parent.Velocity.Z, direction.Z * _currentSpeed,
|
|
(float)delta * accelerationFactor);
|
|
_parent.Velocity = new Vector3(xAcceleration, _parent.Velocity.Y, zAcceleration);
|
|
|
|
if (isDead)
|
|
{
|
|
_parent.MoveAndSlide();
|
|
}
|
|
}
|
|
|
|
public void Jump(JumpTypes jumpType, Vector3? jumpDirection = null, float boost = 1.0f)
|
|
{
|
|
var effectiveJumpDirection = jumpDirection ?? Vector3.Up;
|
|
var jumpForce = 0.0f;
|
|
switch (jumpType)
|
|
{
|
|
case JumpTypes.DOUBLE_JUMP:
|
|
jumpForce = _gravity.CalculateDoubleJumpForce();
|
|
break;
|
|
case JumpTypes.SIMPLE_JUMP:
|
|
jumpForce = _gravity.CalculateJumpForce();
|
|
break;
|
|
case JumpTypes.JUMP_FROM_DASH:
|
|
jumpForce = _gravity.CalculateJumpFromDashForce();
|
|
break;
|
|
default:
|
|
jumpForce = _gravity.CalculateJumpForce();
|
|
break;
|
|
}
|
|
|
|
var currentHorizontalVelocity = new Vector3(_parent.Velocity.X, 0, _parent.Velocity.Z);
|
|
var jumpVelocity = jumpForce * effectiveJumpDirection * boost;
|
|
_parent.Velocity = currentHorizontalVelocity + jumpVelocity;
|
|
}
|
|
|
|
public bool CanMantle()
|
|
{
|
|
var mantleLocationResult = _mantleSystem.FindMantleInFrontOfPlayer();
|
|
return mantleLocationResult.IsSome(out _);
|
|
}
|
|
|
|
public Option<Tween> Mantle()
|
|
{
|
|
var mantleLocationResult = _mantleSystem.FindMantleInFrontOfPlayer();
|
|
if (mantleLocationResult.IsSome(out var mantleLocation))
|
|
{
|
|
var duration = 0.1f * mantleLocation.DistanceTo(_parent.Position);
|
|
var tween = _tweenQueueSystem.TweenToLocation(new TweenQueueSystem.TweenInputs(mantleLocation, duration));
|
|
return tween.Some();
|
|
}
|
|
return Option<Tween>.None;
|
|
}
|
|
} |