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++; } } }