OK, j'ai déjà posté ça sur math.stackechange.com mais je n'ai pas eu de réponse :(
Voici d'abord une photo de mon problème, la description suit:
J'ai donc mis en place tous les points et toutes les valeurs.
Le vaisseau commence à se déplacer sur la planète gauche P1
avec un S=0.27 Degrees
gametick, quand il arrive, Point A
il commence à suivre la courbe de Bézier jusqu'à atteindre Point D
, puis il tourne autour de la bonne planète P2
avec un S=0.42 Degrees
tick par partie. La différence en S
est que le voyage avec la même vitesse de déplacement autour des planètes.
Jusqu'ici tout va bien, je l'ai mis en place, maintenant mon problème.
Quand S P1
et S P2
diffèrent à bien, le navire saute entre les deux vitesses quand il atteint sa destination, ce qui semble assez mauvais. J'ai donc besoin d'accélérer le navire entre Point A
et Point D
de S P1
à S P2
.
La chose qui me manque sont en violet, ce sont:
Une façon de calculer les tics qu'il faut au navire pour se déplacer le long de la couche de Bézier compte tenu de l'accélération.
Et un moyen de trouver une position sur la courbe de Bézier basée sur T, en considérant à nouveau l'accélération.
ATM Je calcule la longueur de la couche de Bézier en calculant la distance entre N
ses points. Donc, ce dont je pense avoir besoin, c’est un moyen de redimensionner celui que T
j’ai besoin d’inscrire dans le calcul de Bézier en fonction de l’accélération.
la source
Réponses:
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ézierVous 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:
Avec cela, on peut maintenant dessiner une courbe de Bézier en appelant
x
ety
avect
qui va de0 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érentsarc 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
surt
la courbe, de sorte que nosT 0.25
résultats dans let
qui est en25%
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'un
0.000001 pixel
solution parfaite.Alors , comment pouvons-nous cartographions
T
surt
? C'est simple et ne comporte que 3 étapes:Calculer des
N
points sur la courbe en utilisantt
et stocker learc-length
(autrement dit la longueur de la courbe) à cette position dans un tableauPour cartographier
T
surt
, d' abord multiplierT
par la longueur totale de la courbe pour obteniru
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 parN
et retourner.C'est tout! Alors maintenant, regardons le code complet:
Ceci initialise notre nouvelle courbe et calcule la
arg-lenghts
, il stocke également la dernière des longueurs commetotal length
la courbe, le facteur clé icithis.len
est le nôtreN
. 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, vous25
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 laT
carte établie àt
.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 petitetargetLength
, puis nous retournons ou faisons l’interpolation et le retour.Encore une fois, cela calcule
t
sur la courbe.Temps pour les résultats
En utilisant maintenant
mx
etmy
vous obtenez une distribution uniformeT
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 , à savoir
ut + 1/2at²
et(v - u) / t
Dans le code actuel, cela ressemblerait à ceci:
Ensuite, nous réduisons cela
0...1
en faisant: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 las
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:Alors et c'est tout! Merci d'avoir lu ;)
la source
Le problème est qu’un navire ne suivrait pas cette trajectoire naturellement. Donc, même si cela fonctionne parfaitement, cela ne semblera toujours pas correct.
Si vous voulez simuler la transition en douceur entre les planètes, je vous suggère de la modéliser. Les équations sont très simples car vous n’avez que deux forces importantes: la gravité et la poussée.
Il vous suffit de définir vos constantes: Masse de P1, P2, expédier
A chaque jeu (tick: time), vous faites 3 choses
Calculez la gravité de p1 sur le navire et de p2 sur le navire, ajoutez les vecteurs résultants au vecteur de poussée.
Calculez votre nouvelle vitesse en fonction de votre nouvelle accélération à partir de l'étape 1
Déplacez le navire en fonction de votre nouvelle vitesse
Cela peut sembler beaucoup de travail, mais cela peut être fait en une douzaine de lignes de code et sera très naturel.
Si vous avez besoin d'aide avec la physique faites le moi savoir.
la source
t
:)J'ai trouvé un excellent article expliquant une solution possible à ce problème avec un exemple de code écrit en javascript. Cela fonctionne en "poussant la valeur t" dans la bonne direction.
Cette question a déjà beaucoup de réponses intéressantes, mais j'ai trouvé cette solution intéressante à noter.
la source
Merci pour votre excellente page décrivant comment vous avez résolu ce problème. J'ai fait quelque chose d'un peu différent de ce que vous avez fait dans un détail, car j'étais extrêmement contraint par la mémoire: je ne construis pas de tableau, je ne dois pas chercher le bon 'segment' avec une recherche binaire. C’est parce que je sais toujours que je me déplace d’un bout à l’autre de ma courbe de Bézier: c’est pourquoi je me souviens simplement du segment "actuel", et si je vois que je vais sortir des limites de ce segment pour calculer mon prochain position, je calcule le segment suivant (ou précédent) (en fonction du sens de déplacement). Cela fonctionne assez bien pour mon application. Le seul petit problème que je devais résoudre était que, dans certaines courbes, la taille des segments était si petite que ma parcelle de terrain suivante était - à de rares occasions - plus d'un segment en avance sur le segment actuel, donc au lieu d'aller simplement à la '
Je ne sais pas si cela a du sens, mais cela m'a certainement aidé.
la source
Ce type de modélisation est étrange et peut produire d'étranges résultats illogiques. Surtout si la vitesse des planètes de départ est vraiment lente.
Modéliser les navires avec une puissance de poussée.
Lorsque les navires sont sur leur dernière orbite sur la planète de départ, accélérez à pleine vitesse.
Lorsque le vaisseau se trouve dans une certaine distance, utilisez l'inversion de poussée pour le ralentir jusqu'à la vitesse d'orbite de la planète cible.
Edit: Effectuez toute la simulation en même temps lorsqu'un nœud est sur le point de quitter l'orbite. soit envoyer toutes les données, soit envoyer seulement quelques vecteurs de mouvement à intervalles, et interpoler entre eux.
la source
Si je comprends bien, votre problème est surcontraint.
Je crois que vous voulez que le vaisseau spatial se déplace le long d'un chemin spécifié entre orbites dans un certain temps t et que vous souhaitiez également qu'il passe de la vitesse s1 à la vitesse s2 dans le même temps t . Malheureusement, vous ne pouvez pas (en général) trouver une accélération qui réponde simultanément à ces deux contraintes.
Vous allez devoir assouplir un peu votre problème pour le rendre résoluble.
la source
Je suis tombé sur cette réponse parce que je cherche à répartir les points uniformément le long d'un chemin svg utilisant une courbe de Bézier.
Bien que MDN dise qu'il est obsolète, vous pouvez utiliser le
path.getPointAtLength
pour obtenir le résultat correct. https://developer.mozilla.org/en-US/docs/Web/API/SVGPathElement/getPointAtLengthCela fonctionne actuellement dans Chrome / Safari / Firefox, et devrait fonctionner dans IE / Edge également, mais je n'ai pas vérifié ces 2.
la source
Le problème avec la solution acceptée
Comme Bézier est une fonction exponentielle , nous nous attendons à des taux d’avancement différents dans différentes zones de la courbe.
Étant donné que la solution d'Ivo interpole linéairement entre ces échantillons exponentiels initiaux , les imprécisions seront fortement biaisées vers les extrémités / milieu de la courbe (généralement cubique) où ces deltas sont les plus grands; ainsi, à moins que la fréquence d'échantillonnage
N
augmente considérablement, comme il le suggère, les erreurs sont évidentes et, à un certain niveau de zoom, le sera toujours pour une donnée donnéeN
, c'est-à-dire que le biais est intrinsèque à cet algorithme. Pas bon pour les graphiques vectoriels, par exemple, où le zoom peut être illimité.Contrer les biais par échantillonnage guidé
Une autre solution est de remappage linéaire
distance
pourt
après contrer la tendance naturelle que la fonction de Bézier produit.En supposant que c'est ce que nous souhaitons idéalement:
mais c’est ce que nous obtenons de la fonction de position de Bézier:
En examinant les
N
échantillons prélevés, nous pouvons voir où les deltas de distance sont les plus grands et rééchantillonner ("scinder") à mi-chemin entre les deux distances adjacentes, en augmentantN
de 1. Par exemple, en se scindant àt=0.9
(ce qui est à mi-chemin dans le plus grand delta), nous pourrions obtenir:Nous répétons ce processus pour le prochain intervalle de distance le plus grand jusqu'à ce que le delta maximal entre deux distances quelconques de l'ensemble soit inférieur à certaines
minDistanceDelta
, et plus précisément à uneepsilon
distance inférieure à des distances spécifiques que nous voulons mapper aux étapes suivantest
; nous pouvons ensuite mapper linéairement nost
étapes souhaitées auxdistance
s correspondants . Cela produit une hashtable / map à laquelle vous pouvez accéder à moindre coût et dont les valeurs peuvent être lerpées, à l'exécution, sans biais.Le
N
coût de répétition de cette opération augmente à mesure que l'ensemble augmente, il est donc préférable de le faire en tant que prétraitement. Chaque fois que vousN
augmentez, ajoutez les deux nouveaux intervalles résultants à uneintervals
collection tout en supprimant l'ancien intervalle unique qu'ils ont remplacé. C'est la structure sur laquelle vous travaillez pour trouver le prochain intervalle le plus grand à scinder en deux. Garder leintervals
tri en fonction de la distance facilite les choses, car vous pouvez simplement supprimer le prochain élément de travail, le fractionner, etc.Nous nous retrouvons avec quelque chose comme ce que nous voulions idéalement:
Étant donné que nous prenons des suppositions à chaque étape, nous n'obtiendrons pas les distances exactes
2
,4
etc., mais nous les souhaitions, mais elles sont rapprochées des valeurs de distance souhaitées de manière à ce que vous puissiez les cartographier.t
étapes avec une précision , éliminant ainsi les biais dus. échantillonnage quasi-équidistant.Vous pouvez ensuite récupérer, par exemple
t=0.5
, comme le fait Ivo dans sa réponse, c'est-à-dire en interpolant entre les deux valeurs les plus proches ci-dessus (3.9998132
et6.00703
).Conclusion
Dans la plupart des cas, la solution d’Ivo fonctionnera bien, mais dans les cas où il faut absolument éviter les biais, assurez-vous que vos
distance
s sont le plus uniformément dispersés possible, puis mappés linéairementt
.Notez que la division peut être effectuée de manière stochastique plutôt que fractionnée au milieu à chaque fois. Par exemple, nous pourrions avoir divisé le premier exemple d'intervalle à
t=0.827
plutôt qu'àt=0.9
.la source