using System; using System.Security.Permissions; using System.Timers; using BITFALL.Entities.Player.Movement.States; using BITFALL.Player.Equip; using BITFALL.Player.Movement; using BITKit; using BITKit.Entities; using BITKit.Entities.Movement; using BITKit.Entities.Physics; using BITKit.Entities.Player; using Lightbug.CharacterControllerPro.Core; using Unity.Mathematics; using UnityEngine; using UnityEngine.AI; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Interactions; using UnityEngine.UIElements; namespace BITFALL.Entities.Player.Movement { [CustomType(typeof(IEntityMovement))] [CustomType(typeof(IPlayerMovement))] public class PlayerCharacterController : StateBasedPlayerBehavior,IEntityMovement,IPlayerMovement { [Header(Constant.Header.Settings)] [SerializeField] internal Vector3 initialCameraPosition = new(0,0.11f,0.27f); [SerializeField] internal float initialSpeed; [Header(Constant.Header.Components)] [SerializeField] private CharacterActor actor; [SerializeField] private LocationAdditive locationAdditive; [Header(Constant.Header.Gameobjects)] [SerializeField] private Transform fpvPoint; [SerializeField] private Transform parachuteModel; [Header(Constant.Header.Input)] [SerializeField] private InputActionReference movementAction; [SerializeField] private InputActionReference viewAction; [SerializeField] private InputActionReference jumpAction; [SerializeField] private InputActionReference crouchAction; [SerializeField] private InputActionReference runAction; [Header(Constant.Header.Providers)] [SerializeReference,SubclassSelector] internal IClosePoint climbClosePoint; [SerializeReference, SubclassSelector] private IProvider adsProvider; public Vector3 Position { get => actor.Position; set=>actor.Position = value; } public Quaternion Rotation { get => actor.Rotation; set => actor.Rotation = value; } public Vector3 Forward => actor.Forward; public Vector3 ViewCenter { get; set; } public Quaternion ViewRotation { get; set; } public Vector3 LocomotionBasedVelocity { get;private set; } public Vector3 Velocity => actor.Velocity; public Vector3 GroundVelocity=>actor.GroundVelocity; public Vector3 AngularVelocity { get;private set; } public bool IsGrounded => actor.IsGrounded; public Vector3 MovementInput { get; private set; } public Vector2 LookInput{ get; private set; } public ExpectState ExpectRun; public ExpectState ExpectJump; public ExpectState ExpectParachute; public ExpectState ExpectCrouch; public ExpectState ExpectSprint; public bool RequestClimb { get;internal set; } public ExpectState ExpectClimb; public ExpectState CurrentCameraPosition; public OffMeshLink OffMeshLink { get; private set; } internal readonly ValidHandle allowMovement = new(); internal readonly ValidHandle allowRun = new(); internal readonly ValidHandle pauseRun = new(); private bool isDead; private Vector3 keepVelocity; private readonly DoubleBuffer gravityDamage = new(); private readonly DoubleBuffer gravityDamping = new(); private Vector3 cacheGravity; [Inject] private IEntityPhysics _physics; [Inject] private IHealth _health; [Inject] private InputActionGroup _inputActionGroup; public override void OnAwake() { base.OnAwake(); _health.OnSetAlive += OnSetAlive; _physics.OnSetPhysics += OnSetPhysics; LookInput = MathV.TransientRotationAxis(transform.rotation.eulerAngles); parachuteModel.gameObject.SetActive(false); } public override void OnStart() { base.OnStart(); foreach (var x in GetComponentsInChildren(true)) { actor.PhysicsComponent.IgnoreCollision(x.transform,true); } TransitionState(); _inputActionGroup.RegisterCallback(movementAction, Movement); _inputActionGroup.RegisterCallback(viewAction, View); _inputActionGroup.RegisterCallback(jumpAction, Jump); _inputActionGroup.RegisterCallback(crouchAction, Crouch); _inputActionGroup.RegisterCallback(runAction, Run); } private void OnSetPhysics(bool obj) { if (obj is false) return; //_physics.Velocity = keepVelocity; _physics.Velocity = default; actor.Velocity = Vector3.zero; } private void OnSetAlive(bool obj) { allowMovement.SetElements(this,obj); allowRun.SetElements(this,obj); if (obj) { if (!isDead) return; actor.Position = _physics.Center; isDead = false; } else { isDead = true; } Enabled = true; TransitionState(); } public void SyncMovement(Vector3 velocity, Vector3 position, Quaternion rotation, bool isGrounded) { throw new NotImplementedException(); } public void Movement(Vector3 relativeVector) { MovementInput = relativeVector; } public void Movement(InputAction.CallbackContext context) { MovementInput = context.ReadValue(); MovementInput = new Vector3(MovementInput.x,0,MovementInput.y); if (!(MovementInput.z <= 0.16f)) return; ExpectRun.Reset(); ExpectSprint.Reset(); } public void ExecuteCommand(T command=default) { foreach (var x in StateDictionary.Values) { x.ExecuteCommand(command); } switch (command) { case PlayerEnableRunCommand enableRunCommand: allowRun.RemoveDisableElements(enableRunCommand.Lock); ExpectRun.Reset(); ExpectSprint.Reset(); break; case PlayerDisableRunCommand disableRunCommand: allowRun.AddDisableElements(disableRunCommand.Lock); break; case PlayerCancelRunCommand: ExpectRun.Reset(); ExpectSprint.Reset(); break; case PlayerChangeVelocityCommand changeVelocityCommand: actor.Velocity = Vector3.Lerp(actor.Velocity, changeVelocityCommand.Velocity, 5 * Time.deltaTime); break; case PlayerAddGravityDampingCommand addGravityDampingCommand: gravityDamping.Release(addGravityDampingCommand.Damping); break; case PlayerPauseRunCommand pauseRunCommand: pauseRun.SetElements(this,pauseRunCommand.Pause); break; } OnCommand?.Invoke(command); } public event Action OnCommand; public void View(InputAction.CallbackContext context) { if (BITAppForUnity.AllowCursor) return; var playerConfig = PlayerConfig.Singleton; var ads = adsProvider.Get(); if (ads is 0) ads = 1; var raw = context.ReadValue() * playerConfig.Sensitivity * playerConfig.M_Yaw * ads; var lookInput = LookInput; lookInput.x -= raw.y; lookInput.y += raw.x; lookInput.x = Mathf.Clamp(lookInput.x, -80, 80); LookInput = lookInput; var rotation = Quaternion.Euler(LookInput); if (LimitViewAngle is not 0) { float angleDifference = Quaternion.Angle(rotation, FpvRotation); // 如果角度差大于最大允许角度差,进行限制 if (angleDifference > LimitViewAngle) { //将当前旋转限制在最大允许角度差的范围内 rotation = Quaternion.Lerp( rotation, Quaternion.RotateTowards(FpvRotation,rotation , LimitViewAngle), 5 * Time.deltaTime ); //rotation = Quaternion.RotateTowards(FpvRotation, rotation, LimitViewAngle); LookInput = MathV.TransientRotationAxis(rotation.eulerAngles); } } AngularVelocity = new Vector3 ( raw.y, -raw.x ); } public void Jump(InputAction.CallbackContext context) { RequestClimb = context switch { { interaction: PressInteraction, performed: true } => true, { interaction: PressInteraction, canceled: true } => false, _ => RequestClimb }; if (ExpectJump.shouldBe) return; if (context is not {interaction:PressInteraction , performed:true}) return; switch (CurrentState) { case IPlayerLinkState: ExpectJump.shouldBe = true; break; case IPlayerWalkState: case IPlayerRunState: case IPlayerSprintState: case IPlayerCrouchState: case IPlayerSlideState: foreach (var x in Physics.OverlapSphere(actor.Position, 1, LayerMask.GetMask("Dynamic"))) { if (!x.TryGetComponent(out var offMeshLink)) continue; switch (offMeshLink.area) { case 5: break; default: var toTarget = x.transform.position - transform.position; toTarget = Vector3.ProjectOnPlane(toTarget, Vector3.up); // 获取正前方的向量 var forward = actor.Forward; // 计算点积 var dotProduct = Vector3.Dot(toTarget.normalized, forward); if (dotProduct < 0.8f) continue; break; } OffMeshLink = offMeshLink; TransitionState(); return; } if (MovementInput.x is not 0 && MovementInput.z is 0) { TransitionState(); return; } if (climbClosePoint.TryGetClosePoint(out var closePoint)) { ExpectClimb.shouldBe = closePoint; TransitionState(); return; } switch (actor.IsGrounded) { case true: ExpectJump.shouldBe = true; break; case false when actor.VerticalVelocity.y<=-16f: ExpectParachute.shouldBe = true; return; } break; } ExpectCrouch.Reset(); } public void Run(InputAction.CallbackContext context) { if (allowRun.Allow is false) return; var holdToRun = Data.Get(BITConstant.Environment.cl_hold_to_run); switch (context) { //case { interaction: PressInteraction, started: true }: case { interaction: PressInteraction, canceled:true } when holdToRun: ExpectRun.Reset(); break; case { interaction: PressInteraction, performed:true }: if (holdToRun) { ExpectRun.shouldBe = true; } else { if (ExpectRun.shouldBe && Stamina> 0) ExpectSprint.shouldBe = true; ExpectRun.shouldBe = true; ExpectCrouch.Reset(); } break; case {interaction:MultiTapInteraction,performed:true}: ExpectSprint.shouldBe = ExpectRun.shouldBe = true; ExpectCrouch.Reset(); break; } } public void Crouch(InputAction.CallbackContext context) { if (context.interaction is not null && context.started is false) return; if (CurrentState is Climb) { TransitionState(); } else { ExpectCrouch.shouldBe = !ExpectCrouch.shouldBe; ExpectRun.Reset(); } } public override void OnUpdate(float deltaTime) { CurrentState?.BeforeUpdateMovement(deltaTime); UpdateState(deltaTime); CurrentState?.AfterUpdateMovement(deltaTime); } public override void OnFixedUpdate(float deltaTime) { var currentVelocity = actor.Velocity; var currentRotation = actor.Rotation; if (Stamina is not 100 && staminaRecoveryInterval.AllowUpdateWithoutReset) { Stamina+=10 * deltaTime; } CurrentState?.UpdateVelocity(ref currentVelocity, deltaTime); CurrentState?.UpdateRotation(ref currentRotation, deltaTime); if (gravityDamping.TryGetRelease(out var damping) && currentVelocity.y < 0) { currentVelocity.y = Mathf.Clamp(currentVelocity.y + damping, float.MinValue, 0); } if (allowMovement && _health.IsAlive && actor.RigidbodyComponent.IsKinematic is false) { actor.Velocity = currentVelocity; actor.Rotation = currentRotation; //if (currentVelocity.sqrMagnitude > 0.01f) keepVelocity = currentVelocity; } var localVelocity = transform.InverseTransformDirection(Velocity); localVelocity.y = 0; LocomotionBasedVelocity = localVelocity; if(actor.IsStable is false && actor.IsGrounded is false) { if (cacheGravity != default && (actor.VerticalVelocity - cacheGravity).sqrMagnitude < 4) { gravityDamage.Release(actor.VerticalVelocity); } cacheGravity = actor.VerticalVelocity; //Debug.Log($"IsGround:{actor.IsGrounded}\tIsStable:{actor.IsStable}\tVelocity:{actor.VerticalVelocity}"); } else if (actor.IsStable && gravityDamage.TryGetRelease(out var currentGravity)) { cacheGravity = default; var value = currentGravity.y; //Debug.Log($"Vector:{currentVelocity}\tMagnitude:{value}\tGravity:{currentGravity}"); //Debug.Log(value); switch (value) { case > 0: break; case < -16: UnityEntity.Invoke(new DamageMessage() { Damage = value < -30 ? int.MaxValue : (int)math.abs(value) * 2 , DamageType = new GravityDamage(), Location = new Location() { position = actor.Position, rotation = actor.Rotation }, Initiator = UnityEntity, Target = UnityEntity, }); break; } } } public override void OnLateUpdate(float deltaTime) { if (allowMovement.Allow is false) return; var additiveTransform = locationAdditive.transform; var rotation = Quaternion.Euler(LookInput); if (LimitViewAngle is not 0) { float angleDifference = Quaternion.Angle(rotation, FpvRotation); // 如果角度差大于最大允许角度差,进行限制 if (angleDifference > LimitViewAngle) { // 将当前旋转限制在最大允许角度差的范围内 rotation = Quaternion.Lerp( rotation, Quaternion.RotateTowards(FpvRotation,rotation , LimitViewAngle), 5 * deltaTime ); LookInput = MathV.TransientRotationAxis(rotation.eulerAngles); } } locationAdditive.SetGlobalRotation(rotation); CurrentCameraPosition.being = Vector3.Lerp(CurrentCameraPosition.being,CurrentCameraPosition.shouldBe,5 * deltaTime); locationAdditive.AddPosition(CurrentCameraPosition); ViewRotation = rotation; //ViewCenter = additiveTransform.position + additiveTransform.forward; ViewCenter = additiveTransform.position + Quaternion.Euler(LookInput) * Vector3.forward; } private float _stamina = 100; public float Stamina { get=>_stamina; set { if (value < _stamina) { staminaRecoveryInterval.Reset(); } _stamina = Mathf.Clamp(value, 0, 100); OnStaminaChanged?.Invoke(_stamina); } } private readonly IntervalUpdate staminaRecoveryInterval = new(1); public int LimitViewAngle { get; set; } public event Action OnStaminaChanged; public float3 FpvPosition => fpvPoint.position; public quaternion FpvRotation => fpvPoint.rotation; public float3 FpvLocalPosition =>fpvPoint.localPosition; public quaternion FpvLocalRotation => fpvPoint.localRotation; public void AddViewEuler(float2 euler) { LookInput = new Vector3 { x = LookInput.x + euler.x, y = LookInput.y + euler.y }; LookInput = MathV.TransientRotationAxis(LookInput); } public event Func TryOpenParachute; public event Action OnParachuteOpened; public event Action OnParachuteClosed; internal void InvokeOpenParachute() { OnParachuteOpened?.Invoke(); parachuteModel.gameObject.SetActive(true); } internal void InvokeCloseParachute() { OnParachuteClosed?.Invoke(); parachuteModel.gameObject.SetActive(false); } } }