OK, eu já postei isso em math.stackechange.com, mas não obtive nenhuma resposta :(
Primeiro, aqui está uma foto do meu problema, a descrição a seguir:
Então, eu tenho todos os pontos e valores configurados.
A nave começa a se mover pelo planeta esquerdo P1
com um S=0.27 Degrees
clique de jogo, quando chega Point A
a seguir a curva de bezier até chegar Point D
, depois viaja pelo planeta certo P2
com o S=0.42 Degrees
tique de jogo. A diferença S
é que a viagem com o mesmo movimento acelera os planetas.
Até aí tudo bem, eu coloquei isso em funcionamento, agora meu problema.
Quando S P1
e S P2
diferem muito, o navio salta entre as duas velocidades quando atinge seu destino, o que parece muito ruim. Então, preciso acelerar o navio entre Point A
e Point D
de S P1
para S P2
.
O que sinto falta está em roxo, são eles:
Uma maneira de calcular os carrapatos que o navio leva para se mover ao longo do bezier, considerando a aceleração.
E uma maneira de encontrar uma posição na curva bezier com base em T, considerando novamente a aceleração.
ATM Calculo o comprimento do bezier calculando a distância entre N
seus pontos. Então, o que eu acho que preciso é uma maneira de escalar o que T
eu preciso colocar no meu cálculo de bezier de acordo com a aceleração.
fonte
Respostas:
OK, eu tenho tudo funcionando, demorou uma eternidade, então vou postar minha solução detalhada aqui.
Nota: Todas as amostras de código estão em JavaScript.
Então, vamos dividir o problema nas partes básicas:
Você precisa calcular o comprimento e os pontos entre
0..1
na curva de bezierAgora você precisa ajustar a escala do seu
T
para acelerar o navio de uma velocidade para outraComo acertar o Bezier
É fácil encontrar algum código para desenhar uma curva de Bezier, porém existem várias abordagens diferentes, uma delas é o algoritmo DeCasteljau , mas você também pode usar a equação para as curvas cúbicas de Bézier:
Com isso, agora é possível desenhar uma curva bezier chamando
x
ey
comt
quais intervalos0 to 1
, vamos dar uma olhada:Não é realmente uma distribuição uniforme dos pontos, é?
Devido à natureza da curva de Bézier, os pontos
0...1
têm diferentesarc lenghts
, portanto, os segmentos próximos ao início e ao final são mais longos do que os que estão próximos ao meio da curva.Mapeamento de T uniformemente na parametrização de comprimento de arco da curva AKA
Então o que fazer? Bem, em termos simples, precisamos de uma função para mapear o nosso
T
para ot
da curva, para que os nossosT 0.25
resultados not
que é pelo25%
do comprimento da curva.Como fazemos isso? Bem, nós pesquisamos no Google ... mas acontece que o termo não é tão googleable e, em algum momento, você acessará este PDF . O que com certeza é uma ótima leitura, mas no caso de você já ter esquecido todas as coisas de matemática que aprendeu na escola (ou simplesmente não gosta desses símbolos matemáticos), é bastante inútil.
E agora? Bem, vá ao Google um pouco mais (leia-se: 6 horas) e você finalmente encontrará um ótimo artigo sobre o assunto (incluindo fotos legais! ^ _ ^ "):
Http://www.planetclegg.com/projects/WarpingTextToSplines.html
Fazendo o código real
Caso você não consiga resistir ao download desses PDFs, embora já tenha perdido seu conhecimento matemático há muito, muito tempo (e você conseguiu pular o ótimo link do artigo), agora você pode pensar: "Deus, isso levará centenas de linhas de código e toneladas de CPU "
Não, não vai. Porque fazemos o que todos os programadores fazem, quando se trata de coisas matemáticas:
simplesmente trapaceamos.
Parametrização do comprimento do arco, a maneira preguiçosa
Vamos ser sinceros, não precisamos de precisão infinita em nosso jogo, precisamos? Portanto, a menos que você esteja trabalhando na Nasa e planejando enviar às pessoas o Marte, não precisará de uma
0.000001 pixel
solução perfeita.Então, como
T
mapeamost
? É simples e consiste apenas em 3 etapas:Calcule
N
pontos na curva usandot
e armazene oarc-length
(também conhecido como comprimento da curva) nessa posição em uma matrizPara mapear
T
parat
, em primeiro lugar multiplicarT
pelo comprimento total da curva para obteru
e então procurar o conjunto de comprimentos para o índice do maior valor que é menor do queu
Se tivermos um resultado exato, retorne o valor da matriz nesse índice dividido por
N
, se não interpolar um pouco entre o ponto encontrado e o próximo, divida a coisa novamenteN
e retorne.Isso é tudo! Então agora vamos dar uma olhada no código completo:
Isso inicializa nossa nova curva e calcula o
arg-lenghts
, ele também armazena o último dos comprimentos como ototal length
da curva, o principal fator aqui éthis.len
qual é o nossoN
. Quanto mais alto, mais preciso será o mapeamento, pois uma curva do tamanho da figura acima100 points
parece ser suficiente; se você precisar apenas de uma boa estimativa de comprimento, algo como25
já fará o trabalho com apenas um pixel de distância em nossa exemplo, mas você terá um mapeamento menos preciso que resultará em uma distribuição não uniforme deT
quando mapeado parat
.O código de mapeamento real, primeiro fazemos um simples
binary search
em nossos comprimentos armazenados para encontrar o maior comprimento menortargetLength
, depois retornamos ou fazemos a interpolação e o retorno.Novamente, isso calcula
t
na curva.Tempo para resultados
Agora, usando
mx
emy
você obtém uma distribuição uniformeT
na curva :)Não foi tão difícil, foi? Mais uma vez, acontece que uma solução simples (embora não perfeita) será suficiente para um jogo.
Caso você queira ver o código completo, há um Gist disponível:
https://gist.github.com/670236
Finalmente, acelerando os navios
Então, tudo o que resta agora é acelerar os navios ao longo de seu caminho, mapeando a posição em
T
que usamos para encontrar at
curva.Primeiro, precisamos de duas das equações de movimento , a saber
ut + 1/2at²
e(v - u) / t
No código real, seria assim:
Em seguida, reduzimos isso
0...1
fazendo:E lá vai você, os navios estão agora se movendo suavemente ao longo do caminho.
Caso isso não funcione ...
Quando você está lendo isso, tudo funciona bem e bem, mas inicialmente tive alguns problemas com a parte da aceleração. Ao explicar o problema para alguém na sala de bate-papo do gamedev, encontrei o erro final em meu pensamento.
Caso você ainda não tenha esquecido a imagem na pergunta original, mencionei
s
lá, as
velocidade é em graus , mas as naves se movem ao longo do caminho em pixels e eu esqueci esse fato. Então, o que eu precisava fazer nesse caso foi converter o deslocamento em graus em deslocamento em pixels, mas isso é bastante fácil:Então e isso é tudo! Obrigado pela leitura;)
fonte
A questão é que um navio não seguiria essa trajetória naturalmente. Portanto, mesmo com o funcionamento perfeito, ainda não parecerá correto.
Se você deseja simular a transição suave entre planetas, eu sugeriria realmente modelá-lo. As equações são muito simples, pois você tem apenas duas forças significativas: gravidade e impulso.
Você só precisa definir suas constantes: massa de P1, P2, navio
Com cada jogo-tick (time: t) você está fazendo 3 coisas
Calcule a gravidade de p1 no navio e p2 no navio, adicione os vetores resultantes ao vetor de empuxo.
Calcule sua nova velocidade com base na sua nova aceleração da etapa 1
Mova a nave de acordo com sua nova velocidade
Pode parecer muito trabalho, mas pode ser feito em uma dúzia de linhas de código e parecerá muito natural.
Se precisar de ajuda com a física, me avise.
fonte
t
:)Encontrei um excelente artigo explicando uma possível solução para esse problema com um exemplo de código escrito em javascript. Ele funciona "empurrando o valor t" na direção certa.
Essa pergunta já tem muitas respostas legais, mas achei que vale a pena notar essa solução.
fonte
Obrigado pela excelente página que descreve como você resolveu esse problema. Eu fiz algo um pouco diferente de você em um detalhe, pois estava com muita memória restrita: não construo uma matriz ou tenho que procurá-la pelo 'segmento' certo com uma pesquisa binária. Isso é porque eu sempre sei que estou passando de uma extremidade da minha curva de Bezier para outra: Portanto, simplesmente me lembro do segmento 'atual' e, se perceber que vou sair dos limites desse segmento para calcular minha próxima posição, eu calculo o próximo (ou anterior) segmento (com base na direção da viagem). Isso funciona muito bem para o meu aplicativo. A única falha que tive que resolver foi que, em algumas curvas, o tamanho dos segmentos era tão pequeno que minha próxima plotagem a apontar foi - em momentos raros - mais de um segmento à frente do atual, então, em vez de apenas ir ao '
Não sei se isso faz sentido, mas isso certamente me ajudou.
fonte
Esse tipo de modelagem é estranho e pode produzir resultados ilógicos estranhos. Especialmente se a velocidade inicial dos planetas for realmente lenta.
Modele os navios com um poder de empuxo.
Quando os navios estiverem em sua última órbita no planeta inicial, acelere com força total.
Quando a nave chegar a uma certa distância, use o impulso reverso para diminuir a velocidade da órbita do planeta alvo.
Editar: faça a simulação inteira de uma só vez quando um nó estiver prestes a sair da órbita. envie todos os dados ou envie apenas alguns vetores de movimento em intervalos e interpole entre eles.
fonte
Se eu entendi direito, seu problema está excessivamente restrito.
Acredito que você deseja que a nave espacial viaje ao longo de um caminho especificado entre as órbitas em algum tempo t e também que acelere da velocidade s1 para a velocidade s2 no mesmo tempo t . Infelizmente, você não pode (em geral) encontrar uma aceleração que satisfaça essas duas restrições simultaneamente.
Você precisará relaxar um pouco o seu problema para torná-lo solucionável.
fonte
Eu me deparei com essa resposta porque estou procurando distribuir pontos uniformemente ao longo de um caminho svg que usa uma curva mais bezier.
Apesar do MDN dizer que está obsoleto, você pode usar o
path.getPointAtLength
para obter o resultado correto. https://developer.mozilla.org/en-US/docs/Web/API/SVGPathElement/getPointAtLengthAtualmente, ele funciona no Chrome / Safari / Firefox e deve funcionar no IE / Edge também, mas não os verifiquei 2.
fonte
O problema com a solução aceita
Como Bezier é uma função exponencial , esperamos diferentes taxas de avanço em diferentes áreas da curva.
Como a solução da Ivo interpola linearmente entre esses exponenciais iniciais amostras , as imprecisões serão fortemente influenciadas pelas extremidades / meio da curva (geralmente cúbica) onde esses deltas são maiores; portanto, a menos que a taxa de amostragem
N
aumente bastante, como ele sugere, os erros são aparentes e, em algum nível de zoom, sempre serão aparentes para um dadoN
, ou seja, o viés é intrínseco a esse algoritmo. Não é bom para, por exemplo, gráficos baseados em vetores em que o zoom pode ser ilimitado.Viés de combate através de amostragem guiada
Uma solução alternativa é remapear linearmente
distance
parat
depois de combater o viés natural que a função Bezier produz.Supondo que é isso o que idealmente queremos:
mas é isso que obtemos da função de posição de Bezier:
Observando as
N
amostras coletadas, podemos ver onde os deltas de distância são maiores e reamostrar ("dividir") a meio caminho entre as duas distâncias adjacentes, aumentandoN
em 1. Por exemplo, dividindo emt=0.9
(que está no meio do delta maior), podemos pegue:Repetimos esse processo para o próximo maior intervalo de distância até que o delta máximo entre duas distâncias em todo o conjunto esteja abaixo de algumas
minDistanceDelta
e, mais especificamente, a menos do queepsilon
distância das distâncias específicas das quais queremos mapear as etapast
; podemos mapear linearmente o desejadot
passos paradistance
s correspondentes . Isso produz um hashtable / mapa que você pode acessar de forma barata e cujos valores você pode ler entre, em tempo de execução, sem preconceitos.À medida que o conjunto cresce
N
, o custo para repetir isso aumenta, então, idealmente, faça isso como um pré-processo. Cada vez queN
aumenta, adicione os dois novos intervalos resultantes a umaintervals
coleção enquanto remove o intervalo único antigo que eles substituíram. Essa é a estrutura na qual você trabalha para encontrar o próximo maior intervalo a ser dividido em dois. Manter aintervals
classificação por distância facilita as coisas, pois você pode simplesmente sair do próximo item de trabalho e dividir etc.Acabamos com algo parecido com o que idealmente queríamos:
Como adivinhamos a cada passo, não obteremos exatamente as distâncias exatas
2
,4
etc., desejávamos, mas, através da repetição da iteração, eles se aproximam o suficiente dos valores de distância desejados para que você possa mapear seust
passos com precisão razoável, eliminando o viés devido amostragem quase equidistante.Você pode recuperar
t=0.5
, por exemplo , como Ivo faz em sua resposta, ou seja, interpolando entre os dois valores mais próximos acima (3.9998132
e6.00703
).Conclusão
Na maioria dos casos, a solução do Ivo funcionará bem, mas nos casos em que o viés deve ser evitado a todo custo, verifique se seus
distance
s estão o mais dispersos possível e, em seguida, mapeados linearmente parat
.Observe que a divisão pode ser feita de forma estocástica, em vez de ser dividida no meio de cada vez, por exemplo, podemos dividir o intervalo do primeiro exemplo em
t=0.827
vez de emt=0.9
.fonte