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

470 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Animancer;
using AYellowpaper.SerializedCollections;
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;
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>();
}
}
public override void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime)
{
base.UpdateVelocity(ref currentVelocity, deltaTime);
currentVelocity = default;
}
}
[Serializable]
public sealed class IdleTurn : MotionBasedState
{
[SerializeField] private AnimationClip clip;
private AnimancerState _state;
private Quaternion entryRotation;
Quaternion targetRotation;
public override void OnStateEntry(IState old)
{
base.OnStateEntry(old);
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;
}
if (clip)
_state.Events.OnEnd += movement.TransitionState<Idle>;
}
public override void OnStateExit(IState old, IState newState)
{
base.OnStateExit(old, newState);
agent.transform.rotation = targetRotation;
_state = null;
}
public override void OnStateUpdate(float deltaTime)
{
base.OnStateUpdate(deltaTime);
if (Quaternion.Angle(movement.Rotation, targetRotation) < 1)
{
movement.TransitionState<Idle>();
return;
}
if (clip)
{
if (_state.Time > clip.length)
{
movement.TransitionState<Idle>();
return;
}
}
}
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);
if (clip)
{
currentRotation = Quaternion.Lerp(entryRotation, targetRotation, _state.NormalizedTime);
}
else
{
currentRotation = Quaternion.RotateTowards(currentRotation, targetRotation, 720 * deltaTime);
}
}
}
[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;
}
if (agent.isOnOffMeshLink)
{
movement.TransitionState<OffMeshLink>();
return;
}
//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();
}
}
}