The problem that I'm trying to solve is that I can't seem to move a 2D point along a cubic bezier curve at a constant speed.
I followed this tutorial: http://catlikecoding.com/unity/tutorials/curves-and-splines/ to implement the curve initially and that has worked wonderfully. But when trying to approximate the point at a constant speed it's way, way off.
From what I've read so far, you're supposed to iterate over the curve, calculating the arc length and interval distances at each step time. Then, compare those distances to a target distance (arc length * time) to find the closest distance. With a high enough resolution, this should have little error and be accurate enough for my needs.
Here is the code that I have so far:
Point calculation:
public static Vector3 GetPoint (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
t = Mathf.Clamp01(t);
float oneMinusT = 1f - t;
return
oneMinusT * oneMinusT * oneMinusT * p0 +
3f * oneMinusT * oneMinusT * t * p1 +
3f * oneMinusT * t * t * p2 +
t * t * t * p3;
}
Sad attempt at calculating the point at a constant time:
private float GetApproximatedTime(float u)
{
int resolution = 100;
float ratio = 1.0f / resolution;
float arcLength = 0.0f;
Vector3 p0 = SelectedSpline.Evaluate(0.0f);
List<MultiCurveUtility.ArcTimeLength> arcTimeLengthMap = new List<MultiCurveUtility.ArcTimeLength>();
arcTimeLengthMap.Add(new MultiCurveUtility.ArcTimeLength(0.0f, 0.0f));
for (int i = 1; i <= resolution; i++)
{
float t = ((float)i) * ratio;
Vector3 p1 = SelectedSpline.Evaluate(t);
arcLength += Vector3.Distance(p0, p1);
arcTimeLengthMap.Add(new MultiCurveUtility.ArcTimeLength(t, arcLength));
p0 = p1;
}
float target = u * arcLength;
int low = 0;
int high = 1;
float min = 0.0f;
float max = 0.0f;
for (int i = 1; i < arcTimeLengthMap.Count; i++)
{
max = arcTimeLengthMap[i].ArcLength;
if (target > min && target < max)
{
high = i;
low = i - 1;
break;
}
min = max;
}
float p = (target - min) / (max - min);
float lowTime = arcTimeLengthMap[low].ArcTime;
float highTime = arcTimeLengthMap[high].ArcTime;
float lowHighDelta = highTime - lowTime;
return arcTimeLengthMap[low].ArcTime + (lowHighDelta * p);
}
In the above code I'm passing in the time(u) between 0 and 1 in order to return a time that can be used to evaluate a point on the cubic bezier curve which represents x-axis movement at a constant rate.
The result is this: Cubic Bezier Image
The red dot represents the normal point that is returned by just evaluating the original time with the bezier formula. The yellow dot represents the 'constant' speed position after passing in time that has been approximated. It seems pretty accurate until I start changing the tangents to be fairly exaggerated. I've also tried increasing the intervals and that doesn't help anything.
Anyway, any help would be wonderful. I'm not terribly great at reading formulas yet (I'm sure where the issue is coming from), so it would be great to get some help using code examples please. :>
Thanks!