1530 lines
48 KiB
C#
1530 lines
48 KiB
C#
![]() |
using System.Linq;
|
||
|
using BITKit;
|
||
|
using BITKit.Entities;
|
||
|
using BITKit.Physics;
|
||
|
using BITKit.StateMachine;
|
||
|
using DrawXXL;
|
||
|
using Kinemation.MotionWarping.Runtime.Core;
|
||
|
using Kinemation.MotionWarping.Runtime.Utility;
|
||
|
using Lightbug.CharacterControllerPro.Core;
|
||
|
using Microsoft.Extensions.Logging;
|
||
|
using Net.Project.B.Inventory;
|
||
|
using Net.Project.B.World;
|
||
|
using Net.Project.B.WorldNode;
|
||
|
using Project.B.Player;
|
||
|
using Unity.Mathematics;
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.InputSystem;
|
||
|
|
||
|
namespace Project.B.CharacterController
|
||
|
{
|
||
|
public abstract class CharacterStateBase:ICharacterState,ICharacterStateData
|
||
|
{
|
||
|
protected readonly PlayerCharacterController Self;
|
||
|
protected CharacterStateBase(ICharacterController characterController)
|
||
|
{
|
||
|
Self=characterController as PlayerCharacterController;
|
||
|
}
|
||
|
public virtual float BaseHeight { get; set; } = 1.6f;
|
||
|
public virtual float BaseSpeed { get; set; } = 2.5f;
|
||
|
public virtual float2 ViewOffset { get; set; } = new float2(1.4f, 0.16f);
|
||
|
public bool Enabled { get; set; }
|
||
|
protected IState EntryState { get; private set; }
|
||
|
public virtual void Initialize()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
public virtual void OnStateEntry(IState old)
|
||
|
{
|
||
|
EntryState = old;
|
||
|
}
|
||
|
|
||
|
public virtual void OnStateUpdate(float deltaTime)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
public virtual void OnStateExit(IState old, IState newState)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
public virtual void UpdateVelocity(ref float3 currentVelocity, float deltaTime)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
public virtual void UpdateRotation(ref float3 localPosition, ref quaternion currentRotation,ref quaternion viewRotation, float deltaTime)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
public virtual void BeforeUpdateVelocity(float deltaTime)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
public virtual void AfterUpdateVelocity(float deltaTime)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
protected virtual void Exit()
|
||
|
{
|
||
|
Self.TransitionState(EntryState as ICharacterState);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public abstract class CharacterMovementState : CharacterStateBase
|
||
|
{
|
||
|
protected readonly ICharacterController CharacterController;
|
||
|
protected virtual bool UseViewPosition => false;
|
||
|
protected virtual bool UseViewRotation => true;
|
||
|
private Quaternion _viewCameraRotation=Quaternion.identity;
|
||
|
private Quaternion _lastRotation=Quaternion.identity;
|
||
|
private Quaternion _targetAdditiveRotation = Quaternion.identity;
|
||
|
private Quaternion _currentAdditiveRotation = Quaternion.identity;
|
||
|
private Optional<Quaternion> _lastDynamicRotation=new();
|
||
|
protected CharacterMovementState(ICharacterController characterController) : base(characterController)
|
||
|
{
|
||
|
CharacterController = characterController;
|
||
|
}
|
||
|
public override void UpdateRotation(ref float3 localPosition, ref quaternion currentRotation,ref quaternion viewRotation, float deltaTime)
|
||
|
{
|
||
|
var isTps = false;
|
||
|
|
||
|
if (CharacterController is PlayerCharacterController playerCharacterController)
|
||
|
{
|
||
|
isTps = playerCharacterController.AllowTpsCamera.Allow;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
playerCharacterController = null;
|
||
|
}
|
||
|
|
||
|
var rotation = new Vector3(Self.InputView.y, Self.InputView.x);
|
||
|
|
||
|
var actorRot = Quaternion.LookRotation(Vector3.ProjectOnPlane(Quaternion.Euler(0,Self.InputView.x,0) * Vector3.forward, Vector3.up));
|
||
|
|
||
|
var currentPos = Self.PlayerView.transform.localPosition;
|
||
|
Vector3.Lerp(currentPos,
|
||
|
new Vector3(0, Self.CurrentStateData.ViewOffset.x, Self.CurrentStateData.ViewOffset.y),
|
||
|
Time.deltaTime * 5);
|
||
|
|
||
|
|
||
|
if (isTps)
|
||
|
{
|
||
|
var input = playerCharacterController.InputDirection;
|
||
|
var direction = actorRot * new Vector3(input.x,0,input.y);
|
||
|
if (direction.sqrMagnitude > 0.16f)
|
||
|
{
|
||
|
currentRotation =
|
||
|
Quaternion.Lerp(currentRotation, Quaternion.LookRotation(direction), 16 * deltaTime) ;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
currentRotation = actorRot ;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
_currentAdditiveRotation =
|
||
|
Quaternion.Lerp(_currentAdditiveRotation,UseViewRotation || Self.ForceRootMotion.Allow ? _targetAdditiveRotation : Quaternion.identity, 8 * deltaTime *Self.PlayerSettings.Value.ViewCameraMovementIntensity);
|
||
|
|
||
|
viewRotation = Quaternion.Euler(rotation) * Quaternion.Lerp(quaternion.identity, _currentAdditiveRotation,0.64f) ;
|
||
|
|
||
|
var newPos = new float3(0,ViewOffset.x,ViewOffset.y);
|
||
|
|
||
|
if (UseViewPosition || Self.ForceRootMotion.Allow)
|
||
|
{
|
||
|
if (Self.PlayerAnimationView)
|
||
|
{
|
||
|
newPos = Self.Transform.InverseTransformPoint(Self.PlayerAnimationView
|
||
|
.position);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
localPosition = Vector3.Lerp(localPosition, newPos, 32*deltaTime);
|
||
|
|
||
|
if (playerCharacterController is{LimitedViewAngle:{Count:>0}} && Self.AllowTpsCamera.Allow is false)
|
||
|
{
|
||
|
var max = playerCharacterController.LimitedViewAngle.Min();
|
||
|
|
||
|
var currentViewRotation = playerCharacterController.PlayerAnimationView.rotation;
|
||
|
|
||
|
float angleDifference = Quaternion.Angle(Quaternion.Euler(rotation), currentViewRotation);
|
||
|
|
||
|
// 如果角度差大于最大允许角度差,进行限制
|
||
|
if (angleDifference > max)
|
||
|
{
|
||
|
//将当前旋转限制在最大允许角度差的范围内
|
||
|
var clampRotation = Quaternion.Lerp(
|
||
|
Quaternion.Euler(rotation),
|
||
|
Quaternion.RotateTowards(currentViewRotation,Quaternion.Euler(rotation) , max),
|
||
|
64 * Time.deltaTime
|
||
|
);
|
||
|
//rotation = Quaternion.RotateTowards(FpvRotation, rotation, LimitViewAngle);
|
||
|
|
||
|
var newView = MathV.TransientRotationAxis(clampRotation.eulerAngles);
|
||
|
|
||
|
playerCharacterController.InputView = new(newView.y,newView.x);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (Self.CharacterActor.supportDynamicGround && Self.CharacterActor.GroundCollider3D is {attachedRigidbody:{} groundRigidbody} && groundRigidbody)
|
||
|
{
|
||
|
var dynamicRotation = groundRigidbody.rotation;
|
||
|
|
||
|
if (_lastDynamicRotation.Allow)
|
||
|
{
|
||
|
if (dynamicRotation != _lastDynamicRotation.Value)
|
||
|
{
|
||
|
var d = Quaternion.Inverse(dynamicRotation) * _lastDynamicRotation;
|
||
|
|
||
|
Self.InputView-=new float2(d.eulerAngles.y,0);
|
||
|
|
||
|
_lastDynamicRotation.SetValueThenAllow(dynamicRotation);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_lastDynamicRotation.SetValueThenAllow(dynamicRotation);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_lastDynamicRotation.Clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void UpdateVelocity(ref float3 currentVelocity, float deltaTime)
|
||
|
{
|
||
|
var rot = Quaternion.Euler(new Vector3(Self.InputView.y, Self.InputView.x));
|
||
|
rot = Quaternion.LookRotation(Vector3.ProjectOnPlane(rot * Vector3.forward, Vector3.up));
|
||
|
|
||
|
if (Self.PlayerAnimationView)
|
||
|
{
|
||
|
_viewCameraRotation = Self.PlayerAnimationView.rotation;
|
||
|
|
||
|
var relativeRotation = Quaternion.Inverse(_viewCameraRotation) * Self.Rotation;
|
||
|
|
||
|
_targetAdditiveRotation = Quaternion.Inverse(relativeRotation) * _lastRotation;
|
||
|
|
||
|
_lastRotation = relativeRotation;
|
||
|
}
|
||
|
|
||
|
|
||
|
var moveVelocity = rot * new Vector3(
|
||
|
Self.InputDirection.x,
|
||
|
0,
|
||
|
Self.InputDirection.y
|
||
|
) * (Self.CurrentStateData?.BaseSpeed ?? 1);
|
||
|
|
||
|
if (Self.CharacterActor.IsGrounded)
|
||
|
{
|
||
|
var effectiveGroundNormal =Self.CharacterActor.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(-12 * deltaTime));
|
||
|
currentVelocity = newVelocity;
|
||
|
|
||
|
if (Self.RequestJump && Self.JumpedThisTime is false)
|
||
|
{
|
||
|
Self.CharacterActor.ForceNotGrounded();
|
||
|
|
||
|
// if (characterController.landFreeze.AllowUpdateWithoutReset is false)
|
||
|
// {
|
||
|
// currentVelocity = Vector3.Lerp(currentVelocity, default, 0.8f);
|
||
|
// }
|
||
|
|
||
|
currentVelocity.y+= 5f;
|
||
|
|
||
|
if (this is ICharacterSliding )
|
||
|
{
|
||
|
currentVelocity += currentVelocity;
|
||
|
|
||
|
var normal = (float3)Self.CharacterActor.GroundStableNormal;
|
||
|
|
||
|
normal.y = Mathf.Abs(normal.y);
|
||
|
|
||
|
currentVelocity += normal;
|
||
|
}
|
||
|
|
||
|
|
||
|
Self.JumpedThisTime = true;
|
||
|
}
|
||
|
}
|
||
|
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 (Self.CharacterActor.WallCollision)
|
||
|
{
|
||
|
if (Vector3.Dot(currentVelocity + (float3)addedVelocity, addedVelocity) > 0f)
|
||
|
{
|
||
|
var perpenticularObstructionNormal = Vector3
|
||
|
.Cross(Vector3.Cross(Vector3.up, Self.CharacterActor.GroundContactNormal), Vector3.up).normalized;
|
||
|
addedVelocity = Vector3.ProjectOnPlane(addedVelocity, perpenticularObstructionNormal);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Apply added velocity
|
||
|
currentVelocity += (float3)addedVelocity;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var tempVelocity = currentVelocity;
|
||
|
tempVelocity = Vector3.Lerp(tempVelocity, default, 2*deltaTime);
|
||
|
currentVelocity.x = tempVelocity.x;
|
||
|
currentVelocity.z = tempVelocity.z;
|
||
|
}
|
||
|
// Gravity
|
||
|
|
||
|
currentVelocity += (float3)(-Vector3.up * (30 * deltaTime));
|
||
|
// Drag
|
||
|
currentVelocity *= (1f / (1f + 0.1f * deltaTime));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
public class CharacterWalkState : CharacterMovementState,ICharacterStateWalk
|
||
|
{
|
||
|
private IMicroStateMachine<IPlayerControlMode> _controlMode;
|
||
|
protected override bool UseViewRotation => false;
|
||
|
protected override bool UseViewPosition => false;
|
||
|
|
||
|
public CharacterWalkState(ICharacterController characterController, IMicroStateMachine<IPlayerControlMode> controlMode) : base(characterController)
|
||
|
{
|
||
|
_controlMode = controlMode;
|
||
|
}
|
||
|
public override void OnStateEntry(IState old)
|
||
|
{
|
||
|
base.OnStateEntry(old);
|
||
|
|
||
|
_controlMode.TransitionState<PlayerWalkMode>();
|
||
|
}
|
||
|
|
||
|
public override void OnStateUpdate(float deltaTime)
|
||
|
{
|
||
|
base.OnStateUpdate(deltaTime);
|
||
|
if(Self.DisableStateTransition.Allow)return;
|
||
|
if (Self.InputDirection is {x:0,y:0})
|
||
|
{
|
||
|
CharacterController.TransitionState<ICharacterStateIdle>();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
public class CharacterAnimationState:CharacterMovementState,ICharacterStateAnimation
|
||
|
{
|
||
|
public CharacterAnimationState(ICharacterController characterController) : base(characterController)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
protected override bool UseViewPosition => true;
|
||
|
protected override bool UseViewRotation => true;
|
||
|
public bool AllowCollision { get; set; }
|
||
|
public override float BaseSpeed => 0;
|
||
|
|
||
|
public override void OnStateEntry(IState old)
|
||
|
{
|
||
|
base.OnStateEntry(old);
|
||
|
Self.CharacterActor.UseRootMotion = true;
|
||
|
}
|
||
|
public override void OnStateExit(IState old, IState newState)
|
||
|
{
|
||
|
base.OnStateExit(old, newState);
|
||
|
Self.CharacterActor.UseRootMotion = false;
|
||
|
}
|
||
|
|
||
|
public override void UpdateRotation(ref float3 localPosition, ref quaternion currentRotation, ref quaternion viewRotation,
|
||
|
float deltaTime)
|
||
|
{
|
||
|
base.UpdateRotation(ref localPosition, ref currentRotation, ref viewRotation, deltaTime);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class CharacterIdleState : CharacterMovementState,ICharacterStateIdle
|
||
|
{
|
||
|
private IMicroStateMachine<IPlayerControlMode> _controlMode;
|
||
|
protected override bool UseViewRotation => false;
|
||
|
protected override bool UseViewPosition => false;
|
||
|
|
||
|
public CharacterIdleState(ICharacterController characterController, IMicroStateMachine<IPlayerControlMode> controlMode) : base(characterController)
|
||
|
{
|
||
|
_controlMode = controlMode;
|
||
|
}
|
||
|
|
||
|
public override void OnStateEntry(IState old)
|
||
|
{
|
||
|
base.OnStateEntry(old);
|
||
|
|
||
|
_controlMode.TransitionState<PlayerWalkMode>();
|
||
|
}
|
||
|
|
||
|
public override void OnStateUpdate(float deltaTime)
|
||
|
{
|
||
|
base.OnStateUpdate(deltaTime);
|
||
|
|
||
|
if(Self.DisableStateTransition.Allow)return;
|
||
|
|
||
|
switch (Self.InputDirection)
|
||
|
{
|
||
|
case {x:not 0}:
|
||
|
case {y:not 0}:
|
||
|
CharacterController.TransitionState<ICharacterStateWalk>();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
public class CharacterCrouchedState : CharacterMovementState,ICharacterStateCrouched
|
||
|
{
|
||
|
public override float BaseHeight { get; set; } = 1.0f;
|
||
|
public override float2 ViewOffset { get; set; } = new float2(1.0f, 0.32f);
|
||
|
public override float BaseSpeed { get; set; } = 1;
|
||
|
protected override bool UseViewRotation => false;
|
||
|
|
||
|
public CharacterCrouchedState(ICharacterController characterController) : base(characterController)
|
||
|
{
|
||
|
}
|
||
|
}
|
||
|
public class CharacterRunState:CharacterMovementState,ICharacterStateRun
|
||
|
{
|
||
|
|
||
|
public override float BaseSpeed { get; set; } = 4.5f;
|
||
|
|
||
|
public CharacterRunState(ICharacterController characterController) : base(characterController)
|
||
|
{
|
||
|
}
|
||
|
}
|
||
|
public class CharacterSprintState:CharacterMovementState,ICharacterSprint
|
||
|
{
|
||
|
public override float BaseSpeed { get; set; } = 6;
|
||
|
private float _consumedStamina;
|
||
|
public CharacterSprintState(ICharacterController characterController) : base(characterController)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
public override void UpdateVelocity(ref float3 currentVelocity, float deltaTime)
|
||
|
{
|
||
|
var length = MathV.GetLength(currentVelocity);
|
||
|
|
||
|
if (length > 3)
|
||
|
{
|
||
|
_consumedStamina += 32 * deltaTime;
|
||
|
|
||
|
if (_consumedStamina > 1 && Self.IsGrounded)
|
||
|
{
|
||
|
var consumed = (int)_consumedStamina;
|
||
|
_consumedStamina -= consumed;
|
||
|
Self.Stamina -= consumed;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (Self.Stamina is 0)
|
||
|
{
|
||
|
Self.TransitionState<ICharacterStateRun>();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
base.UpdateVelocity(ref currentVelocity, deltaTime);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class CharacterVaultState : CharacterMovementState, ICharacterStateVault,IWarpPointProvider
|
||
|
{
|
||
|
protected readonly IPlayerWeaponInventory _weaponInventory;
|
||
|
protected override bool UseViewPosition => true;
|
||
|
protected override bool UseViewRotation => true;
|
||
|
|
||
|
private readonly MotionWarpingAsset _motionWarpingAsset;
|
||
|
private Quaternion _forward;
|
||
|
public CharacterVaultState(ICharacterController characterController, IPlayerWeaponInventory weaponInventory) : base(characterController)
|
||
|
{
|
||
|
_weaponInventory = weaponInventory;
|
||
|
_motionWarpingAsset = Self.EntitiesService.QueryComponents<MotionWarpingAsset>().ToArray()
|
||
|
.First(x => x.name == "motion_wrapping_vault");
|
||
|
}
|
||
|
|
||
|
public override void OnStateEntry(IState old)
|
||
|
{
|
||
|
base.OnStateEntry(old);
|
||
|
|
||
|
Vector3 samplePos = Self.ViewPosition;
|
||
|
samplePos.y = Self.SyncPosition.y;
|
||
|
if (Self.SyncCollider.Raycast(new(samplePos, Self.SyncPosition - samplePos), out var hit, int.MaxValue))
|
||
|
{
|
||
|
_forward =Quaternion.LookRotation( Quaternion.LookRotation(Vector3.ProjectOnPlane(hit.normal, Vector3.up)) * Vector3.back);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_forward = Self.Rotation;
|
||
|
}
|
||
|
|
||
|
|
||
|
Self.MotionWarping.onWarpEnded.AddListener(Exit);
|
||
|
|
||
|
Self.MotionWarping.Interact(this);
|
||
|
|
||
|
Self.AllowOverride.AddElement(Self.MotionWarping);
|
||
|
|
||
|
if (old is ICharacterStateClimb)
|
||
|
{
|
||
|
_weaponInventory.AllowWeapon.AddDisableElements(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void OnStateExit(IState old, IState newState)
|
||
|
{
|
||
|
Self.AllowOverride.RemoveElement(Self.MotionWarping);
|
||
|
base.OnStateExit(old, newState);
|
||
|
|
||
|
_weaponInventory.AllowWeapon.RemoveDisableElements(this);
|
||
|
}
|
||
|
|
||
|
protected override void Exit()
|
||
|
{
|
||
|
Self.MotionWarping.onWarpEnded.RemoveListener(Exit);
|
||
|
base.Exit();
|
||
|
}
|
||
|
|
||
|
public override void UpdateVelocity(ref float3 currentVelocity, float deltaTime)
|
||
|
{
|
||
|
base.UpdateVelocity(ref currentVelocity, deltaTime);
|
||
|
currentVelocity = default;
|
||
|
}
|
||
|
|
||
|
public override void UpdateRotation(ref float3 localPosition, ref quaternion currentRotation, ref quaternion viewRotation,
|
||
|
float deltaTime)
|
||
|
{
|
||
|
base.UpdateRotation(ref localPosition, ref currentRotation, ref viewRotation, deltaTime);
|
||
|
currentRotation = _forward;
|
||
|
}
|
||
|
|
||
|
public WarpInteractionResult Interact(GameObject instigator)
|
||
|
{
|
||
|
var syncTransform = Self.SyncCollider.transform;
|
||
|
|
||
|
var result = new WarpInteractionResult()
|
||
|
{
|
||
|
points = new[]
|
||
|
{
|
||
|
|
||
|
new WarpPoint()
|
||
|
{
|
||
|
transform = syncTransform,
|
||
|
position = syncTransform.InverseTransformPoint(Self.SyncPosition),
|
||
|
rotation = Quaternion.Inverse(syncTransform.rotation) * _forward
|
||
|
}
|
||
|
},
|
||
|
asset = _motionWarpingAsset,
|
||
|
success = true
|
||
|
};
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
public class CharacterStepUpState:CharacterMovementState,ICharacterStateStepUp,IWarpPointProvider
|
||
|
{
|
||
|
|
||
|
private readonly MotionWarpingAsset _motionWarpingAsset;
|
||
|
public CharacterStepUpState(ICharacterController characterController, Animator animator, IPlayerWeaponInventory weaponInventory) : base(characterController)
|
||
|
{
|
||
|
_animator = animator;
|
||
|
_weaponInventory = weaponInventory;
|
||
|
_motionWarpingAsset = Self.EntitiesService.QueryComponents<MotionWarpingAsset>().ToArray()
|
||
|
.First(x => x.name == "motion_wrapping_step_up");
|
||
|
}
|
||
|
|
||
|
public override float BaseHeight => 0.24f;
|
||
|
protected override bool UseViewPosition => true;
|
||
|
protected override bool UseViewRotation => true;
|
||
|
private readonly IPlayerWeaponInventory _weaponInventory;
|
||
|
private readonly Animator _animator;
|
||
|
private readonly float _lerpFactor = 2;
|
||
|
private readonly IntervalUpdate _cancelInterval = new(0.16f);
|
||
|
private Transform _ignore;
|
||
|
private Quaternion _forward;
|
||
|
private bool _needInit;
|
||
|
public override void OnStateEntry(IState old)
|
||
|
{
|
||
|
|
||
|
Vector3 samplePos = Self.ViewPosition;
|
||
|
samplePos.y = Self.SyncPosition.y;
|
||
|
if (Self.SyncCollider.Raycast(new(samplePos, Self.SyncPosition - samplePos), out var hit, int.MaxValue))
|
||
|
{
|
||
|
_forward =Quaternion.LookRotation( Quaternion.LookRotation(Vector3.ProjectOnPlane(hit.normal, Vector3.up)) * Vector3.back);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_forward = Self.Rotation;
|
||
|
}
|
||
|
|
||
|
|
||
|
_ignore = Self.SyncCollider.transform;
|
||
|
//Self.CharacterActor.PhysicsComponent.IgnoreCollision(_ignore,true);
|
||
|
base.OnStateEntry(old);
|
||
|
Self.CharacterActor.alwaysNotGrounded = true;
|
||
|
Self.CharacterActor.ForceNotGrounded();
|
||
|
|
||
|
_cancelInterval.Reset();
|
||
|
_needInit = true;
|
||
|
|
||
|
Self.LimitedViewAngle.Add(80);
|
||
|
|
||
|
|
||
|
var velocity = CharacterController.SelfVelocity;
|
||
|
|
||
|
|
||
|
//velocity.x = Mathf.Clamp(velocity.x, -0.5f, 0.5f);
|
||
|
velocity.y = Mathf.Clamp(velocity.y, 3, 8);
|
||
|
|
||
|
CharacterController.Velocity = velocity;
|
||
|
|
||
|
if (old is ICharacterStateClimb)
|
||
|
{
|
||
|
_weaponInventory.AllowWeapon.AddDisableElements(this);
|
||
|
}
|
||
|
|
||
|
Self.MotionWarping.onWarpEnded.AddListener(Exit);
|
||
|
|
||
|
Self.AllowOverride.AddElement(Self.MotionWarping);
|
||
|
|
||
|
Self.MotionWarping.Interact(this);
|
||
|
}
|
||
|
|
||
|
protected override void Exit()
|
||
|
{
|
||
|
switch (EntryState)
|
||
|
{
|
||
|
case ICharacterStateClimb:
|
||
|
CharacterController.TransitionState<ICharacterStateWalk>();
|
||
|
break;
|
||
|
default:
|
||
|
base.Exit();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void OnStateUpdate(float deltaTime)
|
||
|
{
|
||
|
return;
|
||
|
if (_cancelInterval.AllowUpdate && Self.CharacterActor.Velocity.sqrMagnitude<=0.16f
|
||
|
&& Self.CharacterActor.Position.y > Self.SyncPosition.y
|
||
|
)
|
||
|
{
|
||
|
//Exit();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var direction = Self.Transform.InverseTransformPoint(Self.SyncPosition);
|
||
|
|
||
|
switch (direction)
|
||
|
{
|
||
|
case { z: < 0, y: <= 0 }:
|
||
|
case {} when direction.GetLength()<0.1f:
|
||
|
Self.TransitionState<ICharacterStateWalk>();
|
||
|
break;
|
||
|
case { z: > 0, y: < 0 }:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (_ignore && _ignore.TryGetComponent<Collider>(out var collider) && collider is not MeshCollider{convex:false})
|
||
|
{
|
||
|
var currentPosition = Self.Position;
|
||
|
var closePoint = collider.ClosestPoint(currentPosition);
|
||
|
|
||
|
if (closePoint.y > currentPosition.y)
|
||
|
{
|
||
|
currentPosition.y = closePoint.y;
|
||
|
Self.Position = currentPosition;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
public override void OnStateExit(IState old, IState newState)
|
||
|
{
|
||
|
Self.AllowOverride.RemoveElement(Self.MotionWarping);
|
||
|
|
||
|
Self.MotionWarping.onWarpEnded.RemoveListener(Exit);
|
||
|
|
||
|
// Self.CharacterActor.PhysicsComponent.IgnoreCollision(_ignore,false);
|
||
|
Self.CharacterActor.alwaysNotGrounded = false;
|
||
|
//Self._characterActor.ColliderComponent.enabled = true;
|
||
|
Self.CharacterActor.ForceGrounded();
|
||
|
|
||
|
Self.LimitedViewAngle.TryRemove(80);
|
||
|
|
||
|
_weaponInventory.AllowWeapon.RemoveDisableElements(this);
|
||
|
}
|
||
|
|
||
|
public override void UpdateVelocity(ref float3 currentVelocity, float deltaTime)
|
||
|
{
|
||
|
currentVelocity = default;
|
||
|
return;
|
||
|
if (_needInit)
|
||
|
{
|
||
|
currentVelocity.y = 0;
|
||
|
_needInit = false;
|
||
|
}
|
||
|
|
||
|
|
||
|
var velocity =(Self.SyncPosition - Self.CharacterActor.Position).normalized * _animator.velocity.GetLength();
|
||
|
|
||
|
velocity += _animator.velocity;
|
||
|
|
||
|
//velocity += (float3)((self.Rotation * (Vector3)((Vector2)self.InputDirection).normalized) * 1.0f);
|
||
|
//currentVelocity = Vector3.Lerp(currentVelocity,velocity,_lerpFactor*deltaTime);
|
||
|
|
||
|
var selfVelocity = Self.Transform.InverseTransformDirection(velocity);
|
||
|
//selfVelocity.x = 0;
|
||
|
selfVelocity.z = math.max(3, velocity.z);
|
||
|
selfVelocity.y = math.max(1.6f,selfVelocity.y);
|
||
|
velocity = Self.Transform.TransformDirection(selfVelocity);
|
||
|
|
||
|
currentVelocity = velocity;
|
||
|
}
|
||
|
|
||
|
public override void UpdateRotation(ref float3 localPosition, ref quaternion currentRotation, ref quaternion viewRotation,
|
||
|
float deltaTime)
|
||
|
{
|
||
|
base.UpdateRotation(ref localPosition, ref currentRotation, ref viewRotation, deltaTime);
|
||
|
// currentRotation = _forward;
|
||
|
}
|
||
|
|
||
|
public WarpInteractionResult Interact(GameObject instigator)
|
||
|
{
|
||
|
var syncTransform = Self.SyncCollider.transform;
|
||
|
|
||
|
var result = new WarpInteractionResult()
|
||
|
{
|
||
|
points = new[]
|
||
|
{
|
||
|
|
||
|
new WarpPoint()
|
||
|
{
|
||
|
transform = syncTransform,
|
||
|
position = syncTransform.InverseTransformPoint(Self.SyncPosition),
|
||
|
rotation = Quaternion.Inverse(syncTransform.rotation) * _forward
|
||
|
}
|
||
|
},
|
||
|
asset = _motionWarpingAsset,
|
||
|
success = true
|
||
|
};
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public sealed class CharacterKnockedState : CharacterMovementState, ICharacterKnocked
|
||
|
{
|
||
|
public CharacterKnockedState(ICharacterController characterController) : base(characterController)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
protected override bool UseViewRotation => true;
|
||
|
protected override bool UseViewPosition => true;
|
||
|
public override float BaseSpeed => 0.5f;
|
||
|
public override float BaseHeight => 0.45f;
|
||
|
public override float2 ViewOffset { get; set; } = new(0.4f, 0f);
|
||
|
public override void OnStateEntry(IState old)
|
||
|
{
|
||
|
base.OnStateEntry(old);
|
||
|
Self.LimitedViewAngle.Add(50);
|
||
|
}
|
||
|
|
||
|
public override void UpdateRotation(ref float3 localPosition, ref quaternion currentRotation, ref quaternion viewRotation,
|
||
|
float deltaTime)
|
||
|
{
|
||
|
var currentRot = currentRotation;
|
||
|
base.UpdateRotation(ref localPosition, ref currentRotation, ref viewRotation, deltaTime);
|
||
|
|
||
|
viewRotation *= Quaternion.Euler(0, 0, 3);
|
||
|
|
||
|
if (MathV.GetLength(Self.Velocity) > 0.1f)
|
||
|
{
|
||
|
currentRotation = Quaternion.Lerp(currentRot, currentRotation, deltaTime);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
currentRotation = currentRot;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void OnStateExit(IState old, IState newState)
|
||
|
{
|
||
|
base.OnStateExit(old, newState);
|
||
|
Self.LimitedViewAngle.Remove(50);
|
||
|
}
|
||
|
}
|
||
|
public sealed class CharacterSlidingState : CharacterMovementState, ICharacterSliding
|
||
|
{
|
||
|
private readonly CharacterActor _actor;
|
||
|
private readonly PlayerCharacterController _playerCharacterController;
|
||
|
protected override bool UseViewPosition => true;
|
||
|
|
||
|
public CharacterSlidingState(ICharacterController characterController, CharacterActor actor) : base(characterController)
|
||
|
{
|
||
|
_actor = actor;
|
||
|
_playerCharacterController = characterController as PlayerCharacterController;
|
||
|
}
|
||
|
|
||
|
public override float BaseHeight { get; set; } = 1.2f;
|
||
|
public override float2 ViewOffset { get; set; } = new float2(0.8f, 0.32f);
|
||
|
public override float BaseSpeed { get; set; } = 1;
|
||
|
|
||
|
private const float AdditiveSpeed = 4f;
|
||
|
private const float Damping = 1.8f;
|
||
|
private const float StopSpeed = 1.2f;
|
||
|
private bool _alwaysSlide;
|
||
|
private bool _addedVelocity;
|
||
|
private Quaternion _entryRotation;
|
||
|
public override void OnStateEntry(IState old)
|
||
|
{
|
||
|
base.OnStateEntry(old);
|
||
|
// self.ExpectCrouch.being = true;
|
||
|
// self.ExpectCrouch.shouldBe = true;
|
||
|
// self.ExecuteCommand<PlayerCancelRunCommand>();
|
||
|
_playerCharacterController.CancelRun();
|
||
|
_addedVelocity = false;
|
||
|
|
||
|
_entryRotation = _actor.Rotation;
|
||
|
|
||
|
_playerCharacterController.LimitedViewAngle.Add(60);
|
||
|
}
|
||
|
|
||
|
public override void OnStateExit(IState old, IState newState)
|
||
|
{
|
||
|
base.OnStateExit(old, newState);
|
||
|
_playerCharacterController.LimitedViewAngle.Remove(60);
|
||
|
}
|
||
|
|
||
|
public override void UpdateRotation(ref float3 localPosition, ref quaternion currentRotation, ref quaternion viewRotation,
|
||
|
float deltaTime)
|
||
|
{
|
||
|
base.UpdateRotation(ref localPosition, ref currentRotation, ref viewRotation, deltaTime);
|
||
|
currentRotation = _entryRotation;
|
||
|
}
|
||
|
|
||
|
public override void UpdateVelocity(ref float3 currentVelocity, float deltaTime)
|
||
|
{
|
||
|
if (_alwaysSlide && _actor.IsGrounded is false)
|
||
|
{
|
||
|
base.UpdateVelocity(ref currentVelocity, deltaTime);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (_addedVelocity is false)
|
||
|
{
|
||
|
currentVelocity += (float3)((Vector3)currentVelocity).normalized * AdditiveSpeed;
|
||
|
_addedVelocity = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
currentVelocity = Vector3.Lerp(currentVelocity, Vector3.zero, deltaTime * Damping);
|
||
|
|
||
|
if (_actor.IsGrounded)
|
||
|
{
|
||
|
var normal = _actor.GroundStableNormal;
|
||
|
|
||
|
normal = Vector3.ProjectOnPlane(normal, Vector3.up);
|
||
|
|
||
|
currentVelocity += (float3)normal;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override void Exit()
|
||
|
{
|
||
|
//base.Exit();
|
||
|
CharacterController.TransitionState<ICharacterStateCrouched>();
|
||
|
}
|
||
|
public override void AfterUpdateVelocity(float deltaTime)
|
||
|
{
|
||
|
if(_actor.Velocity.sqrMagnitude <= StopSpeed)
|
||
|
{
|
||
|
Exit();
|
||
|
return;
|
||
|
}
|
||
|
if(_actor.IsGrounded is false)
|
||
|
{
|
||
|
Exit();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public sealed class CharacterClimbState : CharacterMovementState, ICharacterStateClimb
|
||
|
{
|
||
|
private readonly IPlayerWeaponInventory _weaponInventory;
|
||
|
protected override bool UseViewPosition => true;
|
||
|
|
||
|
public CharacterClimbState(ICharacterController characterController, IPlayerWeaponInventory weaponInventory) : base(characterController)
|
||
|
{
|
||
|
_weaponInventory = weaponInventory;
|
||
|
}
|
||
|
|
||
|
private const float LerpFactor = 2;
|
||
|
private Transform _ignore;
|
||
|
private Quaternion _forward;
|
||
|
private bool _needInit;
|
||
|
|
||
|
private Vector3 FixedSyncPosition =>Self.SyncPosition+_forward * new Vector3(0, -1.3f,- 0.5f);
|
||
|
|
||
|
private readonly IntervalUpdate _freeTurn = new(0.2f);
|
||
|
|
||
|
public override void OnStateEntry(IState old)
|
||
|
{
|
||
|
Vector3 samplePos = Self.Position;
|
||
|
samplePos.y = Self.SyncPosition.y;
|
||
|
|
||
|
if (Self.SyncCollider.TryGetClosestPointFromCollider(samplePos, out var closestPoint))
|
||
|
{
|
||
|
DrawBasics.Point(closestPoint,text:"Climb Point", durationInSec:8);
|
||
|
DrawBasics.Line(samplePos,closestPoint,durationInSec:8);
|
||
|
if (Self.SyncCollider.Raycast(new Ray(samplePos, closestPoint - samplePos), out var hit, int.MaxValue))
|
||
|
{
|
||
|
_forward = Quaternion.LookRotation(
|
||
|
Quaternion.LookRotation(Vector3.ProjectOnPlane(hit.normal, Vector3.up)) * Vector3.back);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Debug.LogWarning("未检测到正面");
|
||
|
_forward = Self.Rotation;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Debug.LogWarning("未找到最近点");
|
||
|
_forward = Self.Rotation;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
_ignore = Self.SyncCollider.transform;
|
||
|
Self.CharacterActor.PhysicsComponent.IgnoreCollision(_ignore,true);
|
||
|
base.OnStateEntry(old);
|
||
|
Self.CharacterActor.alwaysNotGrounded = true;
|
||
|
Self.CharacterActor.ForceNotGrounded();
|
||
|
_needInit = true;
|
||
|
|
||
|
Self.LimitedViewAngle.Add(80);
|
||
|
|
||
|
_weaponInventory.AllowWeapon.AddDisableElements(this);
|
||
|
}
|
||
|
public override void OnStateExit(IState old, IState newState)
|
||
|
{
|
||
|
Self.CharacterActor.PhysicsComponent.IgnoreCollision(_ignore,false);
|
||
|
Self.CharacterActor.alwaysNotGrounded = false;
|
||
|
//Self.CharacterActor.ForceGrounded();
|
||
|
|
||
|
Self.LimitedViewAngle.TryRemove(80);
|
||
|
|
||
|
_weaponInventory.AllowWeapon.RemoveDisableElements(this);
|
||
|
}
|
||
|
|
||
|
public override void OnStateUpdate(float deltaTime)
|
||
|
{
|
||
|
base.OnStateUpdate(deltaTime);
|
||
|
Self.Position = Vector3.Lerp(Self.Position, FixedSyncPosition, 5 * deltaTime);
|
||
|
}
|
||
|
|
||
|
public override void UpdateVelocity(ref float3 currentVelocity, float deltaTime)
|
||
|
{
|
||
|
currentVelocity = default;
|
||
|
|
||
|
if (_needInit)
|
||
|
{
|
||
|
CharacterController.Position = Vector3.MoveTowards(CharacterController.Position, FixedSyncPosition, 5 * deltaTime);
|
||
|
if (Vector3.Distance(CharacterController.Position, FixedSyncPosition) < 0.05f)
|
||
|
{
|
||
|
_needInit = false;
|
||
|
CharacterController.Position = FixedSyncPosition;
|
||
|
return;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var desiredVelocity = _forward * Vector3.right * Self.MoveInput.x;
|
||
|
|
||
|
var newPosition= Self.SyncPosition + desiredVelocity * deltaTime ;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
if (Self.SyncCollider.TryGetClosestPointFromCollider(newPosition,out var closestPoint) && closestPoint == newPosition)
|
||
|
{
|
||
|
Self.SyncPosition = newPosition;
|
||
|
|
||
|
currentVelocity = desiredVelocity;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
foreach (var collider in Physics.OverlapSphere(Self.SyncPosition,0.1f,Self.CharacterActor.stableLayerMask))
|
||
|
{
|
||
|
if(collider == Self.SyncCollider)continue;
|
||
|
if(collider is not BoxCollider)continue;
|
||
|
|
||
|
var distance = Vector3.Distance(collider.ClosestPoint(Self.SyncPosition), Self.SyncPosition);
|
||
|
if (distance<0.05f)
|
||
|
{
|
||
|
Self.CharacterActor.PhysicsComponent.IgnoreCollision(Self.SyncCollider.transform,false);
|
||
|
Self.SyncPosition = collider.ClosestPoint(Self.SyncPosition);
|
||
|
currentVelocity = desiredVelocity;
|
||
|
Self.SyncCollider = collider;
|
||
|
|
||
|
Self.CharacterActor.PhysicsComponent.IgnoreCollision(Self.SyncCollider.transform,true);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
currentVelocity = default;
|
||
|
}
|
||
|
|
||
|
if(_freeTurn.AllowUpdateWithoutReset)
|
||
|
{
|
||
|
var sampleStart =newPosition+ _forward * Vector3.forward * -0.1f + desiredVelocity * deltaTime;
|
||
|
|
||
|
var sampleEnd = Self.SyncPosition + _forward * Vector3.forward * 0.1f;
|
||
|
RaycastHit hit;
|
||
|
if (
|
||
|
Physics.Raycast(Self.ViewPosition,desiredVelocity,out hit,Self.CharacterActor.DefaultBodySize.x,Self.CharacterActor.stableLayerMask)
|
||
|
||
|
||
|
Physics.Linecast(sampleStart, sampleEnd, out hit,
|
||
|
layerMask: Self.CharacterActor.stableLayerMask)
|
||
|
)
|
||
|
{
|
||
|
var x = Self.Transform.InverseTransformPoint(hit.point).x;
|
||
|
switch (x, Self.InputDirection.x)
|
||
|
{
|
||
|
case(_,0):
|
||
|
case (<0,>0):
|
||
|
case (>0,<0):
|
||
|
break;
|
||
|
default:
|
||
|
if (Physics.Raycast(hit.point, hit.normal, out _, Self.CharacterActor.DefaultBodySize.x,
|
||
|
Self.CharacterActor.stableLayerMask) is false)
|
||
|
{
|
||
|
if (hit.collider.TryGetClosestPointFromCollider(hit.point + Vector3.up,
|
||
|
out closestPoint))
|
||
|
{
|
||
|
var newForward = Quaternion.LookRotation(
|
||
|
Quaternion.LookRotation(Vector3.ProjectOnPlane(hit.normal, Vector3.up)) * Vector3.back);
|
||
|
|
||
|
if (Quaternion.Angle(newForward, _forward) > 30)
|
||
|
{
|
||
|
DrawText.Write("Rot",hit.point,durationInSec:1);
|
||
|
|
||
|
_freeTurn.Reset();
|
||
|
}
|
||
|
|
||
|
Self.SyncPosition =closestPoint;
|
||
|
|
||
|
Self.CharacterActor.PhysicsComponent.IgnoreCollision(Self.SyncCollider.transform,false);
|
||
|
|
||
|
Self.SyncCollider = hit.collider;
|
||
|
|
||
|
Self.CharacterActor.PhysicsComponent.IgnoreCollision(Self.SyncCollider.transform,true);
|
||
|
|
||
|
_forward = newForward;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
public override void UpdateRotation(ref float3 localPosition, ref quaternion currentRotation, ref quaternion viewRotation,
|
||
|
float deltaTime)
|
||
|
{
|
||
|
base.UpdateRotation(ref localPosition, ref currentRotation, ref viewRotation, deltaTime);
|
||
|
Self.Rotation = currentRotation = _forward;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public sealed class CharacterSeating : CharacterStateBase,ICharacterSeating
|
||
|
{
|
||
|
private readonly IWorldSeatService _seatService;
|
||
|
private readonly IMicroStateMachine<IPlayerControlMode> _controlMode;
|
||
|
private readonly CharacterActor _actor;
|
||
|
private readonly IPlayerWeaponInventory _weaponInventory;
|
||
|
public UnitySeatNode SeatNode { get; set; }
|
||
|
public IEntity SeatEntity { get; set; }
|
||
|
private Quaternion _lastRotation;
|
||
|
private float2 _additiveVehicleView;
|
||
|
private Quaternion _seatCameraView;
|
||
|
|
||
|
private Vector3 _localPosition;
|
||
|
|
||
|
public CharacterSeating(ICharacterController characterController, IPlayerWeaponInventory weaponInventory, CharacterActor actor, IMicroStateMachine<IPlayerControlMode> controlMode, IWorldSeatService seatService) : base(characterController)
|
||
|
{
|
||
|
_weaponInventory = weaponInventory;
|
||
|
_actor = actor;
|
||
|
_controlMode = controlMode;
|
||
|
_seatService = seatService;
|
||
|
}
|
||
|
|
||
|
public override void OnStateEntry(IState old)
|
||
|
{
|
||
|
base.OnStateEntry(old);
|
||
|
|
||
|
if (_seatService.OccupySeat(SeatEntity.Id, Self.Entity) is false)
|
||
|
{
|
||
|
Self.TransitionState<ICharacterStateIdle>();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Self.Transform.position = SeatNode.SeatObject.position;
|
||
|
Self.Position = SeatNode.SeatObject.position;
|
||
|
Self.Rotation = SeatNode.SeatObject.rotation;
|
||
|
|
||
|
|
||
|
if (SeatNode.CameraObject)
|
||
|
{
|
||
|
_localPosition = _actor.transform.InverseTransformPoint(SeatNode.CameraObject.position);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
Self.AllowOverride.AddElement(this);
|
||
|
Self.CharacterActor.constraintRotation = false;
|
||
|
|
||
|
|
||
|
_weaponInventory.AllowWeapon.AddDisableElements(this);
|
||
|
|
||
|
Self.Position = SeatNode.SeatObject.position;
|
||
|
|
||
|
_lastRotation = SeatNode.SeatObject.rotation;
|
||
|
|
||
|
Self.OnInputViewFactor += OnInputView;
|
||
|
|
||
|
_seatCameraView= SeatNode.SeatObject.rotation;
|
||
|
_additiveVehicleView = default;
|
||
|
|
||
|
_controlMode.TransitionState<PlayerCarMode>();
|
||
|
|
||
|
_seatService.OnSeatOccupied += OnSeatOccupied;
|
||
|
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
private void OnSeatOccupied(UnitySeatNode arg1, IEntity arg2, IEntity arg3)
|
||
|
{
|
||
|
if (arg2.Id == Self.Entity.Id)
|
||
|
{
|
||
|
Self.TransitionState<ICharacterStateIdle>();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private float2 OnInputView(float2 arg)
|
||
|
{
|
||
|
_seatCameraView *= Quaternion.Euler(arg.y, arg.x,0);
|
||
|
|
||
|
return default;
|
||
|
}
|
||
|
|
||
|
public override void OnStateUpdate(float deltaTime)
|
||
|
{
|
||
|
base.OnStateUpdate(deltaTime);
|
||
|
if (!SeatNode.SeatObject)
|
||
|
{
|
||
|
Self.TransitionState<ICharacterStateWalk>();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void UpdateVelocity(ref float3 currentVelocity, float deltaTime)
|
||
|
{
|
||
|
if (!SeatNode.Rigidbody) return;
|
||
|
|
||
|
var difference = Quaternion.Inverse(_lastRotation) * SeatNode.Rigidbody.rotation;
|
||
|
|
||
|
var eulerAngles = difference.eulerAngles;
|
||
|
|
||
|
_lastRotation = SeatNode.Rigidbody.rotation;
|
||
|
|
||
|
float2 input = default;
|
||
|
|
||
|
input.x += eulerAngles.y;
|
||
|
input.y += eulerAngles.x;
|
||
|
|
||
|
input = MathV.TransientRotationAxis(input);
|
||
|
|
||
|
_additiveVehicleView += input;
|
||
|
//Self.Rotation = SeatNode.SeatObject.rotation;
|
||
|
}
|
||
|
|
||
|
public override void UpdateRotation(ref float3 localPosition, ref quaternion currentRotation, ref quaternion viewRotation,
|
||
|
float deltaTime)
|
||
|
{
|
||
|
localPosition = _localPosition;
|
||
|
|
||
|
Self.Transform.position = SeatNode.SeatObject.position;
|
||
|
|
||
|
|
||
|
currentRotation = SeatNode.SeatObject.rotation;
|
||
|
|
||
|
var additive = math.lerp(0, _additiveVehicleView, 8 * deltaTime);
|
||
|
|
||
|
_additiveVehicleView -=additive;
|
||
|
|
||
|
//Self.InputView+= additive;
|
||
|
_seatCameraView*= Quaternion.Euler(additive.y,additive.x,0);
|
||
|
|
||
|
var newCameraView = Quaternion.LookRotation(_seatCameraView * Vector3.forward).eulerAngles;
|
||
|
|
||
|
if(SeatNode.SeatObject)
|
||
|
newCameraView.z = SeatNode.SeatObject.rotation.eulerAngles.z;
|
||
|
|
||
|
_seatCameraView = Quaternion.Euler(newCameraView);
|
||
|
|
||
|
viewRotation =_seatCameraView;
|
||
|
|
||
|
currentRotation = SeatNode.SeatObject.rotation;
|
||
|
|
||
|
if (SeatNode.CameraObject)
|
||
|
{
|
||
|
SeatNode.CameraObject.rotation = viewRotation;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void OnStateExit(IState old, IState newState)
|
||
|
{
|
||
|
base.OnStateExit(old, newState);
|
||
|
|
||
|
Self.AllowOverride.RemoveElement(this);
|
||
|
|
||
|
Self.CharacterActor.constraintRotation = true;
|
||
|
|
||
|
_weaponInventory.AllowWeapon.RemoveDisableElements(this);
|
||
|
|
||
|
if (SeatNode.ExitObject)
|
||
|
Self.CharacterActor.Position = SeatNode.ExitObject.position;
|
||
|
|
||
|
Self.OnInputViewFactor -= OnInputView;
|
||
|
|
||
|
var newInputView = MathV.TransientRotationAxis(_seatCameraView.eulerAngles);
|
||
|
Self.InputView = new float2(newInputView.y,newInputView.x);
|
||
|
|
||
|
_seatService.UnOccupySeat(SeatEntity.Id,out _);
|
||
|
|
||
|
_seatService.OnSeatOccupied -= OnSeatOccupied;
|
||
|
}
|
||
|
}
|
||
|
public sealed class CharacterSwimming : CharacterMovementState, ICharacterSwimming
|
||
|
{
|
||
|
protected override bool UseViewRotation => true;
|
||
|
protected override bool UseViewPosition => true;
|
||
|
|
||
|
private readonly IPlayerKeyMap<InputAction> _playerKeyMap;
|
||
|
|
||
|
public CharacterSwimming(ICharacterController characterController, IPlayerKeyMap<InputAction> playerKeyMap) : base(characterController)
|
||
|
{
|
||
|
_playerKeyMap = playerKeyMap;
|
||
|
|
||
|
Self.InputActionGroup.EnsureCreated(_playerKeyMap.JumpKey);
|
||
|
}
|
||
|
|
||
|
public override void OnStateEntry(IState old)
|
||
|
{
|
||
|
base.OnStateEntry(old);
|
||
|
Self.CharacterActor.alwaysNotGrounded = true;
|
||
|
}
|
||
|
|
||
|
public override void OnStateExit(IState old, IState newState)
|
||
|
{
|
||
|
base.OnStateExit(old, newState);
|
||
|
Self.CharacterActor.alwaysNotGrounded = false;
|
||
|
}
|
||
|
|
||
|
public override void UpdateVelocity(ref float3 currentVelocity, float deltaTime)
|
||
|
{
|
||
|
var jumpPressed = Self.InputActionGroup.GetAction(_playerKeyMap.JumpKey.name).ReadValue<float>();
|
||
|
|
||
|
currentVelocity += (float3)Vector3.up * jumpPressed * 2;
|
||
|
|
||
|
currentVelocity = (Quaternion)Self.ViewRotation * Self.MoveInput * 3;
|
||
|
|
||
|
Vector3 currentPos = Self.Position;
|
||
|
|
||
|
var nextPos = currentPos + (Vector3)currentVelocity * deltaTime;
|
||
|
|
||
|
var inWaterPos = Self.SyncCollider.ClosestPoint(nextPos);
|
||
|
|
||
|
if (inWaterPos != nextPos)
|
||
|
{
|
||
|
// currentVelocity = inWaterPos - currentPos;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public sealed class CharacterInitialize : CharacterStateBase, ICharacterInitialize
|
||
|
{
|
||
|
private readonly ILogger<CharacterInitialize> _logger;
|
||
|
public CharacterInitialize(ICharacterController characterController, ILogger<CharacterInitialize> logger) : base(characterController)
|
||
|
{
|
||
|
_logger = logger;
|
||
|
}
|
||
|
|
||
|
public override void OnStateUpdate(float deltaTime)
|
||
|
{
|
||
|
base.OnStateUpdate(deltaTime);
|
||
|
if (Physics.Raycast((Vector3)Self.Position + Vector3.up, Vector3.down, out var hit,512,
|
||
|
Self.CharacterActor.stableLayerMask))
|
||
|
{
|
||
|
_logger.LogInformation($"玩家已检测到地面:{hit.transform.name},初始化完成");
|
||
|
Self.TransitionState<ICharacterStateIdle>();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public sealed class CharacterLadderState : CharacterMovementState, ICharacterLadder
|
||
|
{
|
||
|
private readonly IPlayerWeaponInventory _weaponInventory;
|
||
|
|
||
|
protected override bool UseViewPosition => true;
|
||
|
protected override bool UseViewRotation => true;
|
||
|
|
||
|
public float3 UpPoint { get; set; }
|
||
|
public float3 DownPoint { get; set; }
|
||
|
|
||
|
private Vector3 _rootVelocity;
|
||
|
public CharacterLadderState(ICharacterController characterController, IPlayerWeaponInventory weaponInventory) : base(characterController)
|
||
|
{
|
||
|
_weaponInventory = weaponInventory;
|
||
|
}
|
||
|
|
||
|
public override void OnStateEntry(IState old)
|
||
|
{
|
||
|
base.OnStateEntry(old);
|
||
|
|
||
|
Vector3 currentPosition = Self.Position;
|
||
|
var currentY = currentPosition.y;
|
||
|
currentPosition = DownPoint;
|
||
|
currentPosition.y = currentY;
|
||
|
|
||
|
Self.CharacterActor.alwaysNotGrounded = true;
|
||
|
|
||
|
Self.Position = currentPosition + Self.Transform.forward * - Self.CharacterActor.DefaultBodySize.x/2;
|
||
|
|
||
|
Self.CharacterActor.ForceNotGrounded();
|
||
|
|
||
|
if (Self.CharacterActor.TryGetComponent<AnimatorLink>(out var animatorLink))
|
||
|
{
|
||
|
animatorLink.OnAnimatorMoveEvent += OnAnimatorMove;
|
||
|
}
|
||
|
|
||
|
Self.LimitedViewAngle.Add(30);
|
||
|
|
||
|
_weaponInventory.AllowWeapon.AddDisableElements(this);
|
||
|
}
|
||
|
|
||
|
public override void OnStateExit(IState old, IState newState)
|
||
|
{
|
||
|
base.OnStateExit(old, newState);
|
||
|
Self.CharacterActor.alwaysNotGrounded = false;
|
||
|
|
||
|
if (Self.CharacterActor.TryGetComponent<AnimatorLink>(out var animatorLink))
|
||
|
{
|
||
|
animatorLink.OnAnimatorMoveEvent -= OnAnimatorMove;
|
||
|
}
|
||
|
|
||
|
Self.LimitedViewAngle.Remove(30);
|
||
|
|
||
|
_weaponInventory.AllowWeapon.RemoveDisableElements(this);
|
||
|
}
|
||
|
|
||
|
public override void OnStateUpdate(float deltaTime)
|
||
|
{
|
||
|
base.OnStateUpdate(deltaTime);
|
||
|
var currentPosition = Self.Position;
|
||
|
if (currentPosition.y > UpPoint.y - 1)
|
||
|
{
|
||
|
Self.TransitionState<ICharacterStateIdle>();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (Self.RequestJump)
|
||
|
{
|
||
|
Self.TransitionState<ICharacterStateWalk>();
|
||
|
|
||
|
Self.Velocity = (-Self.CharacterActor.Forward + Self.CharacterActor.Up) * 6;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void UpdateVelocity(ref float3 currentVelocity, float deltaTime)
|
||
|
{
|
||
|
// base.UpdateVelocity(ref currentVelocity, deltaTime);
|
||
|
if (Self.ForceRootMotion.Allow is false)
|
||
|
{
|
||
|
currentVelocity = Vector3.up * (Self.InputDirection.y * 2);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
currentVelocity = _rootVelocity;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void UpdateRotation(ref float3 localPosition, ref quaternion currentRotation, ref quaternion viewRotation,
|
||
|
float deltaTime)
|
||
|
{
|
||
|
base.UpdateRotation(ref localPosition, ref currentRotation, ref viewRotation, deltaTime);
|
||
|
if (Self.SyncCollider)
|
||
|
{
|
||
|
currentRotation =Quaternion.LookRotation( Self.SyncCollider.transform.rotation * Vector3.back);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void OnAnimatorMove()
|
||
|
{
|
||
|
_rootVelocity = Self.CharacterActor.Animator.velocity;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public sealed class CharacterParachute : CharacterMovementState, ICharacterParachute
|
||
|
{
|
||
|
private readonly IPlayerWeaponInventory _weaponInventory;
|
||
|
protected override bool UseViewRotation => true;
|
||
|
protected override bool UseViewPosition => true;
|
||
|
|
||
|
private Vector3 _targetVelocity;
|
||
|
|
||
|
public CharacterParachute(ICharacterController characterController, IPlayerWeaponInventory weaponInventory) : base(characterController)
|
||
|
{
|
||
|
_weaponInventory = weaponInventory;
|
||
|
}
|
||
|
|
||
|
public override void OnStateEntry(IState old)
|
||
|
{
|
||
|
base.OnStateEntry(old);
|
||
|
Self.LimitedViewAngle.Add(90);
|
||
|
|
||
|
_targetVelocity = Self.Velocity;
|
||
|
_weaponInventory.AllowWeapon.AddDisableElements(this);
|
||
|
}
|
||
|
public override void OnStateExit(IState old, IState newState)
|
||
|
{
|
||
|
base.OnStateExit(old,newState);
|
||
|
Self.LimitedViewAngle.Remove(90);
|
||
|
_weaponInventory.AllowWeapon.RemoveDisableElements(this);
|
||
|
}
|
||
|
|
||
|
public override void UpdateVelocity(ref float3 currentVelocity, float deltaTime)
|
||
|
{
|
||
|
currentVelocity.y = Mathf.Lerp(currentVelocity.y, -5, 3*deltaTime);
|
||
|
|
||
|
_targetVelocity = Vector3.ProjectOnPlane((Quaternion)Self.ViewRotation * Self.MoveInput, Vector3.up).normalized;
|
||
|
|
||
|
_targetVelocity *= 32;
|
||
|
|
||
|
_targetVelocity.y = currentVelocity.y;
|
||
|
|
||
|
//currentVelocity = _targetVelocity;
|
||
|
currentVelocity = Vector3.MoveTowards(currentVelocity, _targetVelocity,24* deltaTime);
|
||
|
}
|
||
|
|
||
|
public override void OnStateUpdate(float deltaTime)
|
||
|
{
|
||
|
base.OnStateUpdate(deltaTime);
|
||
|
if (Self.IsGrounded)
|
||
|
{
|
||
|
Self.TransitionState<ICharacterStateIdle>();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
public sealed class CharacterFreeFall : CharacterMovementState, ICharacterFreeFall
|
||
|
{
|
||
|
private readonly IPlayerWeaponInventory _weaponInventory;
|
||
|
protected override bool UseViewPosition => true;
|
||
|
protected override bool UseViewRotation => true;
|
||
|
|
||
|
private Vector3 _targetVelocity;
|
||
|
|
||
|
public CharacterFreeFall(ICharacterController characterController, IPlayerWeaponInventory weaponInventory) : base(characterController)
|
||
|
{
|
||
|
_weaponInventory = weaponInventory;
|
||
|
}
|
||
|
|
||
|
public override void OnStateEntry(IState old)
|
||
|
{
|
||
|
base.OnStateEntry(old);
|
||
|
_weaponInventory.AllowWeapon.AddDisableElements(this);
|
||
|
}
|
||
|
|
||
|
public override void OnStateExit(IState old, IState newState)
|
||
|
{
|
||
|
base.OnStateExit(old, newState);
|
||
|
_weaponInventory.AllowWeapon.RemoveDisableElements(this);
|
||
|
}
|
||
|
|
||
|
public override void UpdateVelocity(ref float3 currentVelocity, float deltaTime)
|
||
|
{
|
||
|
var currentY = currentVelocity.y;
|
||
|
|
||
|
_targetVelocity = Vector3.ProjectOnPlane((Quaternion)Self.ViewRotation * Self.MoveInput, Vector3.up).normalized;
|
||
|
|
||
|
_targetVelocity *= 64;
|
||
|
currentVelocity = Vector3.MoveTowards(currentVelocity, _targetVelocity,64* deltaTime);
|
||
|
|
||
|
currentVelocity.y = currentY;
|
||
|
|
||
|
// Gravity
|
||
|
|
||
|
currentVelocity += (float3)(-Vector3.up * (30 * deltaTime));
|
||
|
// Drag
|
||
|
currentVelocity *= (1f / (1f + 0.1f * deltaTime));
|
||
|
|
||
|
}
|
||
|
|
||
|
public override void OnStateUpdate(float deltaTime)
|
||
|
{
|
||
|
base.OnStateUpdate(deltaTime);
|
||
|
if (Self.IsGrounded)
|
||
|
{
|
||
|
Self.TransitionState<ICharacterStateIdle>();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|