using UnityEngine;
public class ZiplineSplineBuilder : MonoBehaviour
{
[SerializeField]
Transform startPoint;
[SerializeField]
Transform endPoint;
[SerializeField]
bool _autoBuildSpline = true;
[Range(2, 10)]
[SerializeField]
int resolution = 5; // Number of segments/points along the catenary
[Range(0.0f, 0.1f)]
public float slack = 0.03f;
SplineContainer _splineContainer;
private void Awake()
{
_splineContainer = GetComponent<SplineContainer>();
Build();
}
public void Build()
{
var spline = new Spline();
_splineContainer.Spline = spline;
Vector3 A = startPoint.position;
Vector3 B = endPoint.position;
Vector3 dir = B - A;
float length = dir.magnitude;
// --- Compute sag ---
float heightDiff = B.y - A.y;
float slope = Mathf.Clamp(heightDiff / length, -1f, 1f);
float sag = length * slack;
// Reduce sag for very steep lines (prevents weird shapes)
sag *= Mathf.Clamp01(1f - Mathf.Abs(slope));
// --- Generate multi-point catenary curve ---
int pointCount = Mathf.Max(2, resolution);
Vector3[] points = new Vector3[pointCount];
for (int i = 0; i < pointCount; i++)
{
float t = (float)i / (pointCount - 1);
Vector3 basePos = Vector3.Lerp(A, B, t);
// Parabolic sag curve weighted towards the middle (sin(pi * t) peaks at 1 in the center)
float parabolaFactor = Mathf.Sin(t * Mathf.PI);
// Bias sag slightly toward the lower endpoint
float bias = Mathf.Lerp(1f, (heightDiff > 0 ? 0.8f : 1.2f), Mathf.Abs(slope));
float localSag = sag * parabolaFactor * bias;
points[i] = basePos + Vector3.down * localSag;
}
// Force exact start and end positions to match transforms
points[0] = A;
points[pointCount - 1] = B;
// --- Build Bezier Knots with smooth tangents ---
for (int i = 0; i < pointCount; i++)
{
Vector3 currentPos = points[i];
Vector3 tanIn = Vector3.zero;
Vector3 tanOut = Vector3.zero;
if (i > 0 && i < pointCount - 1)
{
// Smooth interior tangents pointing towards neighbors
Vector3 prevPos = points[i - 1];
Vector3 nextPos = points[i + 1];
Vector3 tangentDir = (nextPos - prevPos) * 0.5f;
tanIn = -tangentDir * 0.5f;
tanOut = tangentDir * 0.5f;
}
else if (i == 0)
{
// Start tangent points smoothly toward the second point
tanOut = (points[1] - A) * 0.5f;
}
else if (i == pointCount - 1)
{
// End tangent points smoothly from the second-to-last point
tanIn = (B - points[pointCount - 2]) * -0.5f;
}
spline.Add(new BezierKnot(currentPos, tanIn, tanOut));
}
spline.Closed = false;
}
private void OnGUI()
{
if (GUILayout.Button("Rebuild Spline"))
{
if (_splineContainer == null)
_splineContainer = GetComponent<SplineContainer>();
if (startPoint != null && endPoint != null)
{
Debug.Log("Rebuilding spline");
Build();
}
}
}
#if UNITY_EDITOR
private void OnValidate()
{
if (!_autoBuildSpline)
return;
if (_splineContainer == null)
_splineContainer = GetComponent<SplineContainer>();
if (startPoint != null && endPoint != null)
{
Debug.Log("Rebuilding spline");
Build();
}
}
#endif
}
评论 (0)