using System; using BITFALL.Entities.Player.Movement.States; using BITFALL.Player.Movement; using BITKit.StateMachine; using Lightbug.CharacterControllerPro.Core; using UnityEngine; // ReSharper disable InvertIf // ReSharper disable RedundantJumpStatement // ReSharper disable ConvertToConstant.Global namespace BITFALL.Entities.Player.Movement.States { public abstract class BasicMovement : PlayerCharacterState,IPlayerMovementState { [SerializeField] protected Vector3 initialCameraPosition; [SerializeField] protected float initialSpeed = 3f; [SerializeField] protected float initialJumpForce = 5f; public override void BeforeUpdateMovement(float deltaTime) { characterController.CurrentCameraPosition.shouldBe = initialCameraPosition; } public override void UpdateVelocity(ref Vector3 currentVelocity,float deltaTime) { var rotation = Quaternion.Euler(0,characterController.LookInput.y,0); var moveVelocity = rotation * characterController.MovementInput; if (characterController.IsGrounded) { var effectiveGroundNormal = actor.GroundStableNormal; var inputRight = Vector3.Cross(moveVelocity, Vector3.up); var reorientedInput = Vector3.Cross(effectiveGroundNormal, inputRight).normalized * moveVelocity.magnitude; var targetMovementVelocity = reorientedInput * initialSpeed; // Smooth movement Velocity currentVelocity = Vector3.Lerp(currentVelocity, targetMovementVelocity, 1f - Mathf.Exp(-16 * deltaTime)); if (characterController.ExpectJump.shouldBe) { actor.ForceNotGrounded(); currentVelocity += Vector3.up * initialJumpForce; characterController.ExpectJump.Reset(); characterController.ExecuteCommand(); } } else { if (moveVelocity.sqrMagnitude > 0f) { var addedVelocity = moveVelocity * (3 * deltaTime); var currentVelocityOnInputsPlane = Vector3.ProjectOnPlane(currentVelocity, Vector3.up); // Limit air velocity from inputs if (currentVelocityOnInputsPlane.magnitude < 5) { // clamp addedVel to make total vel not exceed max vel on inputs plane Vector3 newTotal = Vector3.ClampMagnitude(currentVelocityOnInputsPlane + addedVelocity, 5); addedVelocity = newTotal - currentVelocityOnInputsPlane; } else { // Make sure added vel doesn't go in the direction of the already-exceeding velocity if (Vector3.Dot(currentVelocityOnInputsPlane, addedVelocity) > 0f) { addedVelocity = Vector3.ProjectOnPlane(addedVelocity, currentVelocityOnInputsPlane.normalized); } } // Prevent air-climbing sloped walls if (actor.WallCollision) { if (Vector3.Dot(currentVelocity + addedVelocity, addedVelocity) > 0f) { var perpenticularObstructionNormal = Vector3.Cross(Vector3.Cross(Vector3.up, actor.GroundContactNormal), Vector3.up).normalized; addedVelocity = Vector3.ProjectOnPlane(addedVelocity, perpenticularObstructionNormal); } } // Apply added velocity currentVelocity += addedVelocity; } // Gravity currentVelocity += -Vector3.up * (30 * deltaTime); // Drag currentVelocity *= (1f / (1f + (0.1f * deltaTime))); } } public override void UpdateRotation(ref Quaternion currentRotation, float deltaTime) { currentRotation = Quaternion.Euler(0,characterController.LookInput.y,0); } } [Serializable] public sealed class Walk:BasicMovement,IPlayerWalkState { public override void OnStateEntry(IState old) { base.OnStateEntry(old); characterController.ExpectRun.Reset(); characterController.ExpectSprint.Reset(); } public override void AfterUpdateMovement(float deltaTime) { switch (characterController.ExpectRun.shouldBe,characterController.ExpectCrouch.shouldBe) { case (_,true): characterController.TransitionState(); break; case (true,false): characterController.TransitionState(); break; } } } [Serializable] public sealed class Run:BasicMovement,IPlayerRunState { public override void OnStateEntry(IState old) { characterController.ExpectRun.being = true; } public override void OnStateExit(IState old, IState newState) { characterController.ExpectRun.being = false; } public override void AfterUpdateMovement(float deltaTime) { if (characterController.ExpectSprint.shouldBe) { characterController.TransitionState(); } switch (characterController.ExpectRun.shouldBe,characterController.ExpectCrouch.shouldBe) { case (_,true): characterController.TransitionState(); break; case (false,false): characterController.TransitionState(); break; } } public override void ExecuteCommand(T command) { if (Enabled is false) return; if (command is PlayerCancelRunCommand) { characterController.TransitionState(); } } } [Serializable] public sealed class Crouch:BasicMovement,IPlayerCrouchState { public override void OnStateEntry(IState old) { characterController.ExpectCrouch.being = true; } public override void OnStateExit(IState old, IState newState) { characterController.ExpectCrouch.being = false; } public override void AfterUpdateMovement(float deltaTime) { if (characterController.ExpectRun.shouldBe) { characterController.TransitionState(); return; } if (characterController.ExpectCrouch.shouldBe is false) { characterController.TransitionState(); return; } } } [Serializable] public sealed class Sprint:BasicMovement,IPlayerSprintState { public override void OnStateEntry(IState old) { characterController.ExpectSprint.being = true; } public override void OnStateExit(IState old, IState newState) { characterController.ExpectSprint.being = false; } public override void AfterUpdateMovement(float deltaTime) { switch (characterController.ExpectRun.shouldBe,characterController.ExpectCrouch.shouldBe) { case (_,true): characterController.TransitionState(); break; case (false,false): characterController.TransitionState(); break; } } public override void ExecuteCommand(T command) { if (Enabled is false) return; if(command is PlayerCancelRunCommand) characterController.TransitionState(); } } }