Giro mi personaje del juego para mirar al objetivo usando el siguiente código:
transform.rotation = Quaternion.Slerp(startQuaternion, lookQuaternion, turningNormalizer*turningSpeed/10f)
startQuaternion es la rotación actual del personaje cuando se le da un nuevo objetivo.
lookQuaternion es la dirección que debe mirar el personaje y está configurado de esta manera:
destinationVector = currentWaypoint.transform.position - transform.position;
lookQuaternion = Quaternion.LookRotation(destinationVector, Vector3.up);
turnNormalizer solo se Time.deltaTime
incrementa y turningSpeed
es un valor estático dado en el editor.
El problema es que mientras el personaje gira como debería la mayoría de las veces, tiene problemas cuando tiene que hacerlo cerca de 180 grados. Luego, a veces se agita y refleja la rotación:
En esta imagen mal dibujada, el personaje (a la derecha) comienza a girar hacia el círculo a la izquierda. En lugar de simplemente girar hacia la izquierda o hacia la derecha, comienza este "baile de espejo":
- Comienza a girar hacia el nuevo revestimiento.
- Luego, de repente, se ajusta al mismo ángulo pero en el otro lado y sigue girando
Hace este "reflejo" tanto tiempo hasta que mira al objetivo. ¿Es esto una cosa con cuaterniones, slerping / lerping u otra cosa?
EDITAR1: Aparentemente, el problema no surge de la rotación misma. El problema más probable es que el personaje se mueva hacia la cara mientras gira. Al limitar el ángulo, entre la orientación y el objetivo, cuando se permite que el personaje se mueva, se reduce y, a veces, se elimina la rotación de fluctuación / reflejo.
Por supuesto, esto plantea más preguntas, ¿por qué el personaje no puede moverse y rotar al mismo tiempo sin problemas? Código utilizado para el movimiento:
transform.Translate(Vector3.forward * runningSpeed/10f * Time.deltaTime);
Respuestas:
Cada orientación en el espacio 3D se puede representar mediante 2 cuaterniones de unidades distintas
q
y-q
(componentes negadosq
). Por ejemplo, la orientación representada por la matriz de identidad 3x3I
se puede representar por 2 cuaterniones:Ambos representan la misma orientación en el espacio 3D, su producto de punto es exactamente
-1
, cada uno de ellos en el otro hemisferio, exactamente en los lados opuestos de la hiperesfera.El resultado que observa cuando el slerp toma el arco más largo para rotar, es cuando se slerping entre 2 cuaterniones que no se encuentran en el mismo hemisferio. Si eso sucede, solo niega uno de ellos antes de hacerlos, entonces el slerp tomará el arco más corto.
El producto punto es una herramienta fácil para saber si eso sucede. Si el producto escalar de ambos cuaterniones es menor que
0
, entonces no se encuentran en el mismo hemisferio. Por lo tanto, si el producto escalar de ambos es menor que0
, solo por componentes, niegue el otro cuaternión antes de probarlo.fuente
if(Quaternion.Dot (lookQuaternion, startQuaternion) < 0) { startQuaternion = Quaternion.Inverse(startQuaternion); }
Pero no corrigió el problema. Lo siento por el mal formato, parece que no puedo domar esta sección de comentarios.Inverse
, esa es una operación diferente aNegate
.if(Quaternion.Dot (lookQuaternion, q) < 0) { q.x = -q.x; q.y = -q.y; q.z = -q.z; q.w = -q.w; }
No uso C #, pero por el aspecto del docu, parece que también podría usar la función Negate directamente.if(Quaternion.Dot (lookQuaternion, startQuaternion) < 0) { startQuaternion = Quaternion.Negate(startQuaternion); }
Bueno, su problema es porque esos dos vectores que forman sus cuaterniones hacen 180 grados, entonces la pregunta que debe hacerse al interpolar qué arco se supone que debe tomar. el arco superior o el inferior ??
Esto es lo que básicamente causa la duplicación, cada vez que está interpolando está tomando el revés mientras mantiene el ángulo.
De acuerdo a este enlace .
Lo que debe hacer es interpolar su cuaternión inicial con un cuaternión intermedio (para determinar qué arco tomar) y, cuando se encuentre, use el cuaternión intermedio para continuar al cuaternión objetivo real.
fuente
El problema que sospecho que está viendo no tiene nada que ver con su código, sino que es un problema fundamental con la interpolación a lo largo de la superficie de una esfera (en este caso, para viewDirection). Entre (casi) dos puntos en una esfera hay un gran círculo único; por ejemplo, al fijar arbitrariamente nuestro punto de "inicio" en el polo norte, el gran círculo sería el meridiano en el que se encuentra el punto final. La interpolación de un punto a otro se mueve a lo largo de este gran círculo.
Ahora, para la mayoría de los puntos en una esfera, los puntos cercanos corresponden a grandes círculos cercanos; por ejemplo, los meridianos en los que se encuentran Los Ángeles y Phoenix están bastante cerca uno del otro. Pero cuando el punto de destino es la antípoda del punto original, el polo sur al polo norte del punto de inicio, ya no hay un solo gran círculo a través de ambos, sino que todos los grandes círculos a través de uno pasan por el otro. Lo que es peor, esto significa que los puntos cerca del polo sur no son "la mayoría de los puntos"; dos puntos cerca uno del otro y ambos cerca del polo sur pueden tener meridianos muy divergentes.
Sospecho que lo que estás viendo es una manifestación de esta inestabilidad en el meridiano, o en otras palabras, el arco de interpolación. Cuando el objetivo de la mirada está directamente detrás del personaje, entonces cosas como la imprecisión de primer orden en su 'integración de Euler' de la posición del personaje, y la forma en que cambia la dirección de la mirada cuadro a cuadro, conducirán a estos cambios diferentes arcos de interpolación de un cuadro a otro, y esto probablemente conducirá al 'escalonamiento' que estás viendo.
En cuanto a qué hacer con él, lo mejor que se me ocurre es no volver a calcular la dirección de la mirada del "objetivo" en cada cuadro; en su lugar, use el mismo para un lapso de varios cuadros, luego calcule uno nuevo y use eso para varios cuadros, etc. Si es necesario, incluso puede interpolar de un objetivo al siguiente en el lapso de unos pocos cuadros para que La dirección del movimiento no cambia abruptamente.
fuente