BITFALL/Assets/Artists/Scripts/Entities/Movement/MotionBasedStates.cs

451 lines
11 KiB
C#
Raw Normal View History

using System;
using System.Collections;
2023-12-30 17:37:48 +08:00
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Animancer;
using AYellowpaper.SerializedCollections;
2023-12-30 17:37:48 +08:00
using BITFALL.Scene;
using BITKit;
using BITKit.Entities;
using BITKit.StateMachine;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Splines;
namespace BITFALL.Movement.MotionBased.States
{
public abstract class MotionBasedState:IMotionBasedState
{
[Inject] protected NavMeshAgent agent;
[Inject] protected MotionBasedMovement movement;
[Inject] protected AnimancerComponent animancerComponent;
[Inject] protected IHealth health;
2023-12-30 17:37:48 +08:00
protected Vector3 MotionVelocity { get; private set; }
protected Vector3 MotionAngularVelocity { get; private set; }
protected Quaternion MotionDeltaRotation { get; private set; }
public virtual bool Enabled { get; set; }
public virtual void Initialize()
{
}
public virtual void OnStateEntry(IState old)
{
}
public virtual void OnStateUpdate(float deltaTime)
{
}
public virtual void OnStateExit(IState old, IState newState)
{
}
public virtual void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime)
{
}
public virtual void UpdateRotation(ref Quaternion currentRotation, float deltaTime)
{
}
public virtual void BeforeUpdateMovement(float deltaTime)
{
}
public virtual void AfterUpdateMovement(float deltaTime)
{
}
public virtual void ExecuteCommand<T>(T command)
{
}
public virtual void OnAnimatorMove()
{
MotionVelocity = animancerComponent.Animator.velocity;
MotionAngularVelocity = animancerComponent.Animator.angularVelocity;
MotionDeltaRotation = animancerComponent.Animator.deltaRotation;
}
}
[Serializable]
public sealed class Empty : MotionBasedState
{
public override void OnStateEntry(IState old)
{
base.OnStateEntry(old);
animancerComponent.Layers[0].Stop();
}
}
[Serializable]
public sealed class Idle : MotionBasedState
{
[SerializeField] private AnimationClip clip;
public override void OnStateEntry(IState old)
{
base.OnStateEntry(old);
animancerComponent.Play(clip,0.2f);
}
public override void OnStateUpdate(float deltaTime)
{
base.OnStateUpdate(deltaTime);
var transform = agent.transform;
if (!(agent.remainingDistance > 0.1f)) return;
if(MathV.IsForward(transform.position, transform.forward, agent.steeringTarget))
{
movement.TransitionState<Walk>();
}
else
{
movement.TransitionState<IdleTurn>();
}
}
}
[Serializable]
public sealed class IdleTurn : MotionBasedState
{
[SerializeField] private AnimationClip clip;
private AnimancerState _state;
private Quaternion entryRotation;
Quaternion targetRotation;
2023-12-30 17:37:48 +08:00
public override void OnStateEntry(IState old)
{
base.OnStateEntry(old);
2023-12-30 17:37:48 +08:00
if (clip)
_state = animancerComponent.Play(clip, 0.2f);
entryRotation = movement.Rotation;
var direction = agent.steeringTarget - movement.Position;
if (direction.sqrMagnitude > 0.1f)
{
targetRotation =
Quaternion.LookRotation(
Vector3.ProjectOnPlane(agent.steeringTarget - agent.transform.position, Vector3.up)
);
}
else
{
targetRotation = entryRotation;
}
2023-12-30 17:37:48 +08:00
_state.Events.OnEnd += movement.TransitionState<Idle>;
}
public override void OnStateExit(IState old, IState newState)
{
base.OnStateExit(old, newState);
agent.transform.rotation = targetRotation;
2023-12-30 17:37:48 +08:00
_state = null;
}
public override void OnStateUpdate(float deltaTime)
{
base.OnStateUpdate(deltaTime);
2023-12-30 17:37:48 +08:00
if (_state is null)
{
if (Quaternion.Angle(movement.Rotation, targetRotation) < 1)
movement.TransitionState<Idle>();
return;
}
else
{
if (!(_state.Time > clip.length)) return;
movement.TransitionState<Idle>();
}
}
public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime)
{
base.UpdateVelocity(ref currentVelocity, deltaTime);
currentVelocity = default;
}
public override void UpdateRotation(ref Quaternion currentRotation, float deltaTime)
{
base.UpdateRotation(ref currentRotation, deltaTime);
currentRotation = Quaternion.Lerp(entryRotation, targetRotation, _state.NormalizedTime);
}
}
2023-12-30 17:37:48 +08:00
[Serializable]
public sealed class Blocked : MotionBasedState
{
[RuntimeInitializeOnLoadMethod]
private static void Reload()
{
BlockAreas.Clear();
}
internal static readonly ConcurrentDictionary<int, ISceneBlockArea> BlockAreas = new();
internal static bool IsBlocked(Collider x)
{
var instanceId = x.GetInstanceID();
var area = BlockAreas.GetOrAdd(instanceId, GetBlockArea);
return area switch
{
not null => area.IsBlocked,
_ => false,
};
ISceneBlockArea GetBlockArea(int id)
{
return x.GetComponent<ISceneBlockArea>();
}
}
internal static bool DestinationIsBlocked(Collider x, Vector3 initialPosition,Vector3 direction)
{
if (IsBlocked(x) is false) return false;
var block = BlockAreas[x.GetInstanceID()];
var dir = Vector3.ProjectOnPlane(direction, Vector3.up).normalized;
return block.InRange(initialPosition, dir);
}
[SerializeField] private AnimationClip clip;
private Vector3 initialPosition;
public override void OnStateEntry(IState old)
{
animancerComponent.Play(clip);
initialPosition = movement.Position + movement.ViewCenter;
}
public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime)
{
base.UpdateVelocity(ref currentVelocity, deltaTime);
currentVelocity = default;
}
public override void OnStateUpdate(float deltaTime)
{
base.OnStateUpdate(deltaTime);
if(movement.colliders.Length is 0)
{
movement.TransitionState<Idle>();
return;
}
if(movement.colliders.Any(LocalDestinationIsBlocked))
{
}
else
{
movement.TransitionState<Idle>();
}
}
private bool LocalDestinationIsBlocked(Collider x)
{
var direction = agent.nextPosition - initialPosition;
return DestinationIsBlocked(x, initialPosition, direction.normalized);
}
}
[Serializable]
public sealed class Walk : MotionBasedState
{
[SerializeField] private LinearMixerTransition _state;
private AnimancerState _playingState;
private float rot = 0;
public override void OnStateEntry(IState old)
{
base.OnStateEntry(old);
_playingState = animancerComponent.Play(_state);
}
public override void OnStateExit(IState old, IState newState)
{
base.OnStateExit(old, newState);
_playingState = null;
}
public override void OnStateUpdate(float deltaTime)
{
base.OnStateUpdate(deltaTime);
if (agent.remainingDistance < 0.1f)
{
movement.TransitionState<Idle>();
return;
}
2023-12-30 17:37:48 +08:00
if (agent.isOnOffMeshLink)
{
movement.TransitionState<OffMeshLink>();
return;
}
2023-12-30 17:37:48 +08:00
//if(colliders.Any(Blocked.IsBlocked))
if (movement.colliders.Any(LocalDestinationIsBlocked))
{
movement.TransitionState<Blocked>();
return;
}
return;
bool LocalDestinationIsBlocked(Collider x)
{
var direction = agent.nextPosition - movement.Position;
return Blocked.DestinationIsBlocked(x, movement.Position+movement.ViewCenter, direction.normalized);
}
}
public override void AfterUpdateMovement(float deltaTime)
{
base.AfterUpdateMovement(deltaTime);
movement.transform.position = agent.nextPosition;
}
public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime)
{
base.UpdateVelocity(ref currentVelocity, deltaTime);
currentVelocity = MotionVelocity;
}
public override void UpdateRotation(ref Quaternion currentRotation, float deltaTime)
{
base.UpdateRotation(ref currentRotation, deltaTime);
var pathRotation = currentRotation;
var lerpRotation = currentRotation;
var direction = agent.steeringTarget - agent.transform.position;
if(direction.sqrMagnitude>0.1f)
{
direction = Vector3.ProjectOnPlane(direction, Vector3.up);
pathRotation = Quaternion.LookRotation(direction);
lerpRotation =
Quaternion.RotateTowards(
currentRotation,
pathRotation,
360 * deltaTime
)
;
}
if (_playingState is not null)
{
var dir = pathRotation * Vector3.forward; //位置差,方向
var dot = Vector3.Dot(movement.Forward, dir.normalized);//点乘判断前后dot >0在前<0在后
var dot1 = Vector3.Dot(movement.transform.right, dir.normalized);//点乘判断左右: dot1>0在右<0在左
var angle = Mathf.Acos(Vector3.Dot(movement.Forward.normalized, dir.normalized)) * Mathf.Rad2Deg;//通过点乘求出
angle = dot1 > 0 ? angle : -angle;
var clamp = Mathf.Clamp(angle/45, -1, 1);
rot = Mathf.MoveTowards(rot, clamp, 8 * deltaTime);
_state.State.Parameter =rot ;
//Debug.Log($"angle:{angle} dot:{dot} dot1:{dot1} clamp:{clamp}");
}
currentRotation = lerpRotation;
}
}
[Serializable]
public sealed class OffMeshLink : MotionBasedState
{
[SerializeField] public AnimationClip[] clips;
private AnimancerState _state;
private Vector3 startPos;
private Vector3 endPos;
private Spline _spline;
private (float progress,float increment) splineProgress;
public override void OnStateEntry(IState old)
{
base.OnStateEntry(old);
if (clips.TryGetElementAt(agent.currentOffMeshLinkData.offMeshLink.area, out var clip) && clip)
{
agent.autoTraverseOffMeshLink = false;
_state = animancerComponent.Play(clip);
_state.Events.OnEnd += movement.TransitionState<Idle>;
_spline = agent.currentOffMeshLinkData.offMeshLink.TryGetComponent<SplineContainer>(out var splineContainer) ? splineContainer[0] : null;
var currentOffMeshLinkData = agent.currentOffMeshLinkData;
startPos = currentOffMeshLinkData.startPos;
endPos = currentOffMeshLinkData.endPos;
if (_spline is not null)
{
var offsetTransform= splineContainer.transform;
var isStart =
Vector3.Distance(
movement.Position,
offsetTransform.TransformPoint(splineContainer.Splines[0].Knots.First().Position)
) <
Vector3.Distance(
movement.Position,
offsetTransform.TransformPoint(splineContainer.Splines[0].Knots.Last().Position)
);
splineProgress = isStart ? (0, 1) : (1, -1);
}
}
else
{
_state = null;
agent.autoTraverseOffMeshLink = true;
movement.TransitionState<Idle>();
}
}
public override void OnStateUpdate(float deltaTime)
{
base.OnStateUpdate(deltaTime);
try
{
if (_spline is not null)
{
splineProgress.progress += splineProgress.increment * deltaTime;
var link = agent.currentOffMeshLinkData.offMeshLink.transform;
movement.transform.position =
link.position + link.rotation *
_spline.EvaluatePosition(splineProgress.progress);
}
else
{
var currentOffMeshLinkData = agent.currentOffMeshLinkData;
movement.transform.position = Vector3.Lerp(
currentOffMeshLinkData.startPos,
currentOffMeshLinkData.endPos,
_state.NormalizedTime
);
}
}
catch (Exception e)
{
Debug.LogException(e);
movement.TransitionState<Idle>();
}
}
public override void OnStateExit(IState old, IState newState)
{
base.OnStateExit(old, newState);
agent.CompleteOffMeshLink();
}
}
}