Initial commit
This commit is contained in:
193
source/Scripts/PlayerController.cs
Normal file
193
source/Scripts/PlayerController.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
using FactoryGame.Entities;
|
||||
using Godot;
|
||||
|
||||
using static FactoryGame.Utils.Constants.PropertyHints;
|
||||
|
||||
public partial class PlayerController : CharacterBody3D {
|
||||
[ExportGroup("Nodes")]
|
||||
[Export]
|
||||
public CollisionShape3D? StandCollision { get; set; }
|
||||
[Export]
|
||||
public CollisionShape3D? CrouchCollision { get; set; }
|
||||
[Export]
|
||||
public CollisionComponent? HeadCollisionComponent { get; set; }
|
||||
[Export]
|
||||
public CollisionComponent? BottomCollisionComponent { get; set; }
|
||||
[Export]
|
||||
public Camera3D? Camera { get; set; }
|
||||
[Export]
|
||||
public Marker3D? StandCameraPosition { get; set; }
|
||||
[Export]
|
||||
public Marker3D? CrouchCameraPosition { get; set; }
|
||||
|
||||
[ExportGroup("Behavior")]
|
||||
[Export(PropertyHint.Range, PrimaRange)]
|
||||
public float CameraSpeed { get; set; } = 0.25f;
|
||||
[Export(PropertyHint.Range, OctaveRange)]
|
||||
public float PushImpulse { get; set; } = 0.5f;
|
||||
[Export(PropertyHint.Range, PrimaRange)]
|
||||
public float CeilingBounce { get; set; } = 0.3f;
|
||||
[Export]
|
||||
public PlayerMovingState MovingState {
|
||||
get => _movingState; set {
|
||||
if (_movingState == value) return;
|
||||
if (crouchLock) { _movingState = PlayerMovingState.Crouching; return; }
|
||||
|
||||
_movingState = value;
|
||||
StandCollision!.Disabled = !(_movingState == PlayerMovingState.Walking || _movingState == PlayerMovingState.Sprinting);
|
||||
CrouchCollision!.Disabled = _movingState != PlayerMovingState.Crouching;
|
||||
if (_movingState == PlayerMovingState.Walking || _movingState == PlayerMovingState.Sprinting)
|
||||
_cameraTargetPosition = StandCameraPosition!.Position;
|
||||
if (_movingState == PlayerMovingState.Crouching)
|
||||
_cameraTargetPosition = CrouchCameraPosition!.Position;
|
||||
}
|
||||
}
|
||||
private PlayerMovingState _movingState = PlayerMovingState.Walking;
|
||||
private bool crouchLock = false;
|
||||
|
||||
[ExportSubgroup("Gravity Stabilization")]
|
||||
[Export(PropertyHint.Range, TwoOctaveRange)]
|
||||
public float FlyGravityStabilizationSpeed { get; set; } = 2f;
|
||||
[Export(PropertyHint.Range, TwoOctaveRange)]
|
||||
public float FloorGravityStabilizationSpeed { get; set; } = 15f;
|
||||
public float GetCurrentGravityStabilizationSpeed() => BottomCollisionComponent!.IsCollide() ? FloorGravityStabilizationSpeed : FlyGravityStabilizationSpeed;
|
||||
|
||||
[ExportSubgroup("Movement")]
|
||||
[Export(PropertyHint.Range, OctaveRange)]
|
||||
public float MoveVelocity { get; set; } = 4f;
|
||||
[Export(PropertyHint.Range, OctaveRange)]
|
||||
public float CrouchVelocity { get; set; } = 2f;
|
||||
[Export(PropertyHint.Range, OctaveRange)]
|
||||
public float SprintVelocity { get; set; } = 8f;
|
||||
[Export(PropertyHint.Range, QuartRange)]
|
||||
public float FlyVelocityMultiplier { get; set; } = 1.05f;
|
||||
private float GetCurrentSpeed() {
|
||||
var speed = MovingState switch {
|
||||
PlayerMovingState.Walking => MoveVelocity,
|
||||
PlayerMovingState.Crouching => CrouchVelocity,
|
||||
PlayerMovingState.Sprinting => SprintVelocity,
|
||||
_ => MoveVelocity
|
||||
};
|
||||
|
||||
if (!IsOnFloor()) return FlyVelocityMultiplier * speed;
|
||||
return speed;
|
||||
}
|
||||
[Export(PropertyHint.Range, OctaveRange)]
|
||||
public float JumpVelocity { get; set; } = 4f;
|
||||
|
||||
[ExportSubgroup("Friction")]
|
||||
[Export(PropertyHint.Range, PrimaRange)]
|
||||
public float MoveFriction { get; set; } = 0.5f;
|
||||
[Export(PropertyHint.Range, PrimaRange)]
|
||||
public float CrouchFriction { get; set; } = 0.2f;
|
||||
[Export(PropertyHint.Range, OctaveRange)]
|
||||
public float SprintFriction { get; set; } = 0.6f;
|
||||
[Export(PropertyHint.Range, PrimaRange)]
|
||||
public float FlyFriction { get; set; } = 0.1f;
|
||||
private float GetCurrentFriction() => !IsOnFloor() ? FlyFriction : MovingState switch {
|
||||
PlayerMovingState.Walking => MoveFriction,
|
||||
PlayerMovingState.Crouching => CrouchFriction,
|
||||
PlayerMovingState.Sprinting => SprintFriction,
|
||||
_ => MoveFriction
|
||||
};
|
||||
|
||||
public override void _Ready() {
|
||||
_cameraTargetPosition = Camera!.Position;
|
||||
HeadCollisionComponent!.BodyEntered += (_) => ProceedCrouchingCheck(HeadCollisionComponent!);
|
||||
HeadCollisionComponent!.BodyExited += (_) => ProceedCrouchingCheck(HeadCollisionComponent!);
|
||||
|
||||
base._Ready();
|
||||
}
|
||||
public override void _PhysicsProcess(double delta) {
|
||||
var fDelta = (float)delta;
|
||||
UpdatePlayerOrientation(fDelta);
|
||||
|
||||
var gravity = ProceedGravityVelocity(fDelta);
|
||||
var move = ProceedMoveVelocity(fDelta);
|
||||
|
||||
Velocity = gravity + move;
|
||||
MoveAndSlide();
|
||||
|
||||
Push();
|
||||
|
||||
base._PhysicsProcess(delta);
|
||||
}
|
||||
|
||||
private void ProceedCrouchingCheck(CollisionComponent collisionComponent) {
|
||||
if (collisionComponent.IsCollide()) {
|
||||
crouchLock = true;
|
||||
MovingState = PlayerMovingState.Crouching;
|
||||
} else crouchLock = false;
|
||||
}
|
||||
|
||||
private void UpdatePlayerOrientation(float delta) {
|
||||
var gravity = GetGravity();
|
||||
if (gravity == Vector3.Zero) return;
|
||||
|
||||
UpDirection = -gravity;
|
||||
var objectBottomDirection = -Transform.Basis.Y;
|
||||
var angleToTarget = objectBottomDirection.AngleTo(gravity);
|
||||
|
||||
var rotationAxis = objectBottomDirection.Cross(gravity).Normalized();
|
||||
|
||||
var interpolatedAngle = Mathf.Lerp(0, angleToTarget, GetCurrentGravityStabilizationSpeed() * delta);
|
||||
if (rotationAxis.IsNormalized())
|
||||
Rotate(rotationAxis, interpolatedAngle);
|
||||
}
|
||||
|
||||
private Vector3 _gravityVelocity = Vector3.Zero;
|
||||
private Vector3 ProceedGravityVelocity(float delta) {
|
||||
var localUp = GlobalTransform.Basis.Y;
|
||||
var gravityForce = GetGravity();
|
||||
|
||||
if (IsOnFloor()) {
|
||||
_gravityVelocity = gravityForce.Normalized() * 0.1f;
|
||||
|
||||
if (Input.IsActionPressed("move_jump"))
|
||||
_gravityVelocity = localUp * JumpVelocity;
|
||||
|
||||
return _gravityVelocity;
|
||||
}
|
||||
|
||||
if (IsOnCeiling() && GetSlideCollisionCount() > 0) {
|
||||
var normal = GetLastSlideCollision().GetNormal();
|
||||
if (_gravityVelocity.Dot(localUp) > 0 && normal.Dot(localUp) < -0.5f)
|
||||
_gravityVelocity = _gravityVelocity.Bounce(normal) * CeilingBounce;
|
||||
}
|
||||
|
||||
_gravityVelocity += gravityForce * delta;
|
||||
return _gravityVelocity;
|
||||
}
|
||||
|
||||
private Vector3 _cameraTargetPosition;
|
||||
private Vector3 _moveVelocity = Vector3.Zero;
|
||||
private Vector3 ProceedMoveVelocity(float delta) {
|
||||
var movingState = Input.IsActionPressed("action_sprint") ? PlayerMovingState.Sprinting : PlayerMovingState.Walking;
|
||||
movingState = Input.IsActionPressed("action_crouch") ? PlayerMovingState.Crouching : movingState;
|
||||
MovingState = movingState;
|
||||
|
||||
Camera!.Position = Camera!.Position.Lerp(_cameraTargetPosition, CameraSpeed);
|
||||
|
||||
var inputDir = Input.GetVector("move_left", "move_right", "move_forward", "move_backward");
|
||||
|
||||
var dir = (GlobalTransform.Basis * new Vector3(inputDir.X, 0, inputDir.Y)).Normalized();
|
||||
|
||||
if (inputDir == Vector2.Zero)
|
||||
_moveVelocity = _moveVelocity.Lerp(Vector3.Zero, GetCurrentFriction());
|
||||
else
|
||||
_moveVelocity = dir * GetCurrentSpeed();
|
||||
|
||||
return _moveVelocity;
|
||||
}
|
||||
|
||||
private void Push() {
|
||||
for (int i = 0; i < GetSlideCollisionCount(); i++) {
|
||||
var collision = GetSlideCollision(i);
|
||||
if (collision.GetCollider() is RigidBody3D body) {
|
||||
var pushDir = -collision.GetNormal();
|
||||
|
||||
body.ApplyCentralImpulse(pushDir * _moveVelocity.Length() * PushImpulse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user