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;
}
}
///
/// Returns the unmodified result from the evaluation
///
public SplineSample result
{
get { return _result; }
}
///
/// Returns the offsetted evaluation result from the current follow position
///
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 passed);
public event JunctionHandler onNode;
public event EmptySplineHandler onMotionApplied;
private SplineTrigger[] triggerInvokeQueue = new SplineTrigger[0];
private List nodeConnectionQueue = new List();
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();
}
protected virtual Rigidbody2D GetRigidbody2D()
{
return GetComponent();
}
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++;
}
}
}