Como impedir que a minha câmera FPS usando Quaternion se incline e atrapalhe?

17

Estou usando uma câmera tipo FPS e ela usa quaternions. Mas, sempre que tento olhar para cima e para os lados, ele se inclina e, às vezes, pode virar de cabeça para baixo. Como posso consertar isso?

Aeodyn
fonte
3
(Will Self-resposta em 7 horas) ...
Aeodyn

Respostas:

16

Você pode decompor seu quaternion em um conjunto de ângulos de guinada / inclinação / rotação, mas geralmente é um exagero.

Em vez de compor seus quaternions assim:

cameraOrientation = cameraOrientation * framePitch * frameYaw;

Tente o seguinte:

cameraOrientation = framePitch * cameraOrientation * frameYaw;

Nunca gerará inclinação / rotação e é equivalente a armazenar guinada e inclinação separadamente

ltjax
fonte
3
Este. Era exatamente isso que eu estava procurando. Resolvi meu problema de uma só vez. Agradável!
aardvarkk
1
São framePitche frameYaw floattipos? Também agradeceria alguns esclarecimentos sobre sua primeira frase.
Sirdank
1
@sirdank cameraOrientation, framePitche frameYawsão todos quatérnions (cada Quatérnion é 4 flutuadores ou duplos).
Dan
8

Esse é um problema que tive por um tempo e não consegui encontrar respostas para, então pensei em publicá-lo aqui.

Na verdade é bem simples. Como você provavelmente está fazendo as rotações é assim:

currentDirection * newRotation;

Mas, fazer assim também não funciona.

newRotation * currentDirection;

O que você precisa fazer é fazê-lo na primeira ordem para as rotações para cima e para baixo e na segunda ordem para as rotações laterais.

Para mim, foi assim:

        if (keyboard.IsKeyDown(Keys.Up))
            Direction = Direction * Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), TurnSpeed);
        if (keyboard.IsKeyDown(Keys.Down))
            Direction = Direction * Quaternion.CreateFromAxisAngle(new Vector3(-1, 0, 0), TurnSpeed);
        if (keyboard.IsKeyDown(Keys.Left))
            Direction = Quaternion.CreateFromAxisAngle(new Vector3(0, 0, 1), TurnSpeed) * Direction;
        if (keyboard.IsKeyDown(Keys.Right))
            Direction = Quaternion.CreateFromAxisAngle(new Vector3(0, 0, -1), TurnSpeed) * Direction;

Por um motivo, a primeira maneira tem a rotação em relação à direção lateral atual, que você deseja para cima e para baixo, mas você não deseja isso para as rotações laterais, e é por isso que a segunda ordem é necessária.

Aeodyn
fonte
Eu tive que esperar 7 horas para responder a minha própria ...
Aeodyn
desculpe, você pode explicar isso com algumas capturas de tela? "primeira ordem" e "segunda ordem" são um pouco confusas - obrigado!
Atav32
Ao trabalhar com Quaternions, a ordem importa, como sempre acontece com as rotações 3D. No meu exemplo, "Direction * = newRotation" é o mesmo que "Direction = Direction * newRotation". Mas, com as rotações esquerda e direita, queremos o contrário. Isso não depende da inclinação para cima e para baixo, o que causa o rolamento.
Aeodyn
Oh, eu acho que o ponto dos quartos de dólar era que ele evitava a trava do cardan e as rotações eram todas independentes, mas posso ter interpretado mal o livro. Então, só para esclarecer, você está dizendo que, se você reverter a ordem das transformações de rotação, poderá impedir que a câmera se incline? Ou você está definindo manualmente as transformações de rotação e inclinação como 0 quando você gira a guinada?
Atav32
5

Para uma câmera FPS, você geralmente não quer rolar e está limitado a +/- 90 graus de inclinação, então eu acompanharia o estado atual usando ângulos de guinada e inclinação. Todo o poder dos quaternions não é realmente útil para isso.

Você ainda pode converter os ângulos de guinada / pitch de e para quaternions, caso deseje fazer a transição entre a câmera FPS e as câmeras animadas usando a interpolação do quadro-chave de quaternion ou algo assim.

Nathan Reed
fonte
0

Outro truque simples é colocar a câmera em um GameObject e fazer com que a rotação de guinada controle o objeto do jogo, enquanto a câmera secundária estiver configurada com as coordenadas de afinação:

playerCameraHolder.transform.Rotate(0, rotationYaw, 0);
playerCamera.transform.Rotate(rotationPitch, 0, 0);
Victor S
fonte