Możliwe jest przybliżenie rozwiązania tego problemu dla większości trajektorii parametrycznych. Pomysł jest następujący: jeśli powiększysz wystarczająco głęboko krzywą, nie możesz odróżnić samej krzywej od jej stycznej w tym punkcie.
Przyjmując to założenie, nie ma potrzeby wstępnego obliczania niczego więcej niż dwóch wektorów (trzy dla sześciennych krzywych Beziera itp. .).
Dla krzywej M(t) obliczamy jej wektor styczny dMdt w punkciet. Normą tego wektora jest∥dMdT∥a zatem odległość przebytą przez czasΔtmożna określić w przybliżeniu jako∥dMdT∥Δt. Wynika z tego, że przebywa się odległośćLna czasL÷∥dMdT∥.
Zastosowanie: kwadratowa krzywa Beziera
Jeśli punktami kontrolnymi krzywej Beziera są A , B i C , trajektorię można wyrazić jako:
M(t)=(1−t)2A+2t(1−t)B+t2C=t2(A−2B+C)+t(−2A+2B)+A
Więc pochodna to:
dMdt=t(2A−4B+2C)+(−2A+2B)
Musisz po prostu przechowywać wektory v⃗ 1=2A−4B+2C a v⃗ 2=−2A+2B gdzieś. Następnie, dla danego t , jeśli chcesz przejść o długość L , robisz:
t=t+Llength(t⋅v⃗ 1+v⃗ 2)
Sześcienne krzywe Beziera
To samo rozumowanie dotyczy krzywej z czterema punktami kontrolnymi A , B , C i D :
M(t)=(1−t)3A+3t(1−t)2B+3t2(1−t)C+t3D=t3(−A+3B−3C+D)+t2(3A−6B+3C)+t(−3A+3B)+A
Pochodna to:
dMdt=t2(−3A+9B−9C+3D)+t(6A−12B+6C)+(−3A+3B)
Wstępnie obliczamy trzy wektory:
v⃗ 1v⃗ 2v⃗ 3=−3A+9B−9C+3D=6A−12B+6C=−3A+3B
and the final formula is:
t=t+Llength(t2⋅v⃗ 1+t⋅v⃗ 2+v⃗ 3)
Accuracy issues
If you are running at a reasonable framerate, L (which should be computed according to the frame duration) will be sufficiently small for the approximation to work.
However, you may experience inaccuracies in extreme cases. If L is too large, you can do the computation piecewise, for instance using 10 parts:
for (int i = 0; i < 10; i++)
t = t + (L / 10) / length(t * v1 + v2);
L
is sufficiently small, this approximation is actually always more accurate than the answer below, yes. It also uses less memory (because it uses the derivative instead of storing all point values). WhenL
starts to grow, you can use the technique I suggest at the end.You need to reparamaterize the curve. The easiest way to do this is to calculate the arc lengths of several segments of the curve and use these to figure out where you should sample from. For example, maybe at t=0.5 (halfway through), you should pass s=0.7 to the curve to get the "halfway" position. You need to store a list of arc lengths of various curve segments to do this.
There are probably better ways, but here's some very simple C# code I wrote to do this in my game. It should be easy to port to Objective C:
Edit: It's worth noting that this won't give you the exact arc length, since it's impossible to get the arc length of a cubic curve. All this does is estimate the length of the various segments. Depending on how long the curve is, you may need to increase the resolution to prevent it from changing speeds when it reaches a new segment. I usually use ~100, which I have never had a problem with.
źródło
A very lightweight solution is to approximate the speed rather than approximating the curve. Actually this approach is independent of the curve function and enables you to use any exact curve instead of using derivatives or approximations.
Here is the code for C# Unity 3D:
Although the solution is independent of the curve function, I wanted to note it here as I was also looking for how to achieve constant speed on a Bezier curve, and then I come up with this solution. Considering the popularity of the function this may be helpful here.
źródło
I don't know anything about cocos2, but a bezier curve is a kind of parametric equation, so you should be able to get your x and y values in terms of time.
źródło