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

465 lines
13 KiB
C#
Raw Normal View History

2023-08-23 01:59:40 +08:00
using System;
2023-10-20 19:31:12 +08:00
using System.Security.Permissions;
using System.Timers;
2023-08-27 02:58:19 +08:00
using BITFALL.Entities.Player.Movement.States;
2023-09-01 14:33:54 +08:00
using BITFALL.Player.Movement;
2023-08-23 01:59:40 +08:00
using BITKit;
using BITKit.Entities;
2023-10-20 19:31:12 +08:00
using BITKit.Entities.Movement;
2023-08-27 02:58:19 +08:00
using BITKit.Entities.Physics;
using BITKit.Entities.Player;
2023-08-23 01:59:40 +08:00
using Lightbug.CharacterControllerPro.Core;
2023-09-01 14:33:54 +08:00
using Unity.Mathematics;
2023-08-23 01:59:40 +08:00
using UnityEngine;
2023-10-20 19:31:12 +08:00
using UnityEngine.AI;
2023-08-23 01:59:40 +08:00
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Interactions;
2023-08-27 02:58:19 +08:00
using UnityEngine.UIElements;
2023-08-23 01:59:40 +08:00
2023-08-27 02:58:19 +08:00
namespace BITFALL.Entities.Player.Movement
2023-08-23 01:59:40 +08:00
{
2023-09-01 14:33:54 +08:00
[CustomType(typeof(IEntityMovement))]
[CustomType(typeof(IPlayerMovement))]
2023-10-30 01:25:53 +08:00
public class PlayerCharacterController : StateBasedPlayerBehavior<IEntityMovementState>,IEntityMovement,IPlayerMovement
2023-08-23 01:59:40 +08:00
{
[SerializeField] private CharacterActor actor;
2023-09-01 14:33:54 +08:00
[SerializeField] private Vector3 initialCameraPosition = new(0,0.11f,0.27f);
2023-08-27 02:58:19 +08:00
[SerializeField] private float initialSpeed;
[SerializeReference,SubclassSelector] private IClosePoint climbClosePoint;
2023-08-23 01:59:40 +08:00
[SerializeReference, SubclassSelector]
private IProvider adsProvider;
2023-10-02 23:24:56 +08:00
[SerializeField] private LocationAdditive locationAdditive;
2023-10-20 19:31:12 +08:00
[SerializeField] private Transform fpvPoint;
2023-08-23 01:59:40 +08:00
2023-10-24 23:37:59 +08:00
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;
2023-09-01 14:33:54 +08:00
public Vector3 ViewCenter { get; set; }
public Quaternion ViewRotation { get; set; }
2023-08-27 02:58:19 +08:00
public Vector3 LocomotionBasedVelocity { get;private set; }
2023-08-23 01:59:40 +08:00
public Vector3 Velocity => actor.Velocity;
public Vector3 GroundVelocity=>actor.GroundVelocity;
2023-09-01 14:33:54 +08:00
public Vector3 AngularVelocity { get;private set; }
2023-08-23 01:59:40 +08:00
public bool IsGrounded => actor.IsGrounded;
public Vector3 MovementInput { get; private set; }
public Vector2 LookInput{ get; private set; }
public ExpectState<bool> ExpectRun;
public ExpectState<bool> ExpectJump;
2023-10-20 19:31:12 +08:00
public ExpectState<bool> ExpectParachute;
2023-08-23 01:59:40 +08:00
public ExpectState<bool> ExpectCrouch;
public ExpectState<bool> ExpectSprint;
2023-10-02 23:24:56 +08:00
public ExpectState<Vector3> ExpectClimb;
public ExpectState<Vector3> CurrentCameraPosition;
2023-10-20 19:31:12 +08:00
public OffMeshLink OffMeshLink { get; private set; }
[Header(Constant.Header.Gameobjects)]
[SerializeField] private Transform parachuteModel;
2023-08-27 02:58:19 +08:00
private readonly ValidHandle allowMovement = new();
2023-09-01 14:33:54 +08:00
private readonly ValidHandle allowRun = new();
2023-10-20 19:31:12 +08:00
[Inject]
2023-08-27 02:58:19 +08:00
private IEntityPhysics physics;
2023-10-20 19:31:12 +08:00
[Inject]
2023-08-27 02:58:19 +08:00
private IHealth _health;
2023-10-20 19:31:12 +08:00
2023-08-27 02:58:19 +08:00
private bool isDead;
private Vector3 keepVelocity;
2023-10-20 19:31:12 +08:00
private readonly DoubleBuffer<Vector3> gravityDamage = new();
private readonly DoubleBuffer<float> gravityDamping = new();
private Vector3 cacheGravity;
2023-08-23 01:59:40 +08:00
public override void OnAwake()
{
2023-10-20 19:31:12 +08:00
base.OnAwake();
2023-08-27 02:58:19 +08:00
_health.OnSetAlive += OnSetAlive;
physics.OnSetPhysics += OnSetPhysics;
2023-10-20 19:31:12 +08:00
LookInput = MathV.TransientRotationAxis(transform.rotation.eulerAngles);
parachuteModel.gameObject.SetActive(false);
2023-08-27 02:58:19 +08:00
}
2023-10-04 16:50:27 +08:00
public override void OnStart()
{
2023-10-20 19:31:12 +08:00
base.OnStart();
2023-10-04 16:50:27 +08:00
foreach (var x in GetComponentsInChildren<Collider>(true))
{
actor.PhysicsComponent.IgnoreCollision(x.transform,true);
2023-10-20 19:31:12 +08:00
}
TransitionState<Walk>();
2023-10-04 16:50:27 +08:00
}
2023-08-27 02:58:19 +08:00
private void OnSetPhysics(bool obj)
{
if (obj is false) return;
physics.Velocity = keepVelocity;
actor.Velocity = Vector3.zero;
}
private void OnSetAlive(bool obj)
{
allowMovement.SetElements(this,obj);
2023-09-01 14:33:54 +08:00
allowRun.SetElements(this,obj);
2023-08-27 02:58:19 +08:00
if (obj)
{
if (!isDead) return;
actor.Position = physics.Center;
isDead = false;
}
else
{
isDead = true;
}
2023-10-20 19:31:12 +08:00
Enabled = true;
TransitionState<Walk>();
2023-08-23 01:59:40 +08:00
}
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<Vector2>();
MovementInput = new Vector3(MovementInput.x,0,MovementInput.y);
if (!(MovementInput.z <= 0.16f)) return;
ExpectRun.Reset();
ExpectSprint.Reset();
}
2023-09-01 14:33:54 +08:00
public void ExecuteCommand<T>(T command=default)
2023-08-23 01:59:40 +08:00
{
foreach (var x in StateDictionary.Values)
{
x.ExecuteCommand<T>(command);
}
2023-09-01 14:33:54 +08:00
switch (command)
{
case PlayerEnableRunCommand enableRunCommand:
allowRun.RemoveDisableElements(enableRunCommand.Lock);
ExpectRun.Reset();
ExpectSprint.Reset();
break;
case PlayerDisableRunCommand disableRunCommand:
allowRun.AddDisableElements(disableRunCommand.Lock);
break;
2023-10-20 19:31:12 +08:00
case PlayerChangeVelocityCommand changeVelocityCommand:
actor.Velocity = Vector3.Lerp(actor.Velocity, changeVelocityCommand.Velocity, 5 * Time.deltaTime);
break;
case PlayerAddGravityDampingCommand addGravityDampingCommand:
gravityDamping.Release(addGravityDampingCommand.Damping);
break;
2023-09-01 14:33:54 +08:00
}
OnCommand?.Invoke(command);
2023-08-23 01:59:40 +08:00
}
2023-09-01 14:33:54 +08:00
public event Action<object> OnCommand;
2023-08-23 01:59:40 +08:00
public void View(InputAction.CallbackContext context)
{
if (BITAppForUnity.AllowCursor) return;
var playerConfig = Data.Get<PlayerConfig>() ?? new PlayerConfig();
var ads = adsProvider.Get<float>();
if (ads is 0) ads = 1;
2023-10-02 23:24:56 +08:00
var raw = context.ReadValue<Vector2>() * playerConfig.Sensitivity * playerConfig.M_Yaw * ads;
2023-08-23 01:59:40 +08:00
var lookInput = LookInput;
lookInput.x -= raw.y;
lookInput.y += raw.x;
lookInput.x = Mathf.Clamp(lookInput.x, -80, 80);
LookInput = lookInput;
2023-10-20 19:31:12 +08:00
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);
}
}
2023-09-01 14:33:54 +08:00
AngularVelocity = new Vector3
(
raw.y,
-raw.x
);
2023-08-23 01:59:40 +08:00
}
public void Jump(InputAction.CallbackContext context)
{
if (ExpectJump.shouldBe) return;
2023-10-20 19:31:12 +08:00
if (context is not {interaction:PressInteraction , performed:true}) return;
switch (CurrentState)
2023-08-27 02:58:19 +08:00
{
2023-10-20 19:31:12 +08:00
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<OffMeshLink>(out var offMeshLink)) continue;
var toTarget = x.transform.position - transform.position;
2023-10-30 01:25:53 +08:00
2023-10-20 19:31:12 +08:00
toTarget = Vector3.ProjectOnPlane(toTarget, Vector3.up);
// 获取正前方的向量
var forward = actor.Forward;
// 计算点积
var dotProduct = Vector3.Dot(toTarget.normalized, forward);
if (dotProduct < 0.8f) continue;
OffMeshLink = offMeshLink;
TransitionState<Link>();
return;
}
2023-10-24 23:37:59 +08:00
if (MovementInput.x is not 0 && MovementInput.z is 0)
{
TransitionState<Dodge>();
return;
}
2023-10-20 19:31:12 +08:00
if (climbClosePoint.TryGetClosePoint(out var closePoint))
{
ExpectClimb.shouldBe = closePoint;
TransitionState<Climb>();
return;
}
switch (actor.IsGrounded)
{
case true:
ExpectJump.shouldBe = true;
break;
case false when actor.VerticalVelocity.y<=-16f:
ExpectParachute.shouldBe = true;
return;
}
break;
2023-08-27 02:58:19 +08:00
}
2023-10-20 19:31:12 +08:00
ExpectCrouch.Reset();
2023-08-23 01:59:40 +08:00
}
public void Run(InputAction.CallbackContext context)
{
2023-09-01 14:33:54 +08:00
if (allowRun.Allow is false) return;
2023-08-23 01:59:40 +08:00
switch (context)
{
2023-10-20 19:31:12 +08:00
//case { interaction: PressInteraction, started: true }:
case { interaction: PressInteraction, performed:true }:
2023-11-02 20:58:55 +08:00
if (ExpectRun.shouldBe && Stamina> 0)
2023-08-23 01:59:40 +08:00
ExpectSprint.shouldBe = true;
ExpectRun.shouldBe = true;
ExpectCrouch.Reset();
break;
case {interaction:MultiTapInteraction,performed:true}:
2023-10-20 19:31:12 +08:00
ExpectSprint.shouldBe = ExpectRun.shouldBe = true;
2023-08-23 01:59:40 +08:00
ExpectCrouch.Reset();
break;
}
}
public void Crouch(InputAction.CallbackContext context)
{
if (context.interaction is not null && context.started is false) return;
2023-08-27 02:58:19 +08:00
if (CurrentState is Climb)
{
TransitionState<Walk>();
}
else
{
ExpectCrouch.shouldBe = !ExpectCrouch.shouldBe;
ExpectRun.Reset();
}
2023-08-23 01:59:40 +08:00
}
public override void OnUpdate(float deltaTime)
{
CurrentState?.BeforeUpdateMovement(deltaTime);
2023-10-02 23:24:56 +08:00
2023-10-20 19:31:12 +08:00
UpdateState(deltaTime);
2023-10-02 23:24:56 +08:00
CurrentState?.AfterUpdateMovement(deltaTime);
2023-08-23 01:59:40 +08:00
}
2023-10-02 23:24:56 +08:00
2023-08-23 01:59:40 +08:00
public override void OnFixedUpdate(float deltaTime)
{
var currentVelocity = actor.Velocity;
var currentRotation = actor.Rotation;
2023-10-20 19:31:12 +08:00
if (Stamina is not 100 && staminaRecoveryInterval.AllowUpdateWithoutReset)
{
Stamina+=10 * deltaTime;
}
2023-08-27 02:58:19 +08:00
CurrentState?.UpdateVelocity(ref currentVelocity, deltaTime);
CurrentState?.UpdateRotation(ref currentRotation, deltaTime);
2023-10-20 19:31:12 +08:00
if (gravityDamping.TryGetRelease(out var damping) && currentVelocity.y < 0)
{
currentVelocity.y = Mathf.Clamp(currentVelocity.y + damping, float.MinValue, 0);
}
2023-08-27 02:58:19 +08:00
2023-10-20 19:31:12 +08:00
if (allowMovement && _health.IsAlive && actor.RigidbodyComponent.IsKinematic is false)
2023-08-27 02:58:19 +08:00
{
actor.Velocity = currentVelocity;
actor.Rotation = currentRotation;
if (currentVelocity.sqrMagnitude > 0.01f)
keepVelocity = currentVelocity;
}
var localVelocity = transform.InverseTransformDirection(Velocity);
localVelocity.y = 0;
LocomotionBasedVelocity = localVelocity;
2023-08-23 01:59:40 +08:00
2023-10-20 19:31:12 +08:00
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:
2023-10-30 01:25:53 +08:00
UnityEntity.Invoke<DamageMessage>(new DamageMessage()
2023-10-20 19:31:12 +08:00
{
2023-10-24 23:37:59 +08:00
Damage = value < -30 ? int.MaxValue : (int)math.abs(value) * 2 ,
DamageType = new GravityDamage(),
Location = new Location()
2023-10-20 19:31:12 +08:00
{
position = actor.Position,
rotation = actor.Rotation
},
2023-10-30 01:25:53 +08:00
Initiator = UnityEntity,
Target = UnityEntity,
2023-10-20 19:31:12 +08:00
});
break;
}
}
}
2023-08-23 01:59:40 +08:00
public override void OnLateUpdate(float deltaTime)
{
2023-08-27 02:58:19 +08:00
if (allowMovement.Allow is false) return;
2023-10-02 23:24:56 +08:00
var additiveTransform = locationAdditive.transform;
2023-08-23 01:59:40 +08:00
var rotation = Quaternion.Euler(LookInput);
2023-10-20 19:31:12 +08:00
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);
}
}
2023-09-01 14:33:54 +08:00
2023-10-02 23:24:56 +08:00
locationAdditive.SetGlobalRotation(rotation);
CurrentCameraPosition.being = Vector3.Lerp(CurrentCameraPosition.being,CurrentCameraPosition.shouldBe,5 * deltaTime);
locationAdditive.AddPosition(CurrentCameraPosition);
2023-09-01 14:33:54 +08:00
ViewRotation = rotation;
2023-10-02 23:24:56 +08:00
2023-10-24 23:37:59 +08:00
//ViewCenter = additiveTransform.position + additiveTransform.forward;
ViewCenter = additiveTransform.position + Quaternion.Euler(LookInput) * Vector3.forward;
2023-09-01 14:33:54 +08:00
}
2023-10-20 19:31:12 +08:00
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<float> OnStaminaChanged;
public float3 FpvPosition => fpvPoint.position;
public quaternion FpvRotation => fpvPoint.rotation;
public float3 FpvLocalPosition =>fpvPoint.localPosition;
public quaternion FpvLocalRotation => fpvPoint.localRotation;
2023-09-01 14:33:54 +08:00
public void AddViewEuler(float2 euler)
{
LookInput = new Vector3
{
x = LookInput.x + euler.x,
y = LookInput.y + euler.y
};
2023-08-23 01:59:40 +08:00
}
2023-10-20 19:31:12 +08:00
public event Func<bool> 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);
}
2023-08-23 01:59:40 +08:00
}
}