You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Production API: Distance-based evaluation, closest point queries, frame caching
The Even Spacing Problem
Most spline tutorials use parameter-based evaluation (Evaluate(t)), which produces uneven spacing on curves. Objects bunch up on tight curves and spread out on straight sections.
This framework solves the problem with arc-length parameterization:
Method
Result
Evaluate(t)
Uneven - objects cluster on curves
EvaluateByDistance(d)
Even - consistent spacing throughout
Stable Orientation with Rotation-Minimizing Frames
The classic Frenet frame (TNB) flips 180° at inflection points, causing objects to suddenly invert. This framework implements the Double Reflection Method for stable orientation:
Frame Mode
Behavior
Frenet
Fast, but flips at inflection points
Rotation Minimizing
Stable, no flipping (recommended)
Reference Up
Always orients toward a reference direction
Curve Types
Supports multiple interpolation methods with the same API:
Editor Integration
Full Unity Editor tooling with scene handles, visualization options, and inspector controls:
Installation
Copy the Assets/Scripts/Splines folder into your Unity project
Unity will compile the assembly definitions automatically
Folder Structure
Assets/Scripts/Splines/
├── Runtime/
│ ├── Core/ # Interfaces and data structures
│ ├── Segments/ # Curve type implementations
│ ├── Math/ # Pure math utilities
│ └── Spline.cs # Main component
├── Editor/ # Inspector and handles
└── Samples/ # Example components
Quick Start
Creating a Spline
Create an empty GameObject
Add the Spline component
Click Initialize in the inspector
Drag control points in the Scene view
Adding Control Points
Method
Action
Inspector
Click "Add Point" button
Scene View
Shift + Left Click on ground
Delete
Select point + Delete key
Usage
Basic Evaluation
publicclassExample:MonoBehaviour{[SerializeField]privateSpline_spline;voidUpdate(){// T-based (uneven spacing)Vector3position=_spline.Evaluate(0.5f);// Distance-based (even spacing) - RECOMMENDEDVector3posAtDistance=_spline.EvaluateByDistance(5.0f);// Normalized distance (0-1 maps to full length)Vector3posAtHalfway=_spline.EvaluateByNormalizedDistance(0.5f);}}
Evaluation with Orientation
// Get position + full orientation frameSplineSamplesample=_spline.EvaluateByDistanceWithFrame(5.0f);transform.position=sample.Position;transform.rotation=sample.Rotation;// Individual vectorsVector3forward=sample.Tangent;Vector3up=sample.Normal;Vector3right=sample.Binormal;
SplineFollower Component
Attach to any GameObject for automatic path following.
// Read control pointsintcount=_spline.ControlPointCount;Vector3worldPos=_spline.GetControlPointWorldPosition(0);// Modify control points_spline.SetControlPointWorldPosition(0,newWorldPosition);_spline.AddControlPoint(newSplinePoint(localPosition));_spline.RemoveControlPoint(index);// Changes trigger automatic rebuild on next evaluation
voidOnEnable(){_spline.OnSplineModified+=OnSplineChanged;}voidOnDisable(){_spline.OnSplineModified-=OnSplineChanged;}voidOnSplineChanged(){// Spline was rebuilt}
BuildWithIntegration(ISplineSegment segment, int resolution)
Build using Simpson's rule
BuildMultiSegment(ISplineSegment[] segments, int resolutionPerSegment)
Build for multiple segments
GetTByDistance(float distance)
Convert distance to t (binary search)
GetDistanceByT(float t)
Convert t to distance
FrameComputation (static class)
// Frenet frame (may flip at inflection points)SplineFrameFrameComputation.ComputeFrenetFrame(Vector3tangent,Vector3secondDerivative)
SplineFrame FrameComputation.ComputeFrenetFrame(ISplineSegment segment,floatt)// Reference-up frame (stable but may twist)
SplineFrame FrameComputation.ComputeFrameWithReferenceUp(Vector3 tangent, Vector3 referenceUp)// Rotation-minimizing frames (stable, no flipping)
SplineFrame[]FrameComputation.ComputeRotationMinimizingFrames(ISplineSegmentsegment,intsampleCount,Vector3initialUp)
SplineFrame FrameComputation.PropagateFrameDoubleReflection(SplineFrameframe,Vector3t1,Vector3t2,Vector3p1,Vector3p2)// Interpolation
SplineFrame FrameComputation.InterpolateFrames(SplineFramea,SplineFrameb,floatt)
SplineFrame FrameComputation.GetFrameFromCache(SplineFrame[]cachedFrames,floatt)
Enums
publicenumSplineType{Linear,QuadraticBezier,CubicBezier,CatmullRom}publicenumFrameMode{Frenet,// Fast but may flipRotationMinimizing,// Stable (recommended)ReferenceUp// Always uses reference direction}publicenumFollowMode{ByTime,ByDistance,ByNormalizedDistance}publicenumLoopMode{Once,Loop,PingPong}publicenumDecoratorSpacingMode{ByCount,ByDistance,ByNormalizedDistance}
Technical Notes
Arc-Length Parameterization
Bezier curves have no closed-form arc-length solution. This framework uses:
Simpson's Rule Integration on derivative magnitude for building the lookup table
Binary Search + Linear Interpolation for O(log n) distance-to-parameter conversion
Rotation-Minimizing Frames
The Frenet frame (TNB) flips 180° at inflection points. This framework implements the Double Reflection Method (Wang et al.) which propagates frames along the curve with minimal rotation.
Performance
Resolution
Memory
Accuracy
Use Case
16
~128 bytes
±2%
Mobile, many splines
64
~512 bytes
±0.5%
Standard games
256
~2KB
±0.1%
Cinematics
The dirty flag system ensures arc-length tables are only rebuilt when control points change.