304 lines
7.4 KiB
C#
304 lines
7.4 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Animancer;
|
|
using AYellowpaper.SerializedCollections;
|
|
using BITKit;
|
|
using BITKit.Entities;
|
|
using BITKit.StateMachine;
|
|
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; }
|
|
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;
|
|
}
|
|
}
|
|
|
|
[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;
|
|
public override void OnStateEntry(IState old)
|
|
{
|
|
base.OnStateEntry(old);
|
|
_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;
|
|
}
|
|
_state.Events.OnEnd += movement.TransitionState<Idle>;
|
|
}
|
|
|
|
public override void OnStateExit(IState old, IState newState)
|
|
{
|
|
base.OnStateExit(old, newState);
|
|
agent.transform.rotation = targetRotation;
|
|
}
|
|
|
|
public override void OnStateUpdate(float deltaTime)
|
|
{
|
|
base.OnStateUpdate(deltaTime);
|
|
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);
|
|
}
|
|
}
|
|
[Serializable]
|
|
public sealed class Walk : MotionBasedState
|
|
{
|
|
[SerializeField] private AnimationClip clip;
|
|
public override void OnStateEntry(IState old)
|
|
{
|
|
base.OnStateEntry(old);
|
|
animancerComponent.Play(clip,0.1f);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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 direction = agent.steeringTarget - agent.transform.position;
|
|
if(direction.sqrMagnitude>0.1f)
|
|
{
|
|
direction = Vector3.ProjectOnPlane(direction, Vector3.up);
|
|
currentRotation =
|
|
Quaternion.RotateTowards(
|
|
currentRotation,
|
|
Quaternion.LookRotation(direction),
|
|
360 * deltaTime
|
|
)
|
|
;
|
|
}
|
|
}
|
|
}
|
|
|
|
[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();
|
|
}
|
|
}
|
|
}
|
|
|