Estou tentando fazer a junta girar suavemente em torno do centro da tela, em direção ao ângulo do ponteiro do mouse. O que tenho funciona, mas quero que ele anime a menor distância possível para chegar ao ângulo do mouse. O problema ocorre quando o valor circula na linha horizontal ( 3.14
e -3.14
). Passe o mouse nessa área para ver como a direção muda e leva o longo caminho de volta.
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 );
Como posso fazê-lo girar a menor distância, mesmo "atravessando a lacuna"?
mathematics
javascript
animation
lerp
easing
jackrugile
fonte
fonte
Respostas:
Nem sempre é o melhor método, e pode ser mais caro computacionalmente (embora isso dependa de como você armazena seus dados), mas argumentarei que a leitura de valores 2D funciona razoavelmente bem na maioria dos casos. Em vez de ler um ângulo desejado, você pode ler o vetor de direção normalizado desejado .
Uma vantagem desse método em relação ao método “escolher a rota mais curta para o ângulo” é que ele funciona quando você precisa interpolar entre mais de dois valores.
Ao ler valores de matiz, você pode substituir
hue
por um[cos(hue), sin(hue)]
vetor.No seu caso, lendo a direção da junta normalizada:
O código pode ser mais curto se você puder usar uma classe de vetores 2D. Por exemplo:
fonte
dx /= len
...len ? len : 1.0
peça evita uma divisão por zero, no raro caso em que o mouse é colocado exatamente na posição da articulação. Ele poderia ter sido escrito:if (len != 0) dx /= len;
.0°
e180°
? Em forma de vetor:[1, 0]
e[-1, 0]
. Vetores de interpolação vai lhe dar qualquer0°
,180°
ou uma divisão por 0 erro, no caso det=0.5
.O truque é lembrar que os ângulos (pelo menos no espaço euclidiano) são periódicos em 2 * pi. Se a diferença entre o ângulo atual e o ângulo alvo for muito grande (ou seja, o cursor cruzou o limite), basta ajustar o ângulo atual adicionando ou subtraindo 2 * pi de acordo.
Nesse caso, você pode tentar o seguinte: (nunca havia programado em Javascript antes, perdoe meu estilo de codificação.)
EDIT : Nesta implementação, mover o cursor muito rapidamente ao redor do centro da articulação faz com que ele se solte. Este é o comportamento pretendido, uma vez que a velocidade angular da articulação é sempre proporcional a
dtheta
. Se esse comportamento for indesejável, o problema pode ser facilmente resolvido colocando uma tampa na aceleração angular da articulação.Para fazer isso, precisamos acompanhar a velocidade da articulação e impor uma aceleração máxima:
Em seguida, para nossa conveniência, apresentaremos uma função de recorte:
Agora, nosso código de movimento se parece com isso. Primeiro, calculamos
dtheta
como antes, ajustandojoint.angle
conforme necessário:Em seguida, em vez de mover a articulação imediatamente, calculamos uma velocidade alvo e a usamos
clip
para forçá-la dentro de nossa faixa aceitável.Isso produz movimento suave, mesmo ao mudar de direção, enquanto realiza cálculos em apenas uma dimensão. Além disso, permite que a velocidade e a aceleração da junta sejam ajustadas independentemente. Veja a demonstração aqui: http://codepen.io/anon/pen/HGnDF/
fonte
dtheta
. Você pretendia que a junta tivesse algum impulso?Eu amo as outras respostas dadas. Muito técnico!
Se você quiser, eu tenho um método muito simples para fazer isso. Assumiremos ângulos para esses exemplos. O conceito pode ser extrapolado para outros tipos de valor, como cores.
Acabei de criar isso no navegador e nunca foi testado. Espero ter acertado a lógica na primeira tentativa.
02/06/2017 - Esclareceu um pouco a lógica.
Comece calculando distanceForward e distanceBackwards e permita que os resultados ultrapassem o intervalo (0-360).
A normalização de ângulos traz esses valores de volta ao intervalo de (0-360). Para fazer isso, você adiciona 360 até que o valor esteja acima de zero e subtrai 360 enquanto o valor está acima de 360. Os ângulos iniciais / finais resultantes serão equivalentes (-285 é igual a 75).
Em seguida, você encontrará o menor ângulo Normalizado de distanceForward ou distanceBackward. distanceForward no exemplo se torna 75, que é menor que o valor normalizado de distanceBackward (300).
Se distanceForward for o menor AND endAngle <startAngle, estenda endAngle além de 360 adicionando 360. (torna-se 375 no exemplo).
Se distanceBackward for o menor AND endAngle> startAngle, estenda endAngle para abaixo de 0 subtraindo 360.
Você agora leria de startAngle (300) para o novo endAngle (375). O mecanismo deve ajustar automaticamente os valores acima de 360 subtraindo 360 para você. Caso contrário, você teria que lerp de 300 a 360, ENTÃO lerp de 0 a 15 se o mecanismo não normalizar os valores para você.
fonte
atan2
ideia baseada seja direta, essa poderia economizar um pouco de espaço e um pouco de desempenho (não é necessário armazenar xey, nem calcular o pecado, cos e atan2 com muita frequência). Eu acho que vale a pena expandir um pouco o porquê dessa solução estar correta (ou seja, escolher o caminho mais curto em um círculo ou esfera, assim como o SLERP faz para perguntas). Esta pergunta e respostas devem ser colocadas no wiki da comunidade, pois esse é um problema muito comum que a maioria dos programadores de jogos enfrenta.