385 lines
12 KiB
C#
385 lines
12 KiB
C#
using System.Collections;
|
|
using UnityEngine.Events;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Serialization;
|
|
|
|
namespace Dreamteck.Splines
|
|
{
|
|
public class SplineTracer : SplineUser
|
|
{
|
|
public class NodeConnection
|
|
{
|
|
public Node node;
|
|
public int point = 0;
|
|
|
|
public NodeConnection(Node node, int point)
|
|
{
|
|
this.node = node;
|
|
this.point = point;
|
|
}
|
|
}
|
|
|
|
public enum PhysicsMode { Transform, Rigidbody, Rigidbody2D }
|
|
public PhysicsMode physicsMode
|
|
{
|
|
get { return _physicsMode; }
|
|
set
|
|
{
|
|
_physicsMode = value;
|
|
RefreshTargets();
|
|
}
|
|
}
|
|
|
|
public TransformModule motion
|
|
{
|
|
get
|
|
{
|
|
if (_motion == null) _motion = new TransformModule();
|
|
return _motion;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the unmodified result from the evaluation
|
|
/// </summary>
|
|
public SplineSample result
|
|
{
|
|
get { return _result; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the offsetted evaluation result from the current follow position
|
|
/// </summary>
|
|
public SplineSample modifiedResult
|
|
{
|
|
get
|
|
{
|
|
return _finalResult;
|
|
}
|
|
}
|
|
|
|
public bool dontLerpDirection
|
|
{
|
|
get { return _dontLerpDirection; }
|
|
set
|
|
{
|
|
if (value != _dontLerpDirection)
|
|
{
|
|
_dontLerpDirection = value;
|
|
ApplyMotion();
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual Spline.Direction direction
|
|
{
|
|
get { return _direction; }
|
|
set
|
|
{
|
|
if (value != _direction)
|
|
{
|
|
_direction = value;
|
|
ApplyMotion();
|
|
}
|
|
}
|
|
}
|
|
|
|
[HideInInspector]
|
|
public bool applyDirectionRotation = true;
|
|
[HideInInspector]
|
|
public bool useTriggers = false;
|
|
[HideInInspector]
|
|
public int triggerGroup = 0;
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
protected Spline.Direction _direction = Spline.Direction.Forward;
|
|
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
protected bool _dontLerpDirection = false;
|
|
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
protected PhysicsMode _physicsMode = PhysicsMode.Transform;
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
protected TransformModule _motion = null;
|
|
|
|
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
protected Rigidbody targetRigidbody = null;
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
protected Rigidbody2D targetRigidbody2D = null;
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
protected Transform targetTransform = null;
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
protected SplineSample _result = new SplineSample();
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
protected SplineSample _finalResult = new SplineSample();
|
|
|
|
public delegate void JunctionHandler(List<NodeConnection> passed);
|
|
|
|
public event JunctionHandler onNode;
|
|
public event EmptySplineHandler onMotionApplied;
|
|
|
|
private SplineTrigger[] triggerInvokeQueue = new SplineTrigger[0];
|
|
private List<NodeConnection> nodeConnectionQueue = new List<NodeConnection>();
|
|
private int addTriggerIndex = 0;
|
|
|
|
private const double MIN_DELTA = 0.000001;
|
|
|
|
#if UNITY_EDITOR
|
|
public override void EditorAwake()
|
|
{
|
|
base.EditorAwake();
|
|
RefreshTargets();
|
|
ApplyMotion();
|
|
}
|
|
#endif
|
|
|
|
protected override void Awake()
|
|
{
|
|
base.Awake();
|
|
RefreshTargets();
|
|
}
|
|
|
|
protected virtual void Start()
|
|
{
|
|
|
|
}
|
|
|
|
public virtual void SetPercent(double percent, bool checkTriggers = false, bool handleJunctions = false)
|
|
{
|
|
if (sampleCount == 0) return;
|
|
double lastPercent = _result.percent;
|
|
Evaluate(percent, ref _result);
|
|
ApplyMotion();
|
|
if (checkTriggers)
|
|
{
|
|
CheckTriggers(lastPercent, percent);
|
|
InvokeTriggers();
|
|
}
|
|
if (handleJunctions)
|
|
{
|
|
CheckNodes(lastPercent, percent);
|
|
}
|
|
}
|
|
|
|
public double GetPercent()
|
|
{
|
|
return _result.percent;
|
|
}
|
|
|
|
public virtual void SetDistance(float distance, bool checkTriggers = false, bool handleJunctions = false)
|
|
{
|
|
double lastPercent = _result.percent;
|
|
Evaluate(Travel(0.0, distance, Spline.Direction.Forward), ref _result);
|
|
ApplyMotion();
|
|
if (checkTriggers)
|
|
{
|
|
CheckTriggers(lastPercent, _result.percent);
|
|
InvokeTriggers();
|
|
}
|
|
if (handleJunctions)
|
|
{
|
|
CheckNodes(lastPercent, _result.percent);
|
|
}
|
|
}
|
|
|
|
protected virtual Rigidbody GetRigidbody()
|
|
{
|
|
return GetComponent<Rigidbody>();
|
|
}
|
|
|
|
protected virtual Rigidbody2D GetRigidbody2D()
|
|
{
|
|
return GetComponent<Rigidbody2D>();
|
|
}
|
|
|
|
protected virtual Transform GetTransform()
|
|
{
|
|
return transform;
|
|
}
|
|
|
|
protected void ApplyMotion()
|
|
{
|
|
if (sampleCount == 0) return;
|
|
ModifySample(ref _result, ref _finalResult);
|
|
if (_dontLerpDirection)
|
|
{
|
|
double unclippedPercent = UnclipPercent(_result.percent);
|
|
int index;
|
|
double lerp;
|
|
spline.GetSamplingValues(unclippedPercent, out index, out lerp);
|
|
_finalResult.forward = spline[index].forward;
|
|
_finalResult.up = spline[index].up;
|
|
}
|
|
|
|
motion.targetUser = this;
|
|
motion.splineResult = _finalResult;
|
|
if (applyDirectionRotation) motion.direction = _direction;
|
|
else motion.direction = Spline.Direction.Forward;
|
|
|
|
switch (_physicsMode)
|
|
{
|
|
case PhysicsMode.Transform:
|
|
if (targetTransform == null) RefreshTargets();
|
|
if (targetTransform == null) return;
|
|
motion.ApplyTransform(targetTransform);
|
|
if (onMotionApplied != null) onMotionApplied();
|
|
break;
|
|
case PhysicsMode.Rigidbody:
|
|
if (targetRigidbody == null)
|
|
{
|
|
RefreshTargets();
|
|
if (targetRigidbody == null) throw new MissingComponentException("There is no Rigidbody attached to " + name + " but the Physics mode is set to use one.");
|
|
}
|
|
motion.ApplyRigidbody(targetRigidbody);
|
|
if (onMotionApplied != null) onMotionApplied();
|
|
break;
|
|
case PhysicsMode.Rigidbody2D:
|
|
if (targetRigidbody2D == null)
|
|
{
|
|
RefreshTargets();
|
|
if (targetRigidbody2D == null) throw new MissingComponentException("There is no Rigidbody2D attached to " + name + " but the Physics mode is set to use one.");
|
|
}
|
|
motion.ApplyRigidbody2D(targetRigidbody2D);
|
|
if (onMotionApplied != null) onMotionApplied();
|
|
break;
|
|
}
|
|
}
|
|
|
|
protected void CheckNodes(double from, double to)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying) return;
|
|
#endif
|
|
if (onNode == null) return;
|
|
if (from == to) return;
|
|
UnclipPercent(ref from);
|
|
UnclipPercent(ref to);
|
|
Spline.FormatFromTo(ref from, ref to, true);
|
|
int fromPoint, toPoint;
|
|
fromPoint = spline.PercentToPointIndex(from, _direction);
|
|
toPoint = spline.PercentToPointIndex(to, _direction);
|
|
|
|
if (fromPoint != toPoint)
|
|
{
|
|
if (_direction == Spline.Direction.Forward)
|
|
{
|
|
for (int i = fromPoint + 1; i <= toPoint; i++)
|
|
{
|
|
NodeConnection junction = GetJunction(i);
|
|
if (junction != null) nodeConnectionQueue.Add(junction);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = toPoint - 1; i >= fromPoint; i--)
|
|
{
|
|
NodeConnection junction = GetJunction(i);
|
|
if (junction != null) nodeConnectionQueue.Add(junction);
|
|
}
|
|
}
|
|
}
|
|
else if (from < MIN_DELTA && to > from)
|
|
{
|
|
NodeConnection junction = GetJunction(0);
|
|
if (junction != null) nodeConnectionQueue.Add(junction);
|
|
}
|
|
else if (to > 1.0 - MIN_DELTA && from < to)
|
|
{
|
|
int pointCount = spline.pointCount - 1;
|
|
if (spline.isClosed)
|
|
{
|
|
pointCount = spline.pointCount;
|
|
}
|
|
NodeConnection junction = GetJunction(pointCount);
|
|
if (junction != null) nodeConnectionQueue.Add(junction);
|
|
}
|
|
}
|
|
|
|
protected void InvokeNodes()
|
|
{
|
|
if(nodeConnectionQueue.Count > 0)
|
|
{
|
|
onNode(nodeConnectionQueue);
|
|
nodeConnectionQueue.Clear();
|
|
}
|
|
}
|
|
|
|
protected void CheckTriggers(double from, double to)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying) return;
|
|
#endif
|
|
if (!useTriggers) return;
|
|
if (from == to) return;
|
|
UnclipPercent(ref from);
|
|
UnclipPercent(ref to);
|
|
if (triggerGroup < 0 || triggerGroup >= spline.triggerGroups.Length) return;
|
|
for (int i = 0; i < spline.triggerGroups[triggerGroup].triggers.Length; i++)
|
|
{
|
|
if (spline.triggerGroups[triggerGroup].triggers[i] == null) continue;
|
|
if (spline.triggerGroups[triggerGroup].triggers[i].Check(from, to)) AddTriggerToQueue(spline.triggerGroups[triggerGroup].triggers[i]);
|
|
}
|
|
}
|
|
|
|
NodeConnection GetJunction(int pointIndex)
|
|
{
|
|
Node node = spline.GetNode(pointIndex);
|
|
if (node == null) return null;
|
|
return new NodeConnection(node, pointIndex);
|
|
}
|
|
|
|
protected void InvokeTriggers()
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying) return;
|
|
#endif
|
|
for (int i = 0; i < addTriggerIndex; i++)
|
|
{
|
|
if (triggerInvokeQueue[i] != null)
|
|
{
|
|
triggerInvokeQueue[i].Invoke(this);
|
|
}
|
|
}
|
|
addTriggerIndex = 0;
|
|
}
|
|
|
|
protected void RefreshTargets()
|
|
{
|
|
switch (_physicsMode)
|
|
{
|
|
case PhysicsMode.Transform:
|
|
targetTransform = GetTransform();
|
|
break;
|
|
case PhysicsMode.Rigidbody:
|
|
targetRigidbody = GetRigidbody();
|
|
break;
|
|
case PhysicsMode.Rigidbody2D:
|
|
targetRigidbody2D = GetRigidbody2D();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void AddTriggerToQueue(SplineTrigger trigger)
|
|
{
|
|
if (addTriggerIndex >= triggerInvokeQueue.Length)
|
|
{
|
|
SplineTrigger[] newQueue = new SplineTrigger[triggerInvokeQueue.Length + spline.triggerGroups[triggerGroup].triggers.Length];
|
|
triggerInvokeQueue.CopyTo(newQueue, 0);
|
|
triggerInvokeQueue = newQueue;
|
|
}
|
|
triggerInvokeQueue[addTriggerIndex] = trigger;
|
|
addTriggerIndex++;
|
|
}
|
|
}
|
|
}
|