Files
MovementTests/player_controller/Scripts/PlayerController.cs

341 lines
10 KiB
C#

using System;
using Godot;
using GodotStateCharts;
using Movementtests.systems;
using Movementtests.player_controller.Scripts;
public partial class PlayerController : CharacterBody3D
{
// User API to important child nodes.
public HeadSystem HeadSystem;
public Bobbing Bobbing;
public FieldOfView FieldOfView;
public Stamina Stamina;
public StairsSystem StairsSystem;
public MantleSystem MantleSystem;
public DashSystem DashSystem;
public CapsuleCollider CapsuleCollider;
public Gravity Gravity;
public HealthSystem HealthSystem;
public MoveSystem MoveSystem;
public TweenQueueSystem TweenQueueSystem;
public Node3D WeaponRoot;
public WeaponSystem WeaponSystem;
private bool _movementEnabled = true;
private bool _shouldMantle;
private Vector3 _dashLocation = Vector3.Zero;
private Vector3 _mantleLocation = Vector3.Zero;
private float _lastFrameWasOnFloor = -Mathf.Inf;
private const int NUM_OF_HEAD_COLLISION_DETECTORS = 4;
private RayCast3D[] _headCollisionDetectors;
private Vector3 _inputMove = Vector3.Zero;
private float _inputRotateY;
private float _inputRotateFloorplane;
private bool _isAiming;
private bool _dashCanceled;
private StateChart _playerState;
private StateChartState _weaponInHand;
private StateChartState _aiming;
private StateChartState _dashing;
private StateChartState _weaponThrown;
public override void _Ready()
{
///////////////////////////
// Getting components /////
///////////////////////////
// General use stuff
TweenQueueSystem = GetNode<TweenQueueSystem>("TweenQueueSystem");
// Node3D mapNode = GetTree().Root.FindChild("Map", true, false) as Node3D;
// Camera stuff
HeadSystem = GetNode<HeadSystem>("HeadSystem");
Bobbing = GetNode<Bobbing>("Bobbing");
FieldOfView = GetNode<FieldOfView>("FieldOfView");
Camera3D camera = GetNode<Camera3D>("HeadSystem/CameraSmooth/Camera3D");
Node3D cameraSmooth = GetNode<Node3D>("HeadSystem/CameraSmooth");
ColorRect vignetteRect = GetNode<ColorRect>(
"HeadSystem/CameraSmooth/Camera3D/CLVignette(Layer_1)/HealthVignetteRect");
ColorRect distortionRect = GetNode<ColorRect>(
"HeadSystem/CameraSmooth/Camera3D/CLDistortion(Layer_2)/HealthDistortionRect");
ColorRect blurRect = GetNode<ColorRect>("HeadSystem/CameraSmooth/Camera3D/CLBlur(Layer_2)/BlurRect");
// Movement stuff
WeaponRoot = GetNode<Node3D>("WeaponRoot");
WeaponSystem = GetNode<WeaponSystem>("WeaponRoot/WeaponSystem");
MantleSystem = GetNode<MantleSystem>("MantleSystem");
CapsuleCollider = GetNode<CapsuleCollider>("CapsuleCollider");
Gravity = GetNode<Gravity>("Gravity");
MoveSystem = GetNode<MoveSystem>("MoveSystem");
DashSystem = GetNode<DashSystem>("DashSystem");
StairsSystem = GetNode<StairsSystem>("StairsSystem");
RayCast3D stairsBelowRayCast3D = GetNode<RayCast3D>("StairsBelowRayCast3D");
RayCast3D stairsAheadRayCast3D = GetNode<RayCast3D>("StairsAheadRayCast3D");
_headCollisionDetectors = new RayCast3D[NUM_OF_HEAD_COLLISION_DETECTORS];
for (int i = 0; i < NUM_OF_HEAD_COLLISION_DETECTORS; i++)
{
_headCollisionDetectors[i] = GetNode<RayCast3D>(
"HeadCollisionDetectors/HeadCollisionDetector" + i);
}
// RPG Stuff
Stamina = GetNode<Stamina>("Stamina");
HealthSystem = GetNode<HealthSystem>("HealthSystem");
// State management
_playerState = StateChart.Of(GetNode("StateChart"));
_weaponInHand = StateChartState.Of(GetNode("StateChart/Root/WeaponInHand"));
_aiming = StateChartState.Of(GetNode("StateChart/Root/Aiming"));
_dashing = StateChartState.Of(GetNode("StateChart/Root/Dashing"));
_weaponThrown = StateChartState.Of(GetNode("StateChart/Root/WeaponThrown"));
///////////////////////////
// Initialize components //
///////////////////////////
// General use stuff
TweenQueueSystem.Init(this);
// Camera stuff
HeadSystem.Init();
Bobbing.Init(camera);
FieldOfView.Init(camera);
// Movement stuff
// Getting universal setting from GODOT editor to be in sync
float gravitySetting = (float)ProjectSettings.GetSetting("physics/3d/default_gravity");
Gravity.Init(gravitySetting);
MantleSystem.Init(HeadSystem);
var moveSystemParams = new MoveSystem.MoveSystemParameters(this, Gravity, MantleSystem, TweenQueueSystem,
HeadSystem, CapsuleCollider);
MoveSystem.Init(moveSystemParams);
StairsSystem.Init(stairsBelowRayCast3D, stairsAheadRayCast3D, cameraSmooth);
DashSystem.Init(HeadSystem, camera, TweenQueueSystem);
WeaponSystem.Init(HeadSystem, camera);
// RPG Stuff
HealthSystem.HealthSystemInitParams healthSystemParams = new HealthSystem.HealthSystemInitParams()
{
Gravity = Gravity,
Parent = this,
Camera = camera,
Head = HeadSystem,
VignetteRect = vignetteRect,
DistortionRect = distortionRect,
BlurRect = blurRect,
};
HealthSystem.Init(healthSystemParams);
Stamina.SetSpeeds(MoveSystem.WalkSpeed, MoveSystem.SprintSpeed);
///////////////////////////
// Signal setup ///////////
///////////////////////////
DashSystem.DashEnded += OnDashEnded;
_dashing.StateEntered += OnDashStarted;
_weaponThrown.StateEntered += OnWeaponThrown;
}
public void OnDashStarted()
{
DashSystem.Dash();
}
public void OnWeaponThrown()
{
RemoveChild(WeaponRoot);
GetTree().GetRoot().AddChild(WeaponRoot);
WeaponRoot.SetGlobalPosition(GlobalPosition);
var (hasHit, location, collisionPoint, collisionNormal) = DashSystem.DashComputation;
var (endWithMantle, dashLocation, mantleLocation) = DashSystem.DashResolve;
DashSystem.CancelDash();
WeaponSystem.ThrowWeapon(location, hasHit, collisionPoint, collisionNormal);
}
public void OnDashEnded()
{
// Generates an error when dashing normally
// This should solve itself when we handle weapon thrown dashes and regular dashes through different states
GetTree().GetRoot().RemoveChild(WeaponRoot);
AddChild(WeaponRoot);
WeaponRoot.SetGlobalPosition(GlobalPosition);
WeaponSystem.ResetWeapon();
_playerState.SendEvent("dash_ended");
}
public void OnInputMove(Vector3 value)
{
_inputMove = value;
}
public void OnInputRotateY(float value)
{
_inputRotateY = value;
}
public void OnInputRotateFloorplane(float value)
{
_inputRotateFloorplane = value;
}
public void OnInputAimPressed()
{
_playerState.SendEvent("aim_pressed");
}
public void OnInputAimReleased()
{
_playerState.SendEvent("aim_released");
}
public void OnInputAimCanceled()
{
_playerState.SendEvent("aim_canceled");
DashSystem.CancelDash();
}
public void OnInputHitPressed()
{
_playerState.SendEvent("hit_pressed");
}
public void OnInputJumpPressed()
{
bool doesCapsuleHaveCrouchingHeight = CapsuleCollider.IsCrouchingHeight();
bool isPlayerDead = HealthSystem.IsDead();
if (!doesCapsuleHaveCrouchingHeight && !isPlayerDead)
MoveSystem.Jump(IsOnFloor());
}
public override void _PhysicsProcess(double delta)
{
var isPlayerDead = HealthSystem.IsDead();
var isHeadTouchingCeiling = IsHeadTouchingCeiling();
TweenQueueSystem.ProcessTweens();
if (_weaponInHand.Active || _aiming.Active)
WeaponRoot.SetRotation(HeadSystem.Rotation);
if (_aiming.Active)
DashSystem.PrepareDash();
var moveAroundParams = new MoveSystem.MoveAroundParameters(
delta,
_inputMove,
isOnFloorCustom(),
isPlayerDead,
isHeadTouchingCeiling);
MoveSystem.MoveAround(moveAroundParams);
Vector2 inputLookDir = new Vector2(_inputRotateY, _inputRotateFloorplane);
HeadSystem.LookAround(inputLookDir);
Bobbing.CameraBobbingParams cameraBobbingParams = new Bobbing.CameraBobbingParams
{
Delta = (float)delta,
IsOnFloorCustom = isOnFloorCustom(),
Velocity = Velocity
};
Bobbing.PerformCameraBobbing(cameraBobbingParams);
FieldOfView.FovParameters fovParams = new FieldOfView.FovParameters
{
IsCrouchingHeight = CapsuleCollider.IsCrouchingHeight(),
Delta = (float)delta,
SprintSpeed = MoveSystem.SprintSpeed,
Velocity = Velocity
};
FieldOfView.PerformFovAdjustment(fovParams);
StairsSystem.UpStairsCheckParams upStairsCheckParams = new StairsSystem.UpStairsCheckParams
{
IsOnFloorCustom = isOnFloorCustom(),
IsCapsuleHeightLessThanNormal = CapsuleCollider.IsCapsuleHeightLessThanNormal(),
CurrentSpeedGreaterThanWalkSpeed = MoveSystem._currentSpeed > MoveSystem.WalkSpeed,
IsCrouchingHeight = CapsuleCollider.IsCrouchingHeight(),
Delta = (float)delta,
FloorMaxAngle = FloorMaxAngle,
GlobalPositionFromDriver = GlobalPosition,
Velocity = Velocity,
GlobalTransformFromDriver = GlobalTransform,
Rid = GetRid()
};
// TODO: SnapUpStairsCheck influences the ability of player to crouch because of `stepHeightY <= 0.01` part
// Ideally, it should not. SnapUpStairsCheck and SnapDownStairsCheck should be called, when player is actually
// on the stairs
StairsSystem.UpStairsCheckResult upStairsCheckResult = StairsSystem.SnapUpStairsCheck(upStairsCheckParams);
if (upStairsCheckResult.UpdateRequired)
{
upStairsCheckResult.Update(this);
}
else
{
MoveAndSlide();
StairsSystem.DownStairsCheckParams downStairsCheckParams = new StairsSystem.DownStairsCheckParams
{
IsOnFloor = IsOnFloor(), // TODO: replace on IsOnFloor Custom
IsCrouchingHeight = CapsuleCollider.IsCrouchingHeight(),
LastFrameWasOnFloor = _lastFrameWasOnFloor,
CapsuleDefaultHeight = CapsuleCollider.GetDefaultHeight(),
CurrentCapsuleHeight = CapsuleCollider.GetCurrentHeight(),
FloorMaxAngle = FloorMaxAngle,
VelocityY = Velocity.Y,
GlobalTransformFromDriver = GlobalTransform,
Rid = GetRid()
};
StairsSystem.DownStairsCheckResult downStairsCheckResult = StairsSystem.SnapDownStairsCheck(
downStairsCheckParams);
if (downStairsCheckResult.UpdateIsRequired)
{
downStairsCheckResult.Update(this);
}
}
StairsSystem.SlideCameraParams slideCameraParams = new StairsSystem.SlideCameraParams
{
IsCapsuleHeightLessThanNormal = CapsuleCollider.IsCapsuleHeightLessThanNormal(),
CurrentSpeedGreaterThanWalkSpeed = MoveSystem._currentSpeed > MoveSystem.WalkSpeed,
BetweenCrouchingAndNormalHeight = CapsuleCollider.IsBetweenCrouchingAndNormalHeight(),
Delta = (float)delta
};
StairsSystem.SlideCameraSmoothBackToOrigin(slideCameraParams);
}
private bool IsHeadTouchingCeiling()
{
for (int i = 0; i < NUM_OF_HEAD_COLLISION_DETECTORS; i++)
{
if (_headCollisionDetectors[i].IsColliding())
{
return true;
}
}
return false;
}
private bool isOnFloorCustom()
{
return IsOnFloor() || StairsSystem.WasSnappedToStairsLastFrame();
}
}