namespace Dreamteck.Splines.Examples { using System.Collections; using System.Collections.Generic; using UnityEngine; public class Wagon : MonoBehaviour { //A helper class which contains an information for a spline and the points //between which the spline is traversed (start and end) //If one of the points is equal to -1 it means that there is no constraint public class SplineSegment { public SplineComputer spline; public int start = -1, end = -1; public Spline.Direction direction; public SplineSegment(SplineComputer spline, int entryPoint, Spline.Direction direction) { this.spline = spline; start = entryPoint; this.direction = direction; } public SplineSegment(SplineSegment input) { spline = input.spline; start = input.start; end = input.end; direction = input.direction; } public double Travel(double percent, float distance, Spline.Direction direction, out float moved, bool loop) { double max = direction == Spline.Direction.Forward ? 1.0 : 0.0; if (start >= 0) max = spline.GetPointPercent(start); return TravelClamped(percent, distance, direction, max, out moved, loop); } //Travel the spline segment by automatically starting at the segment's exit (end) public double Travel(float distance, Spline.Direction direction, out float moved, bool loop) { double startPercent = spline.GetPointPercent(end); double max = direction == Spline.Direction.Forward ? 1.0 : 0.0; if (start >= 0) max = spline.GetPointPercent(start); return TravelClamped(startPercent, distance, direction, max, out moved, loop); } //Travel the spline segment while not exceeding the "max" percent //It also supports looping splines unlike the standard Travel methods found in SplineComputer and SplineUser double TravelClamped(double percent, float distance, Spline.Direction direction, double max, out float moved, bool loop) { moved = 0f; float traveled = 0f; double result = spline.Travel(percent, distance, out traveled, direction); moved += traveled; if (loop && moved < distance) { if (direction == Spline.Direction.Forward && Mathf.Approximately((float)result, 1f)) { result = spline.Travel(0.0, distance - moved, out traveled, direction); } else if (direction == Spline.Direction.Backward && Mathf.Approximately((float)result, 0f)) { result = spline.Travel(1.0, distance - moved, out traveled, direction); } moved += traveled; } if (direction == Spline.Direction.Forward && percent <= max) { if (result > max) { moved -= spline.CalculateLength(result, max); result = max; } } else if (direction == Spline.Direction.Backward && percent >= max) { if (result < max) { moved -= spline.CalculateLength(max, result); result = max; } } return result; } } SplineTracer tracer; public bool isEngine = false; public Wagon back; public float offset = 0f; Wagon front; SplineSegment segment, tempSegment; private void Awake() { tracer = GetComponent(); //Wagon compoenent that is attached to the train engine and is marked as "isEngine" will //run a recursive setup for the rest of the wagons if (isEngine) SetupRecursively(null, new SplineSegment(tracer.spline, -1, tracer.direction)); } void SetupRecursively(Wagon frontWagon, SplineSegment inputSegment) { front = frontWagon; segment = inputSegment; if (back != null) back.SetupRecursively(this, segment); } public void UpdateOffset() { ApplyOffset(); if (back != null) back.UpdateOffset(); } Wagon GetRootWagon() { Wagon current = this; while (current.front != null) current = current.front; return current; } void ApplyOffset() { if (isEngine) { ResetSegments(); return; } float totalMoved = 0f, moved = 0f; double start = front.tracer.UnclipPercent(front.tracer.result.percent); //Travel backwards along the front wagon's spline Spline.Direction inverseDirection = front.segment.direction; InvertDirection(ref inverseDirection); SplineComputer spline = front.segment.spline; double percent = front.segment.Travel(start, offset, inverseDirection, out moved, front.segment.spline.isClosed); totalMoved += moved; //Finalize if moved fully without reaching a spline end or a junction if (Mathf.Approximately(totalMoved, offset)) { if (segment != front.segment) { if (back != null) back.segment = segment; } if(segment != front.segment) segment = front.segment; ApplyTracer(spline, percent, front.tracer.direction); return; } //Otherwise, move along the current recorded spline segment if (segment != front.segment) { inverseDirection = segment.direction; InvertDirection(ref inverseDirection); spline = segment.spline; percent = segment.Travel(offset - totalMoved, inverseDirection, out moved, segment.spline.isClosed); totalMoved += moved; } ApplyTracer(spline, percent, segment.direction); } void ResetSegments() { Wagon current = back; bool same = true; while (current != null) { if(current.segment != segment) { same = false; break; } current = current.back; } //if all wagons are on the same segment, remove the segment entrance so that they can loop if(same) segment.start = -1; } void ApplyTracer(SplineComputer spline, double percent, Spline.Direction direction) { bool rebuild = tracer.spline != spline; tracer.spline = spline; if (rebuild) tracer.RebuildImmediate(); tracer.direction = direction; tracer.SetPercent(tracer.ClipPercent(percent)); } public void EnterSplineSegment(int previousSplineExitPoint, SplineComputer spline, int entryPoint, Spline.Direction direction) { if (!isEngine) return; if (back != null) { segment.end = previousSplineExitPoint; back.segment = segment; } segment = new SplineSegment(spline, entryPoint, direction); } static void InvertDirection(ref Spline.Direction direction) { if (direction == Spline.Direction.Forward) direction = Spline.Direction.Backward; else direction = Spline.Direction.Forward; } } }