Reputation: 190
i have an spline were i am able to move an object along the curve with variable speed but i want to move with constant speed how can i achieve this ?
public static class SplineCurve {
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;
}
public static Vector3 GetFirstDerivative (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) {
t = Mathf.Clamp01(t);
float oneMinusT = 1f - t;
return
3f * oneMinusT * oneMinusT * (p1 - p0) +
6f * oneMinusT * t * (p2 - p1) +
3f * t * t * (p3 - p2);
}
}
I am sending the constant parameter t to this curve and getting the point and then moving the object to that point, but this is giving me variable speed. I want to move my object with constant speed how can i achieve this ?
Is there any equation were i can solve for t for a specific distance ?
B(t) = (1 - t)^3 * P0 + 3 * (1 - t)^2 * t * P1 + 3 * (1 - t) * t^2* P2 + t^3* P3
I have used this equation
Upvotes: 1
Views: 6004
Reputation: 53666
What you're looking for is a reparameterisation of the Bezier curve, which is parameterised on time, for distance instead, so that rather than "curve point at time t
", you can compute "curve point at distance (or ratio) d
". This is actually a very hard problem, and it is by far the most efficient to realise you're working for "a computer screen": build a LUT (lookup table) of points where for each point you record the x/y(/z) coordinate, the time value, and the linear distance from the curve start that has been covered in this way, so that you can then move along the curve as drawn (rather than the curve as mathematically precise) based on that LUT with a simple function:
getCoordinate(d):
// get a known coordinate at, or before, distance 'd'
s = findBefore(d)
// get a known coordinate after distance 'd',
// or 'false' if 'd' is actually a real LUT point
e = findAfter(d)
// if it was a real LUT point, we're done.
if (e is false): return LUT[d]
// if it wasn't, perform linear interpolation
// between the known before/after coordinates,
// and treat that as the coordinate-for-distance.
return interpolate(d, e, s)
And as distance along the curve increases monotonically, you make your life easy by writing the LUT keyed on distance, so that LUT[0] is the start, LUT[len/2] is the curve half-way travelled, and LUT[len-1] is the end.
The one thing to make sure to bear in mind is that in order to use this properly you need to mark your curve as having two lengths: a true mathematical arc length based on something like a legendre-guass quadrature computation, and this LUT's approximated length, based on the sum of all linear distances between the coordinates you computed. You use the first for general purpose "telling the user the curve length", but you use the second for actually moving things along the curve.
(Because you are not moving things along the curve, you're moving things along a polygon-approximation of the curve, which 99.99% of the time is exactly what you want)
This code does not specify the findbefore
and findafter
functions, but there are too many ways to do this - a binary search (i = len, v = len >> 1
, check LUT[i-v]; higher than d
=> i -= v
, lower than d
=> i += v
, fix v through v >> 1
, repeat until v
becomes zero and you've found your boundary array indices.
Upvotes: 3
Reputation: 80287
Unfortunately, Bezier curve length cannot be expressed as closed formula (it is elliptic integral). But for moving you may neglect the precision and use some simple approximation.
Arbitrary found example - "What I did" part.
Using this approach, you can split a curve into small segments, get lengths for these segments and define speed of t changing at every segment.
Upvotes: 1