using UnityEngine;
|
|
using System;
|
|
|
|
public class BezierSpline : MonoBehaviour {
|
|
|
|
[SerializeField]
|
|
private Vector3[] points;
|
|
|
|
[SerializeField]
|
|
private BezierControlPointMode[] modes;
|
|
|
|
[SerializeField]
|
|
private bool loop;
|
|
|
|
public bool Loop {
|
|
get {
|
|
return loop;
|
|
}
|
|
set {
|
|
loop = value;
|
|
if (value == true) {
|
|
modes[modes.Length - 1] = modes[0];
|
|
SetControlPoint(0, points[0]);
|
|
}
|
|
}
|
|
}
|
|
|
|
public int ControlPointCount {
|
|
get {
|
|
return points.Length;
|
|
}
|
|
}
|
|
|
|
public Vector3 GetControlPoint (int index) {
|
|
return points[index];
|
|
}
|
|
|
|
public void SetControlPoint (int index, Vector3 point) {
|
|
if (index % 3 == 0) {
|
|
Vector3 delta = point - points[index];
|
|
if (loop) {
|
|
if (index == 0) {
|
|
points[1] += delta;
|
|
points[points.Length - 2] += delta;
|
|
points[points.Length - 1] = point;
|
|
}
|
|
else if (index == points.Length - 1) {
|
|
points[0] = point;
|
|
points[1] += delta;
|
|
points[index - 1] += delta;
|
|
}
|
|
else {
|
|
points[index - 1] += delta;
|
|
points[index + 1] += delta;
|
|
}
|
|
}
|
|
else {
|
|
if (index > 0) {
|
|
points[index - 1] += delta;
|
|
}
|
|
if (index + 1 < points.Length) {
|
|
points[index + 1] += delta;
|
|
}
|
|
}
|
|
}
|
|
points[index] = point;
|
|
EnforceMode(index);
|
|
}
|
|
|
|
public BezierControlPointMode GetControlPointMode (int index) {
|
|
return modes[(index + 1) / 3];
|
|
}
|
|
|
|
public void SetControlPointMode (int index, BezierControlPointMode mode) {
|
|
int modeIndex = (index + 1) / 3;
|
|
modes[modeIndex] = mode;
|
|
if (loop) {
|
|
if (modeIndex == 0) {
|
|
modes[modes.Length - 1] = mode;
|
|
}
|
|
else if (modeIndex == modes.Length - 1) {
|
|
modes[0] = mode;
|
|
}
|
|
}
|
|
EnforceMode(index);
|
|
}
|
|
|
|
private void EnforceMode (int index) {
|
|
int modeIndex = (index + 1) / 3;
|
|
BezierControlPointMode mode = modes[modeIndex];
|
|
if (mode == BezierControlPointMode.Free || !loop && (modeIndex == 0 || modeIndex == modes.Length - 1)) {
|
|
return;
|
|
}
|
|
|
|
int middleIndex = modeIndex * 3;
|
|
int fixedIndex, enforcedIndex;
|
|
if (index <= middleIndex) {
|
|
fixedIndex = middleIndex - 1;
|
|
if (fixedIndex < 0) {
|
|
fixedIndex = points.Length - 2;
|
|
}
|
|
enforcedIndex = middleIndex + 1;
|
|
if (enforcedIndex >= points.Length) {
|
|
enforcedIndex = 1;
|
|
}
|
|
}
|
|
else {
|
|
fixedIndex = middleIndex + 1;
|
|
if (fixedIndex >= points.Length) {
|
|
fixedIndex = 1;
|
|
}
|
|
enforcedIndex = middleIndex - 1;
|
|
if (enforcedIndex < 0) {
|
|
enforcedIndex = points.Length - 2;
|
|
}
|
|
}
|
|
|
|
Vector3 middle = points[middleIndex];
|
|
Vector3 enforcedTangent = middle - points[fixedIndex];
|
|
if (mode == BezierControlPointMode.Aligned) {
|
|
enforcedTangent = enforcedTangent.normalized * Vector3.Distance(middle, points[enforcedIndex]);
|
|
}
|
|
points[enforcedIndex] = middle + enforcedTangent;
|
|
}
|
|
|
|
public int CurveCount {
|
|
get {
|
|
return (points.Length - 1) / 3;
|
|
}
|
|
}
|
|
|
|
public Vector3 GetPoint (float t) {
|
|
int i;
|
|
if (t >= 1f) {
|
|
t = 1f;
|
|
i = points.Length - 4;
|
|
}
|
|
else {
|
|
t = Mathf.Clamp01(t) * CurveCount;
|
|
i = (int)t;
|
|
t -= i;
|
|
i *= 3;
|
|
}
|
|
return transform.TransformPoint(Bezier.GetPoint(points[i], points[i + 1], points[i + 2], points[i + 3], t));
|
|
}
|
|
|
|
public Vector3 GetVelocity (float t) {
|
|
int i;
|
|
if (t >= 1f) {
|
|
t = 1f;
|
|
i = points.Length - 4;
|
|
}
|
|
else {
|
|
t = Mathf.Clamp01(t) * CurveCount;
|
|
i = (int)t;
|
|
t -= i;
|
|
i *= 3;
|
|
}
|
|
return transform.TransformPoint(Bezier.GetFirstDerivative(points[i], points[i + 1], points[i + 2], points[i + 3], t)) - transform.position;
|
|
}
|
|
|
|
public Vector3 GetDirection (float t) {
|
|
return GetVelocity(t).normalized;
|
|
}
|
|
|
|
public void AddCurve () {
|
|
Vector3 point = points[points.Length - 1];
|
|
Array.Resize(ref points, points.Length + 3);
|
|
point.x += 1f;
|
|
points[points.Length - 3] = point;
|
|
point.x += 1f;
|
|
points[points.Length - 2] = point;
|
|
point.x += 1f;
|
|
points[points.Length - 1] = point;
|
|
|
|
Array.Resize(ref modes, modes.Length + 1);
|
|
modes[modes.Length - 1] = modes[modes.Length - 2];
|
|
EnforceMode(points.Length - 4);
|
|
|
|
if (loop) {
|
|
points[points.Length - 1] = points[0];
|
|
modes[modes.Length - 1] = modes[0];
|
|
EnforceMode(0);
|
|
}
|
|
}
|
|
|
|
public void Reset () {
|
|
points = new Vector3[] {
|
|
new Vector3(1f, 0f, 0f),
|
|
new Vector3(2f, 0f, 0f),
|
|
new Vector3(3f, 0f, 0f),
|
|
new Vector3(4f, 0f, 0f)
|
|
};
|
|
modes = new BezierControlPointMode[] {
|
|
BezierControlPointMode.Free,
|
|
BezierControlPointMode.Free
|
|
};
|
|
}
|
|
}
|