Vejo muitas vezes perguntas surgindo que têm esse problema subjacente, mas todas são capturadas nos detalhes de um determinado recurso ou ferramenta. Aqui está uma tentativa de criar uma resposta canônica à qual podemos referir os usuários quando isso surgir - com muitos exemplos animados! :)
Digamos que estamos fazendo uma câmera de primeira pessoa. A idéia básica é que você deve olhar para a esquerda e para a direita e inclinar para cima e para baixo. Então, escrevemos um pouco de código como este (usando o Unity como exemplo):
void Update() {
float speed = lookSpeed * Time.deltaTime;
// Yaw around the y axis using the player's horizontal input.
transform.Rotate(0f, Input.GetAxis("Horizontal") * speed, 0f);
// Pitch around the x axis using the player's vertical input.
transform.Rotate(-Input.GetAxis("Vertical") * speed, 0f, 0f);
}
ou talvez
// Construct a quaternion or a matrix representing incremental camera rotation.
Quaternion rotation = Quaternion.Euler(
-Input.GetAxis("Vertical") * speed,
Input.GetAxis("Horizontal") * speed,
0);
// Fold this change into the camera's current rotation.
transform.rotation *= rotation;
E geralmente funciona, mas com o tempo a visualização começa a ficar torta. A câmera parece estar girando em seu eixo de rotação (z), embora tenhamos dito apenas para girar nos x e y!
Isso também pode acontecer se estivermos tentando manipular um objeto na frente da câmera - digamos que é um globo que queremos girar para olhar ao redor:
O mesmo problema - depois de um tempo, o Pólo Norte começa a se afastar para a esquerda ou direita. Estamos dando entrada em dois eixos, mas estamos obtendo essa rotação confusa em um terceiro. E acontece se aplicamos todas as nossas rotações em torno dos eixos locais do objeto ou dos eixos globais do mundo.
Em muitos mecanismos, você também verá isso no inspetor - gire o objeto no mundo e, de repente, os números mudam em um eixo que nem tocamos!
Então, isso é um bug do motor?Como podemos dizer ao programa que não queremos que ele adicione rotação extra?
Tem algo a ver com ângulos de Euler? Em vez disso, devo usar quaterniões, matrizes de rotação ou vetores de base?
fonte
Respostas:
Não, isso não é um bug do mecanismo ou um artefato de uma representação de rotação específica (isso também pode acontecer, mas esse efeito se aplica a todos os sistemas que representam rotações, inclusive quaterniões).
Você descobriu um fato real sobre como a rotação funciona no espaço tridimensional e parte de nossa intuição sobre outras transformações, como a tradução:
Quando compomos rotações em mais de um eixo, o resultado obtido não é apenas o valor total / líquido que aplicamos a cada eixo (como seria de esperar para a tradução). A ordem em que aplicamos as rotações altera o resultado, à medida que cada rotação move os eixos nos quais as próximas rotações são aplicadas (se estiver girando sobre os eixos locais do objeto) ou a relação entre o objeto e o eixo (se estiver girando sobre o mundo) eixos).
A mudança das relações dos eixos ao longo do tempo pode confundir nossa intuição sobre o que cada eixo deve "fazer". Em particular, certas combinações de rotação de guinada e inclinação produzem o mesmo resultado que uma rotação de rolagem!
Você pode verificar se cada etapa está girando corretamente em torno do eixo que solicitamos - não há falhas ou artefatos no motor em nossa notação que interfiram ou adivinhem nossa entrada - a natureza esférica (ou hiperesférica / quaternária) da rotação significa apenas as transformações " ao redor "um sobre o outro. Eles podem ser ortogonais localmente, para pequenas rotações, mas à medida que se acumulam, descobrimos que não são ortogonais globalmente.
Isso é mais dramático e claro em curvas de 90 graus, como as anteriores, mas os eixos errantes também se arrastam por muitas pequenas rotações, como demonstrado na pergunta.
Então, o que fazemos sobre isso?
Se você já possui um sistema de rotação de inclinação da guinada, uma das maneiras mais rápidas de eliminar o rolo indesejado é alterar uma das rotações para operar nos eixos de transformação global ou pai, em vez dos eixos locais do objeto. Dessa forma, você não pode obter contaminação cruzada entre os dois - um eixo permanece absolutamente controlado.
Aqui está a mesma sequência de pitch-yaw-pitch que se tornou um exemplo no exemplo acima, mas agora aplicamos nossa guinada ao redor do eixo Y global em vez da do objeto
Assim, podemos consertar a câmera em primeira pessoa com o mantra "Pitch Locally, Yaw Globally":
Se você estiver compondo suas rotações usando a multiplicação, inverta a ordem esquerda / direita de uma das multiplicações para obter o mesmo efeito:
(A ordem específica dependerá das convenções de multiplicação em seu ambiente, mas esquerda = mais global / direita = mais local é uma escolha comum)
Isso equivale a armazenar a guinada total líquida e a inclinação total que você deseja como variáveis de flutuação, aplicando sempre o resultado líquido de uma só vez, construindo uma única nova orientação ou matriz de orientação a partir desses ângulos (desde que você mantenha-se
totalPitch
preso):ou equivalente...
Usando essa divisão global / local, as rotações não têm chance de se compor e influenciar uma à outra, porque são aplicadas a conjuntos independentes de eixos.
A mesma idéia pode ajudar se for um objeto no mundo que queremos rotacionar. Para um exemplo como o globo, muitas vezes queremos invertê-lo e aplicar nossa guinada localmente (para que ela sempre gire em torno de seus polos) e incline-se globalmente (para que se incline em direção à nossa visão, em vez de na Austrália. , onde quer que esteja apontando ...)
Limitações
Essa estratégia híbrida global / local nem sempre é a solução correta. Por exemplo, em um jogo com vôo / natação em 3D, convém apontar para cima / para baixo e ainda ter controle total. Mas com esta configuração, você apertará o bloqueio do cardan - seu eixo de guinada (ascendente global) se torna paralelo ao seu eixo de rolagem (local para a frente) e você não tem como olhar para a esquerda ou direita sem torcer.
O que você pode fazer em casos como esse é usar rotações locais puras, como começamos na pergunta acima (para que seus controles pareçam o mesmo, não importa para onde você esteja olhando), o que inicialmente permitirá que alguns movimentos entrem em ação - mas depois nós corrigimos isso.
Por exemplo, podemos usar rotações locais para atualizar nosso vetor "forward" e, em seguida, usar esse vetor forward junto com um vetor "up" de referência para construir nossa orientação final. (Usando, por exemplo, o Quaternion da Unity. LookRotation método , ou construindo manualmente uma matriz ortonormal a partir desses vetores). Controlando o vetor up, controlamos o roll ou twist.
Para o exemplo de voo / natação, convém aplicar essas correções gradualmente ao longo do tempo. Se for muito abrupto, a vista pode mudar de uma maneira perturbadora. Em vez disso, você pode usar o vetor atual do player e apontá-lo para a vertical, quadro a quadro, até que a visualização seja nivelada. Aplicar isso durante um turno às vezes pode ser menos nauseante do que girar a câmera enquanto os controles do player estão ociosos.
fonte
TCVector::calcRotationMatrix(totalPitch, totalYaw, identity)
- isso é equivalente ao exemplo "de uma só vez Euler" acima.APÓS 16 horas de funções parentais e rotação lol.
Eu só queria que o código tivesse um vazio, basicamente, girando em círculos e também virando a frente.
Ou, em outras palavras, o objetivo é girar a guinada e o arremesso sem nenhum efeito no rolamento.
Aqui estão os poucos que realmente funcionaram no meu aplicativo
Na unidade:
Agora, crie scripts no Visual Studio, faça sua instalação e termine com isso na atualização:
Observe que tudo quebrou para mim quando tentei variar a ordem dos pais. Ou, se eu mudar o Space.World para o objeto de pitch filho (o Yaw pai não se importa de ser girado pelo Space.Self). Confuso. Definitivamente, eu poderia usar alguns esclarecimentos sobre o motivo de ter que usar um eixo local, mas aplicá-lo no espaço do mundo - por que o Space.Self estraga tudo?
fonte
me.Rotate(me.right, angle, Space.World)
é o mesmo queme.Rotate(Vector3.right, angle, Space.Self
, e suas transformações aninhadas são equivalentes aos exemplos "Inclinar localmente, guiar globalmente" na resposta aceita.