J'essaie de faire pivoter le joint en douceur autour du centre de la toile, vers l'angle du pointeur de la souris. Ce que j'ai fonctionne, mais je veux qu'il anime la distance la plus courte possible pour arriver à l'angle de la souris. Le problème se produit lorsque la valeur boucle sur la ligne horizontale ( 3.14
et -3.14
). Passez la souris sur cette zone pour voir comment la direction change et cela prend du temps.
Code pertinent
// ease the current angle to the target angle
joint.angle += ( joint.targetAngle - joint.angle ) * 0.1;
// get angle from joint to mouse
var dx = e.clientX - joint.x,
dy = e.clientY - joint.y;
joint.targetAngle = Math.atan2( dy, dx );
Comment puis-je le faire pivoter sur la distance la plus courte, même "à travers l'espace"?
mathematics
javascript
animation
lerp
easing
jackrugile
la source
la source
Réponses:
Ce n'est pas toujours la meilleure méthode, et elle peut être plus coûteuse en termes de calcul (bien que cela dépende en fin de compte de la façon dont vous stockez vos données), mais je ferai valoir que le lerping des valeurs 2D fonctionne assez bien dans la majorité des cas. Au lieu de lerper un angle souhaité, vous pouvez lerp le vecteur de direction normalisé souhaité .
Un avantage de cette méthode par rapport à la méthode «choisissez l'itinéraire le plus court jusqu'à l'angle» est qu'elle fonctionne lorsque vous devez interpoler entre plus de deux valeurs.
Lorsque vous lerpez des valeurs de teinte, vous pouvez les remplacer
hue
par un[cos(hue), sin(hue)]
vecteur.Dans votre cas, lerpant la direction articulaire normalisée:
Le code peut être plus court si vous pouvez utiliser une classe vectorielle 2D. Par exemple:
la source
dx /= len
...len ? len : 1.0
pièce évite juste une division par zéro, dans le cas rare où la souris est placée exactement à la position commune. Il aurait pu être écrit:if (len != 0) dx /= len;
.0°
et180°
? Sous forme vectorielle:[1, 0]
et[-1, 0]
. Vecteurs interpoler vous donnera soit0°
,180°
ou une division par 0 erreur, en cas det=0.5
.L'astuce consiste à se rappeler que les angles (au moins dans l'espace euclidien) sont périodiques de 2 * pi. Si la différence entre l'angle actuel et l'angle cible est trop grande (c'est-à-dire que le curseur a franchi la limite), ajustez simplement l'angle actuel en ajoutant ou en soustrayant 2 * pi en conséquence.
Dans ce cas, vous pouvez essayer ce qui suit: (Je n'ai jamais programmé en Javascript auparavant, alors pardonnez mon style de codage.)
EDIT : Dans cette implémentation, déplacer le curseur trop rapidement autour du centre de l'articulation le fait bouger. C'est le comportement recherché, car la vitesse angulaire du joint est toujours proportionnelle à
dtheta
. Si ce comportement n'est pas souhaité, le problème peut être facilement résolu en plaçant un capuchon sur l'accélération angulaire du joint.Pour ce faire, nous devons suivre la vitesse de l'articulation et imposer une accélération maximale:
Ensuite, pour notre commodité, nous allons introduire une fonction d'écrêtage:
Maintenant, notre code de mouvement ressemble à ceci. Tout d'abord, nous calculons
dtheta
comme précédemment, en ajustantjoint.angle
si nécessaire:Ensuite, au lieu de déplacer le joint immédiatement, nous calculons une vitesse cible et l'utilisons
clip
pour la forcer dans notre plage acceptable.Cela produit un mouvement fluide, même lors du changement de direction, tout en effectuant des calculs dans une seule dimension. De plus, il permet de régler indépendamment la vitesse et l'accélération du joint. Voir la démo ici: http://codepen.io/anon/pen/HGnDF/
la source
dtheta
. Souhaitiez-vous que l'articulation prenne un certain élan?J'adore les autres réponses données. Très technique!
Si vous le souhaitez, j'ai une méthode très simple pour y parvenir. Nous prendrons des angles pour ces exemples. Le concept peut être extrapolé à d'autres types de valeur, tels que les couleurs.
Je viens de le créer dans le navigateur et je n'ai jamais été testé. J'espère avoir compris la logique du premier coup.
[Modifier] 02/06/2017 - Clarifié un peu la logique.
Commencez par calculer distanceForward et distanceBackwards et laissez les résultats s'étendre au-delà de la plage (0-360).
La normalisation des angles ramène ces valeurs dans la plage (0-360). Pour ce faire, vous ajoutez 360 jusqu'à ce que la valeur soit supérieure à zéro et soustrayez 360 tandis que la valeur est supérieure à 360. Les angles de début / fin résultants seront équivalents (-285 est le même que 75).
Vous trouverez ensuite le plus petit angle normalisé de distanceForward ou distanceBackward. distanceForward dans l'exemple devient 75, ce qui est inférieur à la valeur normalisée de distanceBackward (300).
Si distanceForward est le plus petit ET endAngle <startAngle, étendez endAngle au-delà de 360 en ajoutant 360. (il devient 375 dans l'exemple).
Si distanceBackward est le plus petit ET endAngle> startAngle, étendez endAngle en dessous de 0 en soustrayant 360.
Vous devez maintenant passer de startAngle (300) au nouveau endAngle (375). Le moteur devrait ajuster automatiquement les valeurs supérieures à 360 en soustrayant 360 pour vous. Sinon, vous devrez lerp de 300 à 360, PUIS lerp de 0 à 15 si le moteur ne normalise pas les valeurs pour vous.
la source
atan2
idée de base soit simple, celle-ci pourrait économiser de l'espace et un peu de performance (pas besoin de stocker x et y, ni de calculer trop souvent sin, cos et atan2). Je pense qu'il vaut la peine d'étendre un peu la raison pour laquelle cette solution est correcte (c'est-à-dire choisir le chemin le plus court sur un cercle ou une sphère, tout comme SLERP le fait pour les quaterions). Cette question et les réponses devraient être placées dans le wiki de la communauté car c'est un problème très courant auquel la plupart des programmeurs de gameplay sont confrontés.