122 lines
3 KiB
C#
122 lines
3 KiB
C#
using System;
|
|
using System.Threading;
|
|
using KitsuneCafe.Extension;
|
|
using KitsuneCafe.Sys;
|
|
using R3;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
|
|
namespace KitsuneCafe.Entities
|
|
{
|
|
public class WaypointFollower : MonoBehaviour
|
|
{
|
|
[SerializeField]
|
|
private Transform[] waypoints;
|
|
|
|
[SerializeField, Range(0.01f, 2f)]
|
|
private float speed = 0.25f;
|
|
|
|
[SerializeField]
|
|
private float minimumDistance = 0.05f;
|
|
|
|
[Space(8)]
|
|
[SerializeField]
|
|
private UnityEvent<int> OnWaypointReached;
|
|
public event EventHandler<int> WaypointReached = delegate { };
|
|
|
|
[SerializeField]
|
|
private UnityEvent OnMoving;
|
|
public event EventHandler Moving = delegate { };
|
|
|
|
[SerializeField]
|
|
private UnityEvent OnCompleted;
|
|
public event EventHandler Completed = delegate { };
|
|
|
|
[SerializeField]
|
|
private UnityEvent OnRestarted;
|
|
public event EventHandler Restarted = delegate { };
|
|
|
|
private readonly ReactiveProperty<int> index = new(0);
|
|
private float sqrDistance;
|
|
|
|
private CancellationTokenSource disableCancellationTokenSource;
|
|
private CancellationToken disableCancellationToken => disableCancellationTokenSource.Token;
|
|
|
|
private void OnValidate()
|
|
{
|
|
sqrDistance = Mathf.Pow(minimumDistance, 2);
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
disableCancellationTokenSource = new();
|
|
|
|
index.AsObservable()
|
|
.SelectMany(index =>
|
|
DirectionTo(waypoints[index].position)
|
|
.Do(
|
|
onNext: _ => NotifyMoving(),
|
|
onCompleted: result =>
|
|
{
|
|
if (result.IsSuccess) NotifyWaypointReached(index);
|
|
}
|
|
)
|
|
)
|
|
.Subscribe(ApplyDirection)
|
|
.RegisterTo(disableCancellationToken);
|
|
}
|
|
|
|
private void NotifyMoving()
|
|
{
|
|
Moving?.Invoke(this, EventArgs.Empty);
|
|
OnMoving?.Invoke();
|
|
}
|
|
|
|
private void NotifyWaypointReached(int index)
|
|
{
|
|
WaypointReached?.Invoke(this, index);
|
|
OnWaypointReached.Invoke(index);
|
|
|
|
if (index == waypoints.Length - 1)
|
|
{
|
|
Completed?.Invoke(this, EventArgs.Empty);
|
|
OnCompleted?.Invoke();
|
|
}
|
|
|
|
if (index == 0)
|
|
{
|
|
Restarted?.Invoke(this, EventArgs.Empty);
|
|
OnRestarted?.Invoke();
|
|
}
|
|
}
|
|
|
|
private void ApplyDirection(Vector3 vector)
|
|
{
|
|
transform.position += vector.normalized * speed;
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
disableCancellationTokenSource.Cancel();
|
|
}
|
|
|
|
public void MoveNext()
|
|
{
|
|
index.Value = (index.Value + 1) % waypoints.Length;
|
|
}
|
|
|
|
private Observable<Vector3> DirectionTo(Vector3 destination) =>
|
|
Observable.EveryUpdate(UnityFrameProvider.FixedUpdate)
|
|
.Select(_ => destination - transform.position)
|
|
.TakeWhile(_ => transform.position.SqrDistance(destination) > sqrDistance);
|
|
|
|
private void OnDrawGizmos()
|
|
{
|
|
Gizmos.color = Color.white;
|
|
foreach (var point in waypoints)
|
|
{
|
|
Gizmos.DrawWireSphere(point.position, minimumDistance);
|
|
}
|
|
}
|
|
}
|
|
}
|