BITFALL/Assets/Artists/Scripts/Player/CharacterControllerPro/PlayerCharacterStates.cs

810 lines
22 KiB
C#

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<Knockdown>();
}
if (knockdown.IsKnockdown is false && self.ExpectParachute.shouldBe)
{
self.TransitionState<Parachute>();
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<Climb>();
// return;
// }
if (self.vaultPoint.TryGetClosePoint(out var closePoint))
{
self.ExpectClimb.shouldBe = closePoint;
self.TransitionState<Vault>();
self.ExpectJump.Reset();
self.RequestClimb = false;
return;
}
if (self.climbClosePoint.TryGetClosePoint(out closePoint))
{
self.ExpectClimb.shouldBe = closePoint;
self.TransitionState<Climb>();
self.ExpectJump.Reset();
self.RequestClimb = false;
return;
}
if (self.edgeClimbPoint.TryGetClosePoint(out closePoint))
{
self.ExpectClimb.shouldBe = closePoint;
self.TransitionState<EdgeClimb>();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<OnPlayerJumpCommand>();
}
}
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<Crouch>();
return;
case (true, false) when self.pauseRun.Allow is false &&
self.MovementInput.z > 0:
self.TransitionState<Run>();
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<Walk>();
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<Walk>();
}
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<Crouch>();
return;
case (false, false):
self.TransitionState<Walk>();
return;
}
if (_cameraService.IsCameraActivated)
{
if (self.MovementInput.z <= 0)
{
self.TransitionState<Walk>();
return;
}
}
else
{
if (self.MovementInput == default)
{
self.TransitionState<Walk>();
return;
}
}
if (self.pauseRun.Allow)
{
self.TransitionState<Walk>();
return;
}
if (self.ExpectSprint.shouldBe && self.MovementInput.z > 0)
{
self.TransitionState<Sprint>();
return;
}
break;
}
}
public override void ExecuteCommand<T>(T command)
{
if (Enabled is false) return;
if (command is PlayerCancelRunCommand)
{
self.TransitionState<Walk>();
}
}
}
[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<PlayerCancelRunCommand>();
_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<Run>();
return;
}
if (self.ExpectCrouch.shouldBe is false || (alwaysSlide is false && actor.IsGrounded is false))
{
self.TransitionState<Walk>();
return;
}
if (actor.Velocity.sqrMagnitude <= stopSpeed)
{
self.TransitionState<Crouch>();
}
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<Run>();
return;
}
if (self.ExpectCrouch.shouldBe is false)
{
if (self.topBlocked)
{
}
else if(standInterval.AllowUpdateWithoutReset)
{
self.TransitionState<Walk>();
}
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<Walk>();
return;
}
switch (self.ExpectRun.shouldBe,self.ExpectCrouch.shouldBe)
{
case (_,_) when self.Stamina is 0:
self.ExpectSprint.Reset();
self.TransitionState<Run>();
return;
case (_,true) when self.IsGrounded && self.Stamina > 0:
self.Stamina -= slideCost;
self.TransitionState<Slide>();
return;
case (_,true):
self.TransitionState<Crouch>();
return;
case (true,false) when self.MovementInput.z <=0:
case (false,false):
self.TransitionState<Walk>();
return;
}
}
public override void ExecuteCommand<T>(T command)
{
if (Enabled is false) return;
if(command is PlayerCancelRunCommand)
self.TransitionState<Walk>();
}
}
[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<Walk>();
}
private void OnKnockdown()
{
self.TransitionState<Knockdown>();
}
}
[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<bool>(clipEnv.Value, OnClip);
_clipAction = _ClipAction;
self.destroyCancellationToken.Register(() =>
{
Data.RemoveListender<bool>(clipEnv.Value, OnClip);
});
}
private void _ClipAction()
{
var clip = Data.Get<bool>(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<Clip>();
//BIT4Log.Log<Clip>("NoClip Enabled");
}else if (Enabled && obj is false)
{
self.TransitionState<Walk>();
//BIT4Log.Log<Clip>("NoClip Disabled");
}
}
private void OnSetAlive(bool obj)
{
if (Enabled && obj is false)
{
self.TransitionState<Walk>();
}
}
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);
}
}
}