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 Cysharp.Threading.Tasks; using I18N.Other; 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 command) { } public virtual void DrawGizmos() { } 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,old switch { null=>1f, _=>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.referenceRenderer.isVisible is false) { movement.TransitionState(); } else { movement.TransitionState(); } } 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 async 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 (_state is not null) { await _state; if(Enabled is false) return; movement.TransitionState(); } } 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.referenceRenderer.isVisible is false) { movement.TransitionState(); return; } if (clip) { if (_state.Time > clip.length) { movement.TransitionState(); 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); currentRotation *= MotionDeltaRotation; } 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 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(); } } 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(); return; } if(movement.colliders.Any(LocalDestinationIsBlocked)) { } else { movement.TransitionState(); } } private bool LocalDestinationIsBlocked(Collider x) { var direction = agent.nextPosition - initialPosition; return DestinationIsBlocked(x, initialPosition, direction.normalized); } } [Serializable] public sealed class Walk : MotionBasedState { [SerializeField] private AnimationClip[] entryClips; //[SerializeField] private LinearMixerTransition _state; [SerializeField] private AnimationClip[] clips; private AnimancerState _playingState; private float rot = 0; private Vector3 _savedVelocity; public override async void OnStateEntry(IState old) { base.OnStateEntry(old); if (entryClips is { Length: > 0 } && agent.remainingDistance > 0.2f) { var state = animancerComponent.Play(entryClips.Random(),0.2f); await state; if (!Enabled || !movement) return; } _playingState = animancerComponent.Play(clips.Random(),0.2f); } 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.isOnNavMesh is false) return; if (agent.remainingDistance < 0.1f) { movement.TransitionState(); return; } if (agent.isOnOffMeshLink) { movement.TransitionState(); return; } //if(colliders.Any(Blocked.IsBlocked)) // if (movement.colliders.Any(LocalDestinationIsBlocked)) // { // movement.TransitionState(); // 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); switch (animancerComponent.Animator.cullingMode,movement.referenceRenderer.isVisible) { case (_,true): currentVelocity = MotionVelocity; break; default: //currentVelocity = agent.nextPosition - movement.Position; break; } } 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 || movement.referenceRenderer.isVisible is false) { direction = Vector3.ProjectOnPlane(direction, Vector3.up); if (direction.magnitude < 0.01f) return; pathRotation = Quaternion.LookRotation(direction); lerpRotation = Quaternion.RotateTowards( currentRotation, pathRotation, 360 * deltaTime ) ; if (movement.referenceRenderer.isVisible is false) { currentRotation = pathRotation; return; } } // 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; _spline = agent.currentOffMeshLinkData.offMeshLink.TryGetComponent(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(); } } 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(); } } public override void OnStateExit(IState old, IState newState) { base.OnStateExit(old, newState); agent.CompleteOffMeshLink(); } } }