OK, tout fonctionne, cela a pris une éternité, alors je vais poster ici ma solution détaillée.
Remarque: tous les exemples de code sont en JavaScript.
Alors divisons le problème en deux parties:
Vous devez calculer la longueur de, ainsi que les points entre 0..1
la courbe de Bézier
Vous devez maintenant ajuster l’échelle de votre T
pour accélérer le vaisseau d’une vitesse à l’autre.
Bien faire le Bézier
Il est facile de trouver du code pour dessiner une courbe de Bézier, mais il existe différentes approches. L'une d'entre elles est l' algorithme DeCasteljau , mais vous pouvez également utiliser l' équation pour les courbes de Bézier cubiques:
// Part of a class, a, b, c, d are the four control points of the curve
x: function (t) {
return ((1 - t) * (1 - t) * (1 - t)) * this.a.x
+ 3 * ((1 - t) * (1 - t)) * t * this.b.x
+ 3 * (1 - t) * (t * t) * this.c.x
+ (t * t * t) * this.d.x;
},
y: function (t) {
return ((1 - t) * (1 - t) * (1 - t)) * this.a.y
+ 3 * ((1 - t) * (1 - t)) * t * this.b.y
+ 3 * (1 - t) * (t * t) * this.c.y
+ (t * t * t) * this.d.y;
}
Avec cela, on peut maintenant dessiner une courbe de Bézier en appelant x
et y
avec t
qui va de 0 to 1
, jetons un coup d'oeil:
Euh ... ce n'est pas vraiment une répartition uniforme des points, n'est-ce pas?
En raison de la nature de la courbe de Bézier, les points 0...1
sont différents arc lenghts
, donc les segments proches du début et de la fin sont plus longs que ceux situés près du milieu de la courbe.
Cartographie de T uniformément sur la courbe AKA paramétrage longueur d'arc
Alors que faire? En termes simples, nous avons besoin d’une fonction pour mapper notre T
sur t
la courbe, de sorte que nos T 0.25
résultats dans let
qui est en 25%
de la longueur de la courbe.
Comment fait-on cela? Eh bien, nous Google ... mais il s’avère que le terme n’est pas acceptable , et à un moment donné, vous atteindrez ce fichier PDF . Ce qui est sûr, c'est une bonne lecture, mais dans le cas où vous avez déjà oublié tout ce que vous avez appris en mathématiques à l'école (ou que vous n'aimez pas ces symboles mathématiques), c'est plutôt inutile.
Et maintenant? Eh bien, allez un peu plus sur Google (lisez: 6 heures), et vous trouverez enfin un excellent article sur le sujet (y compris de belles images! ^ _ ^ "):
Http://www.planetclegg.com/projects/WarpingToTplines.html
Faire le code actuel
Au cas où vous ne pouviez pas résister à télécharger ce fichier PDF alors que vous aviez déjà perdu vos connaissances en mathématiques il y a très longtemps (et que vous avez réussi à ignorer la bonne lien de article), vous pourriez maintenant penser: "Mon Dieu, cela prendra des centaines de lignes de code et des tonnes de CPU "
Non, ça ne va pas. Parce que nous faisons ce que tous les programmeurs font en matière de maths:
nous trichons tout simplement.
Paramétrage de la longueur d'arc, paresseux
Voyons les choses en face, nous n'avons pas besoin d'une précision sans fin dans notre jeu, n'est-ce pas? Donc, sauf si vous travaillez à la Nasa et que vous envisagez d'envoyer des gens sur la Mars, vous n'aurez pas besoin d'un0.000001 pixel
solution parfaite.
Alors , comment pouvons-nous cartographions T
sur t
? C'est simple et ne comporte que 3 étapes:
Calculer des N
points sur la courbe en utilisant t
et stocker le arc-length
(autrement dit la longueur de la courbe) à cette position dans un tableau
Pour cartographier T
sur t
, d' abord multiplier T
par la longueur totale de la courbe pour obtenir u
et rechercher ensuite la gamme de longueurs pour l'indice de la plus grande valeur qui est plus petite queu
Si nous avons eu un résultat exact, renvoyer la valeur du tableau à cet index divisé par N
, sinon interpoler un bit entre le point trouvé et le suivant, diviser à nouveau l'élément par N
et retourner.
C'est tout! Alors maintenant, regardons le code complet:
function Bezier(a, b, c, d) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.len = 100;
this.arcLengths = new Array(this.len + 1);
this.arcLengths[0] = 0;
var ox = this.x(0), oy = this.y(0), clen = 0;
for(var i = 1; i <= this.len; i += 1) {
var x = this.x(i * 0.05), y = this.y(i * 0.05);
var dx = ox - x, dy = oy - y;
clen += Math.sqrt(dx * dx + dy * dy);
this.arcLengths[i] = clen;
ox = x, oy = y;
}
this.length = clen;
}
Ceci initialise notre nouvelle courbe et calcule la arg-lenghts
, il stocke également la dernière des longueurs comme total length
la courbe, le facteur clé ici this.len
est le nôtre N
. Plus la cartographie sera haute et précise, pour une courbe de la taille de l'image ci-dessus100 points
semble suffire, si vous avez simplement besoin d’une bonne estimation de la longueur, vous 25
obtiendrez peut-être déjà le résultat escompté. par exemple, mais vous aurez une cartographie précise moins ce qui entraînera pas une répartition uniforme de la T
carte établie à t
.
Bezier.prototype = {
map: function(u) {
var targetLength = u * this.arcLengths[this.len];
var low = 0, high = this.len, index = 0;
while (low < high) {
index = low + (((high - low) / 2) | 0);
if (this.arcLengths[index] < targetLength) {
low = index + 1;
} else {
high = index;
}
}
if (this.arcLengths[index] > targetLength) {
index--;
}
var lengthBefore = this.arcLengths[index];
if (lengthBefore === targetLength) {
return index / this.len;
} else {
return (index + (targetLength - lengthBefore) / (this.arcLengths[index + 1] - lengthBefore)) / this.len;
}
},
mx: function (u) {
return this.x(this.map(u));
},
my: function (u) {
return this.y(this.map(u));
},
Le code de mappage lui-même, d’abord nous faisons un simple binary search
sur nos longueurs stockées pour trouver la plus grande longueur qui est plus petite targetLength
, puis nous retournons ou faisons l’interpolation et le retour.
x: function (t) {
return ((1 - t) * (1 - t) * (1 - t)) * this.a.x
+ 3 * ((1 - t) * (1 - t)) * t * this.b.x
+ 3 * (1 - t) * (t * t) * this.c.x
+ (t * t * t) * this.d.x;
},
y: function (t) {
return ((1 - t) * (1 - t) * (1 - t)) * this.a.y
+ 3 * ((1 - t) * (1 - t)) * t * this.b.y
+ 3 * (1 - t) * (t * t) * this.c.y
+ (t * t * t) * this.d.y;
}
};
Encore une fois, cela calcule t
sur la courbe.
Temps pour les résultats
En utilisant maintenant mx
et my
vous obtenez une distribution uniforme T
sur la courbe :)
N'était-ce pas difficile? Une fois encore, il s'avère qu'une solution simple (bien que non parfaite) suffira pour un match.
Si vous voulez voir le code complet, un Gist est disponible:
https://gist.github.com/670236
Enfin, accélérer les navires
Il ne reste donc maintenant qu’à accélérer les navires tout au long de leur route, en cartographiant la position sur T
laquelle nous nous servirons ensuite pour trouver let
courbe.
Nous avons d’abord besoin de deux des équations du mouvement , à savoirut + 1/2at²
et(v - u) / t
Dans le code actuel, cela ressemblerait à ceci:
startSpeed = getStartingSpeedInPixels() // Note: pixels
endSpeed = getFinalSpeedInPixels() // Note: pixels
acceleration = (endSpeed - startSpeed) // since we scale to 0...1 we can leave out the division by 1 here
position = 0.5 * acceleration * t * t + startSpeed * t;
Ensuite, nous réduisons cela 0...1
en faisant:
maxPosition = 0.5 * acceleration + startSpeed;
newT = 1 / maxPosition * position;
Et voilà, les navires se déplacent maintenant sans à-coup le long du chemin.
Si ça ne marche pas ...
Quand vous lisez ceci, tout fonctionne bien et dandy, mais au début, j'avais quelques problèmes avec l'accélération. Lorsque j'ai expliqué le problème à quelqu'un dans la salle de discussion Gamedev, j'ai trouvé l'erreur finale dans ma pensée.
Au cas où vous n’auriez pas déjà oublié la photo dans la question initiale, je mentionne s
ici que la s
vitesse est en degrés , mais les navires se déplacent le long du chemin en pixels et j’avais oublié ce fait. Donc ce que je devais faire dans ce cas était de convertir le déplacement en degrés en un déplacement en pixels, cela s'avère être assez facile:
function rotationToMovement(planetSize, rotationSpeed) {
var r = shipAngle * Math.PI / 180;
var rr = (shipAngle + rotationSpeed) * Math.PI / 180;
var orbit = planetSize + shipOrbit;
var dx = Math.cos(r) * orbit - Math.cos(rr) * orbit;
var dy = Math.sin(r) * orbit - Math.sin(rr) * orbit;
return Math.sqrt(dx * dx + dy * dy);
};
Alors et c'est tout! Merci d'avoir lu ;)