using System; using BITFALL.Entities.Player.Movement.States; using BITFALL.Player.Equip; using BITFALL.Player.Movement; using BITFALL.Sensors; using BITKit; using BITKit.Entities; using BITKit.PlayerCamera; using BITKit.Sensors; using BITKit.StateMachine; using Cysharp.Threading.Tasks; using Lightbug.CharacterControllerPro.Core; using Steamworks.ServerList; using UnityEngine; using UnityEngine.InputSystem; // ReSharper disable InvertIf // ReSharper disable RedundantJumpStatement // ReSharper disable ConvertToConstant.Global namespace BITFALL.Entities.Player.Movement.States { public abstract class BasicMovement : PlayerCharacterState, IPlayerMovementState { [SerializeField] internal Vector3 initialCameraPosition; [SerializeField] internal Vector2 initialSize=new Vector2(0.8f,1.6f); [SerializeField] internal float initialSpeed = 3f; [SerializeField] internal float initialJumpForce = 5f; [Inject] protected IKnockdown knockdown; [Inject] protected IPlayerCameraService _cameraService; public override void OnStateEntry(IState old) { base.OnStateEntry(old); actor.SetSize(initialSize,CharacterActor.SizeReferenceType.Bottom); self.ReferenceSpeed = initialSpeed; } public override void BeforeUpdateMovement(float deltaTime) { self.CurrentCameraPosition.shouldBe = initialCameraPosition; } public override void AfterUpdateMovement(float deltaTime) { if (knockdown.IsKnockdown) { self.TransitionState(); } if (knockdown.IsKnockdown is false && self.ExpectParachute.shouldBe) { self.TransitionState(); self.ExpectParachute.Reset(); } if (self.RequestClimb is false) return; if(self.topBlocked)return; // if (characterController.topBlocked is false && characterController.RequestClimb && // characterController.climbClosePoint.TryGetClosePoint(out var closePoint)) // { // characterController.ExpectClimb.shouldBe = closePoint; // characterController.TransitionState(); // return; // } if (self.vaultPoint.TryGetClosePoint(out var closePoint)) { self.ExpectClimb.shouldBe = closePoint; self.TransitionState(); self.ExpectJump.Reset(); self.RequestClimb = false; return; } if (self.climbClosePoint.TryGetClosePoint(out closePoint)) { self.ExpectClimb.shouldBe = closePoint; self.TransitionState(); self.ExpectJump.Reset(); self.RequestClimb = false; return; } if (self.edgeClimbPoint.TryGetClosePoint(out closePoint)) { self.ExpectClimb.shouldBe = closePoint; self.TransitionState();self.ExpectJump.Reset(); self.RequestClimb = false; return; } } public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime) { var rotation = Quaternion.Euler(0, self.LookInput.y, 0); var movementInput = self.MovementInput; if (_cameraService?.IsCameraActivated is false) { rotation =Quaternion.LookRotation( _cameraService.CameraRotation * Vector3.forward,Vector3.up); } var moveVelocity = rotation * new Vector3( movementInput.x * Mathf.Min(self.initialSpeed, initialSpeed), 0, movementInput.z * initialSpeed ); if (_cameraService?.IsCameraActivated is false) { moveVelocity = rotation * movementInput * initialSpeed; } if (self.limitSpeed.Allow) { switch (self.CurrentState) { case IPlayerWalkState: case IPlayerCrouchState: moveVelocity = Vector3.ClampMagnitude(moveVelocity, self.limitSpeed.Value); break; } } if (self.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 var newVelocity = Vector3.Lerp(currentVelocity, targetMovementVelocity, 1f - Mathf.Exp(-16 * deltaTime)); currentVelocity = newVelocity; if (self.ExpectJump.shouldBe) { actor.ForceNotGrounded(); // if (characterController.landFreeze.AllowUpdateWithoutReset is false) // { // currentVelocity = Vector3.Lerp(currentVelocity, default, 0.8f); // } currentVelocity.y+= initialJumpForce; self.ExpectJump.Reset(); self.ExecuteCommand(); } } else { if (moveVelocity.sqrMagnitude > 0f && self.landFreeze.AllowUpdateWithoutReset) { 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; } else { var tempVelocity = currentVelocity; tempVelocity = Vector3.Lerp(tempVelocity, default, 2*deltaTime); currentVelocity.x = tempVelocity.x; currentVelocity.z = tempVelocity.z; } // Gravity currentVelocity += -Vector3.up * (30 * deltaTime); // Drag currentVelocity *= (1f / (1f + 0.1f * deltaTime)); } } public override void UpdateRotation(ref Quaternion currentRotation, float deltaTime) { var newRotation = Quaternion.Euler(0, self.LookInput.y, 0); if (_cameraService.IsCameraActivated is false) { if (Physics.Raycast(_cameraService.CameraPosition, _cameraService.CameraRotation * Vector3.forward, out var hit, 256, LayerMask.GetMask("Default"), QueryTriggerInteraction.Ignore)) { self.ViewForward = (hit.point - (self.Position + self.Rotation * self.ViewCenter)).normalized; newRotation = Quaternion.LookRotation(self.ViewForward); self.ViewRotation = newRotation; self.FocusPoint = hit.point; Debug.DrawLine(_cameraService.CameraPosition, hit.point, Color.green, 0.1f); Debug.DrawLine(self.Position + self.ViewCenter, self.Position + self.ViewCenter + self.ViewRotation * Vector3.forward , Color.blue, 0.1f); } else { self.ViewRotation = Quaternion.Euler(self.LookInput);; self.FocusPoint = self.Position + self.ViewRotation * Vector3.forward * 256; Debug.DrawLine(_cameraService.CameraPosition, _cameraService.CameraRotation * Vector3.forward, Color.red, 0.1f); } var rotationDirection = _cameraService.CameraRotation * self.MovementInput; rotationDirection = Vector3.ProjectOnPlane(rotationDirection, Vector3.up); if (rotationDirection.sqrMagnitude >= 0.16f) { var newPlayerRotation =Quaternion.LookRotation(rotationDirection) ; currentRotation = Quaternion.Lerp(currentRotation, newPlayerRotation, 1f - Mathf.Exp(-16 * deltaTime)); } if (self.allowFocus) { currentRotation = Quaternion.LookRotation(_cameraService.CameraRotation * Vector3.forward, Vector3.up); } } else { currentRotation = newRotation; } } } [Serializable] public sealed class Walk : BasicMovement, IPlayerWalkState { [Inject] private InputActionGroup _inputActionGroup; public override void AfterUpdateMovement(float deltaTime) { base.AfterUpdateMovement(deltaTime); if (self.CurrentState != this) return; switch (self.ExpectRun.shouldBe, self.ExpectCrouch.shouldBe) { case (_,_) when self.topBlocked && self.IsGrounded: case (_, true): self.TransitionState(); return; case (true, false) when self.pauseRun.Allow is false && self.MovementInput.z > 0: self.TransitionState(); return; } } } [Serializable] public sealed class Crawl : BasicMovement,IPlayerCrawlState { [Inject] private IEquipService _equipService; public override void OnStateEntry(IState old) { _equipService.AllowEquip.AddDisableElements(this); self.LimitViewAngle = 20; base.OnStateEntry(old); } public override void AfterUpdateMovement(float deltaTime) { if (actor.IsGrounded is false) { self.TransitionState(); return; } self.CurrentCameraPosition.shouldBe = self.FpvLocalPosition; } public override void UpdateRotation(ref Quaternion currentRotation, float deltaTime) { var targetRotation = currentRotation; base.UpdateRotation(ref targetRotation,deltaTime); currentRotation = Quaternion.RotateTowards(currentRotation,targetRotation,90*deltaTime); } public override void OnStateExit(IState old, IState newState) { base.OnStateExit(old, newState); self.LimitViewAngle = 0; _equipService.AllowEquip.RemoveDisableElements(this); } } [Serializable] public sealed class Parachute : PlayerCharacterState, IPlayerParachuteState { [SerializeField] private float moveDamping = 3f; [SerializeField] private float damping = 1f; [Inject] private IEquipService _equipService; public override void OnStateEntry(IState old) { base.OnStateEntry(old); self.InvokeOpenParachute(); _equipService.AllowEquip.AddDisableElements(this); var velocity = actor.Velocity; velocity.y = Mathf.Clamp(velocity.y, -2, 2); actor.Velocity = velocity; } public override void OnStateExit(IState old, IState newState) { base.OnStateExit(old, newState); self.InvokeCloseParachute(); _equipService.AllowEquip.RemoveDisableElements(this); } public override void UpdateRotation(ref Quaternion currentRotation, float deltaTime) { currentRotation = Quaternion.Euler(0,self.LookInput.y,0); } public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime) { var rotation = Quaternion.Euler(0,self.LookInput.y,0); var moveVelocity = rotation * self.MovementInput; currentVelocity.y = Mathf.MoveTowards( currentVelocity.y, -8, damping * deltaTime ); currentVelocity+= moveVelocity.normalized * (moveDamping * deltaTime); currentVelocity.x = Mathf.Clamp(currentVelocity.x, -moveDamping, moveDamping); currentVelocity.z = Mathf.Clamp(currentVelocity.z, -moveDamping, moveDamping); } public override void AfterUpdateMovement(float deltaTime) { if (actor.IsGrounded) { self.TransitionState(); } self.ExpectParachute.Reset(); self.CurrentCameraPosition.shouldBe = self.FpvLocalPosition; } } [Serializable] public sealed class Run:BasicMovement,IPlayerRunState { public override void OnStateEntry(IState old) { self.ExpectRun.being = true; } public override void OnStateExit(IState old, IState newState) { self.ExpectRun.being = false; } public override void UpdateRotation(ref Quaternion currentRotation, float deltaTime) { if (_cameraService.IsCameraActivated is false) { base.UpdateRotation(ref currentRotation, deltaTime); return; } var baseRotation = Quaternion.identity; base.UpdateRotation(ref baseRotation, deltaTime); var targetRotation = Quaternion.LookRotation(baseRotation * self.MovementInput); float maxAngleDelta = 30; // 你可以根据需要调整最大角度差值 float maxDeltaMagnitude = Mathf.Sin(Mathf.Deg2Rad * maxAngleDelta / 2); targetRotation = Quaternion.RotateTowards(targetRotation,baseRotation , maxDeltaMagnitude); var max = Quaternion.RotateTowards(targetRotation, currentRotation, maxAngleDelta); var lerp = Quaternion.Lerp(currentRotation, targetRotation, 12 * deltaTime); if (Quaternion.Angle(currentRotation, max) < Quaternion.Angle(currentRotation, lerp)) { currentRotation = lerp; } else { currentRotation = max; } } public override void OnStateUpdate(float deltaTime) { base.OnStateUpdate(deltaTime); AudioSensorService.MakeNoise(actor.Position,self.transform,3,new MovementNoise()); } public override void AfterUpdateMovement(float deltaTime) { base.AfterUpdateMovement(deltaTime); switch (self.CurrentState) { case Walk: case Run: case Sprint: case Crouch: switch (self.ExpectRun.shouldBe, self.ExpectCrouch.shouldBe) { case (_, true): self.TransitionState(); return; case (false, false): self.TransitionState(); return; } if (_cameraService.IsCameraActivated) { if (self.MovementInput.z <= 0) { self.TransitionState(); return; } } else { if (self.MovementInput == default) { self.TransitionState(); return; } } if (self.pauseRun.Allow) { self.TransitionState(); return; } if (self.ExpectSprint.shouldBe && self.MovementInput.z > 0) { self.TransitionState(); return; } break; } } public override void ExecuteCommand(T command) { if (Enabled is false) return; if (command is PlayerCancelRunCommand) { self.TransitionState(); } } } [Serializable] public sealed class Slide : BasicMovement, IPlayerSlideState { [Header(nameof(Slide))] [SerializeField] private float additiveSpeed = 1f; [SerializeField] private float damping = 1; [SerializeField] private float stopSpeed = 0.64f; [SerializeField] private bool alwaysSlide; private bool _addedVelocity; public override void OnStateEntry(IState old) { base.OnStateEntry(old); self.ExpectCrouch.being = true; self.ExpectCrouch.shouldBe = true; self.ExecuteCommand(); _addedVelocity = false; } public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime) { if (alwaysSlide && actor.IsGrounded is false) { base.UpdateVelocity(ref currentVelocity, deltaTime); } else { if (_addedVelocity is false) { currentVelocity += currentVelocity.normalized * additiveSpeed; _addedVelocity = true; } else { currentVelocity = Vector3.Lerp(currentVelocity, Vector3.zero, deltaTime * damping); } } } public override void AfterUpdateMovement(float deltaTime) { if (self.ExpectRun.shouldBe) { self.TransitionState(); return; } if (self.ExpectCrouch.shouldBe is false || (alwaysSlide is false && actor.IsGrounded is false)) { self.TransitionState(); return; } if (actor.Velocity.sqrMagnitude <= stopSpeed) { self.TransitionState(); } self.CurrentCameraPosition.shouldBe = self.FpvLocalPosition; } } [Serializable] public sealed class Crouch:BasicMovement,IPlayerCrouchState { private readonly IntervalUpdate standInterval = new(0.1f); public override void OnStateEntry(IState old) { base.OnStateEntry(old); self.ExpectCrouch.being = true; standInterval.Reset(); } public override void OnStateExit(IState old, IState newState) { self.ExpectCrouch.being = false; } public override void AfterUpdateMovement(float deltaTime) { base.AfterUpdateMovement(deltaTime); if(self.topBlocked)standInterval.Reset(); if (self.ExpectRun.shouldBe) { self.TransitionState(); return; } if (self.ExpectCrouch.shouldBe is false) { if (self.topBlocked) { } else if(standInterval.AllowUpdateWithoutReset) { self.TransitionState(); } return; } } } [Serializable] public sealed class Sprint:BasicMovement,IPlayerSprintState { [SerializeField] private int slideCost = 16; [SerializeField] private int staminaCost = 1; public override void OnStateEntry(IState old) { self.ExpectSprint.being = true; } public override void OnStateExit(IState old, IState newState) { self.ExpectSprint.being = false; } public override void UpdateRotation(ref Quaternion currentRotation, float deltaTime) { if (_cameraService.IsCameraActivated is false) { base.UpdateRotation(ref currentRotation, deltaTime); return; } var baseRotation = Quaternion.identity; base.UpdateRotation(ref baseRotation, deltaTime); var targetRotation = Quaternion.LookRotation(baseRotation * self.MovementInput); float maxAngleDelta = 30; // 你可以根据需要调整最大角度差值 float maxDeltaMagnitude = Mathf.Sin(Mathf.Deg2Rad * maxAngleDelta / 2); targetRotation = Quaternion.RotateTowards(targetRotation,baseRotation , maxDeltaMagnitude); var max = Quaternion.RotateTowards(targetRotation, currentRotation, maxAngleDelta); var lerp = Quaternion.Lerp(currentRotation, targetRotation, 12 * deltaTime); if (Quaternion.Angle(currentRotation, max) < Quaternion.Angle(currentRotation, lerp)) { currentRotation = lerp; } else { currentRotation = max; } } public override void OnStateUpdate(float deltaTime) { if (actor.IsGrounded && actor.Velocity.GetLength() > self.initialSpeed/2) self.Stamina -= staminaCost * deltaTime; AudioSensorService.MakeNoise(actor.Position,self.transform); } public override void AfterUpdateMovement(float deltaTime) { base.AfterUpdateMovement(deltaTime); if (Enabled is false) return; if (self.pauseRun.Allow) { self.TransitionState(); return; } switch (self.ExpectRun.shouldBe,self.ExpectCrouch.shouldBe) { case (_,_) when self.Stamina is 0: self.ExpectSprint.Reset(); self.TransitionState(); return; case (_,true) when self.IsGrounded && self.Stamina > 0: self.Stamina -= slideCost; self.TransitionState(); return; case (_,true): self.TransitionState(); return; case (true,false) when self.MovementInput.z <=0: case (false,false): self.TransitionState(); return; } } public override void ExecuteCommand(T command) { if (Enabled is false) return; if(command is PlayerCancelRunCommand) self.TransitionState(); } } [Serializable] public sealed class Knockdown : BasicMovement, IPlayerKnockdownState { public override void Initialize() { base.Initialize(); knockdown.OnKnockdown += OnKnockdown; knockdown.OnRevive += OnRevive; } public override void OnStateEntry(IState old) { base.OnStateEntry(old); actor.UseRootMotion = true; } public override void OnStateExit(IState old, IState newState) { base.OnStateExit(old, newState); actor.UseRootMotion = false; } public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime) { base.UpdateVelocity(ref currentVelocity, deltaTime); if (knockdown.IsPressured) { currentVelocity.x = 0; currentVelocity.z = 0; } } public override void AfterUpdateMovement(float deltaTime) { base.AfterUpdateMovement(deltaTime); self.CurrentCameraPosition.shouldBe = self.FpvLocalPosition; } private void OnRevive() { if(Enabled)self.TransitionState(); } private void OnKnockdown() { self.TransitionState(); } } [Serializable] public sealed class Clip:PlayerCharacterState { [BITCommand] public static void NoClip() { _clipAction?.Invoke(); } private static Action _clipAction; [SerializeField] private InputActionReference clipAction; [SerializeReference, SubclassSelector] private IReference clipEnv; [Inject] private IHealth _health; [Inject] private InputActionGroup _inputActionGroup; public override void Initialize() { base.Initialize(); _health.OnSetAlive += OnSetAlive; Data.AddListener(clipEnv.Value, OnClip); _clipAction = _ClipAction; self.destroyCancellationToken.Register(() => { Data.RemoveListender(clipEnv.Value, OnClip); }); } private void _ClipAction() { var clip = Data.Get(clipEnv.Value); Data.Set(clipEnv.Value, !clip); } private async void OnClip(bool obj) { await UniTask.SwitchToMainThread(); if(_health.IsAlive is false) return; if (obj && Enabled is false) { self.TransitionState(); //BIT4Log.Log("NoClip Enabled"); }else if (Enabled && obj is false) { self.TransitionState(); //BIT4Log.Log("NoClip Disabled"); } } private void OnSetAlive(bool obj) { if (Enabled && obj is false) { self.TransitionState(); } } public override void OnStateEntry(IState old) { base.OnStateEntry(old); actor.Velocity = default; actor.ColliderComponent.enabled = false; actor.alwaysNotGrounded = true; } public override void OnStateExit(IState old, IState newState) { base.OnStateExit(old, newState); actor.ColliderComponent.enabled = true; actor.alwaysNotGrounded = false; } public override void AfterUpdateMovement(float deltaTime) { base.AfterUpdateMovement(deltaTime); actor.Position += self.ViewRotation * self.MovementInput * ( deltaTime * (_inputActionGroup.GetAction(self.RunAction).IsPressed() ? 8 : self.initialSpeed )); actor.Rotation = Quaternion.Euler(0,self.LookInput.y,0); } } }