Spline Mesher - Pro
4.2.Procedural Splines
Many use cases call for creating Splines from a script. For instance building pipelines or tracing a river down a mountain.
❌ Don’t use SplineContainer.Spline = newSpline or SplineContainer.Splines = newSplineList. This will trigger the deletion of all the splines in the container and immediately trigger their creation again.
The Spline Mesher components reacts to these changes by default and destroys/recreates impactful resources. If this is done repeatedly it can lead to race conditions that can leave stray splines behind.
✅ Do: use SplineContainer.Spline.Copy(newSpline) (possibly in a loop). This merely changes the Spline, allowing a Spline Mesher to simply rebuild according to it.
Example 👇
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace sc.splinemesher.pro.examples
{
public class ProceduralSplines : MonoBehaviour
{
public SplineContainer splineContainer;
[Min(1)] public int count = 5;
public float width = 1f;
public float length = 1f;
private void Reset()
{
splineContainer = GetComponent();
if (!splineContainer) splineContainer = gameObject.AddComponent();
GenerateSplines();
}
public void GenerateSplines()
{
int currentSplineCount = splineContainer.Splines.Count;
//Create a row of splines
float widthOffset = 0;
for (int i = 0; i < count; i++)
{
BezierKnot[] knots = new BezierKnot[2];
for (int j = 0; j < knots.Length; j++)
{
float3 position = (math.forward() * (j * length));
position.x += widthOffset;
knots[j] = new BezierKnot(position, float3.zero, float3.zero, quaternion.identity);
}
widthOffset += width;
Spline spline = new Spline(knots);
//Spline already exists in container, copy new spline into it.
if (i < currentSplineCount)
{
splineContainer.Splines[i].Copy(spline);
}
//Creating more splines than exist in the container, add it as new.
else
{
splineContainer.AddSpline(spline);
}
}
//If the "count" is lower, the container may still contain more splines than desired (likely created before).
currentSplineCount = splineContainer.Splines.Count;
if (count < currentSplineCount) { //Delete any excess splines (loop backwards to avoid index shifting issues). for (int i = currentSplineCount - 1; i >= count; i--)
{
splineContainer.RemoveSplineAt(i);
}
}
}
}
//Not related to this example, but this'll simply call Rebuild when any value changes in the inspector.
//It's not possible to use OnValidate as a Spline Mesher will perform actions that are not allowed when called from OnValidate
#if UNITY_EDITOR
[CustomEditor(typeof(ProceduralSplines))]
public class ProceduralSplinesEditor : Editor
{
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
base.OnInspectorGUI();
if (EditorGUI.EndChangeCheck())
{
foreach (var m_target in targets)
((ProceduralSplines)m_target).GenerateSplines();
}
}
}
#endif
}
This technique allows Splines to be pooled in a way. This is also how all the spline generators in the Spline Extensions asset work.
