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; } } SplineTrigger[] triggerInvokeQueue = new SplineTrigger[0]; int addTriggerIndex = 0; 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 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 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(); private bool setPercentOnRebuild = false; private double targetPercentOnRebuild = 0.0; public delegate void JunctionHandler(List passed); public event JunctionHandler onNode; public event EmptySplineHandler onMotionApplied; #if UNITY_EDITOR public override void EditorAwake() { base.EditorAwake(); RefreshTargets(); } #endif protected virtual void Start() { RefreshTargets(); } public virtual void SetPercent(double percent, bool checkTriggers = false, bool handleJunctions = false) { if (sampleCount == 0) return; double lastPercent = _result.percent; Evaluate(percent, _result); ApplyMotion(); if (checkTriggers) { CheckTriggers(lastPercent, percent); InvokeTriggers(); } if(handleJunctions) HandleJunctions(lastPercent, percent); } public virtual void SetDistance(float distance, bool checkTriggers = false, bool handleJunctions = false) { if (sampleCount == 0) return; double lastPercent = _result.percent; Evaluate(Travel(0.0, distance, Spline.Direction.Forward), _result); ApplyMotion(); if (checkTriggers) { CheckTriggers(lastPercent, _result.percent); InvokeTriggers(); } if (handleJunctions) HandleJunctions(lastPercent, _result.percent); } protected override void PostBuild() { if (setPercentOnRebuild) { SetPercent(targetPercentOnRebuild); setPercentOnRebuild = false; } } protected virtual Rigidbody GetRigidbody() { return GetComponent(); } protected virtual Rigidbody2D GetRigidbody2D() { return GetComponent(); } protected virtual Transform GetTransform() { return transform; } protected void ApplyMotion() { ModifySample(_result, _finalResult); motion.targetUser = this; motion.splineResult = _finalResult; if (applyDirectionRotation) motion.direction = _direction; else motion.direction = Spline.Direction.Forward; #if UNITY_EDITOR if (!Application.isPlaying) { if (targetTransform == null) RefreshTargets(); if (targetTransform == null) return; motion.ApplyTransform(targetTransform); if (onMotionApplied != null) onMotionApplied(); return; } #endif 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 HandleJunctions(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) { List queue = new List(); if (_direction == Spline.Direction.Forward) { for (int i = fromPoint + 1; i <= toPoint; i++) { NodeConnection junction = GetJunction(i); if (junction != null) queue.Add(junction); } } else { for (int i = toPoint-1; i >= fromPoint; i--) { NodeConnection junction = GetJunction(i); if (junction != null) queue.Add(junction); } } if(queue.Count > 0) onNode(queue); } } 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(); } 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++; } } }