Estoy tratando de hacer que la articulación gire suavemente alrededor del centro del lienzo, hacia el ángulo del puntero del mouse. Lo que tengo funciona, pero quiero que anime la distancia más corta posible para llegar al ángulo del mouse. El problema ocurre cuando el valor gira alrededor de la línea horizontal ( 3.14
y -3.14
). Pase el mouse sobre esa área para ver cómo cambia la dirección y se tarda mucho en regresar.
Código relevante
// 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 );
¿Cómo puedo hacer que gire la distancia más corta, incluso "a través del espacio"?
mathematics
javascript
animation
lerp
easing
Jackrugile
fuente
fuente
Respuestas:
No siempre es el mejor método, y puede ser más costoso desde el punto de vista computacional (aunque esto depende en última instancia de cómo almacene sus datos), pero haré el argumento de que los valores bidimensionales funcionan razonablemente bien en la mayoría de los casos. En lugar de elevar un ángulo deseado, puede elevar el vector de dirección normalizado deseado .
Una ventaja de este método sobre el método de "elegir la ruta más corta al ángulo" es que funciona cuando necesita interpolar entre más de dos valores.
Al extraer valores de matiz, puede reemplazar
hue
con un[cos(hue), sin(hue)]
vector.En su caso, siguiendo la dirección común normalizada:
El código puede ser más corto si puede usar una clase de vector 2D. Por ejemplo:
fuente
dx /= len
...len ? len : 1.0
parte simplemente evita una división por cero, en el raro caso de que el mouse se coloque exactamente en la posición conjunta. Podría haber sido escrito:if (len != 0) dx /= len;
.0°
y180°
? En forma vectorial:[1, 0]
y[-1, 0]
. Vectores de interpolación le dará cualquiera0°
,180°
o una división por 0 errores, en caso det=0.5
.El truco es recordar que los ángulos (al menos en el espacio euclidiano) son periódicos por 2 * pi. Si la diferencia entre el ángulo actual y el ángulo objetivo es demasiado grande (es decir, el cursor ha cruzado el límite), simplemente ajuste el ángulo actual sumando o restando 2 * pi en consecuencia.
En este caso, puede intentar lo siguiente: (Nunca he programado en Javascript antes, así que perdone mi estilo de codificación).
EDITAR : en esta implementación, mover el cursor demasiado rápido alrededor del centro de la articulación hace que se mueva. Este es el comportamiento previsto, ya que la velocidad angular de la articulación es siempre proporcional a
dtheta
. Si este comportamiento no es deseado, el problema se puede solucionar fácilmente colocando una tapa en la aceleración angular de la articulación.Para hacer esto, necesitaremos hacer un seguimiento de la velocidad de la articulación e imponer una aceleración máxima:
Luego, para nuestra conveniencia, presentaremos una función de recorte:
Ahora, nuestro código de movimiento se ve así. Primero, calculamos
dtheta
como antes, ajustandojoint.angle
según sea necesario:Luego, en lugar de mover la articulación de inmediato, calculamos una velocidad objetivo y la usamos
clip
para forzarla dentro de nuestro rango aceptable.Esto produce un movimiento suave, incluso al cambiar de dirección, mientras se realizan cálculos en una sola dimensión. Además, permite que la velocidad y la aceleración de la articulación se ajusten independientemente. Ver demostración aquí: http://codepen.io/anon/pen/HGnDF/
fuente
dtheta
. ¿Pretendía que la articulación tuviera algo de impulso?Me encantan las otras respuestas dadas. Muy técnico!
Si quieres, tengo un método muy simple para lograr esto. Asumiremos ángulos para estos ejemplos. El concepto se puede extrapolar a otros tipos de valores, como los colores.
Acabo de crear esto en el navegador y nunca he sido probado. Espero tener la lógica correcta el primer intento.
[Editar] 2017/06/02 - Aclaró un poco la lógica.
Comience calculando distanceForward y distanceBackwards, y permita que los resultados se extiendan más allá del rango (0-360).
La normalización de ángulos devuelve esos valores al rango de (0-360). Para hacer esto, sumas 360 hasta que el valor esté por encima de cero y restas 360 mientras el valor está por encima de 360. Los ángulos de inicio / final resultantes serán equivalentes (-285 es lo mismo que 75).
A continuación, encontrará el ángulo normalizado más pequeño de distanceForward o distanceBackward. distanceForward en el ejemplo se convierte en 75, que es más pequeño que el valor normalizado de distanceBackward (300).
Si distanceForward es el AND final más pequeño <startAngle, extienda endAngle más allá de 360 agregando 360. (se convierte en 375 en el ejemplo).
Si distanceBackward es el AND final más pequeño> startAngle, extienda endAngle por debajo de 0 restando 360.
Ahora pasaría de startAngle (300) al nuevo endAngle (375). El motor debería ajustar automáticamente los valores por encima de 360 restando 360 por usted. De lo contrario, tendría que subir de 300 a 360, LUEGO subir de 0 a 15 si el motor no normaliza los valores por usted.
fuente
atan2
idea basada es sencilla, esta podría ahorrar algo de espacio y un poco de rendimiento (no es necesario almacenar x e y, ni calcular sin, cos y atan2 con demasiada frecuencia). Creo que vale la pena ampliar un poco sobre por qué esta solución es correcta (es decir, elegir el camino más corto en un círculo o esfera, al igual que SLERP hace para los quaterions). Esta pregunta y las respuestas deben incluirse en el wiki de la comunidad, ya que este es un problema muy común que enfrentan la mayoría de los programadores de juegos.