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

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();
}
}
}