Files
2026-06-07 22:59:32 +08:00

332 lines
11 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;
}
}
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;
}
}
/// <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 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<NodeConnection> 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<Rigidbody>();
}
protected virtual Rigidbody2D GetRigidbody2D()
{
return GetComponent<Rigidbody2D>();
}
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<NodeConnection> queue = new List<NodeConnection>();
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++;
}
}
}