Como liderar um alvo em movimento de um atirador em movimento

7

Vi a seguinte pergunta: prever a posição do inimigo para que um objeto lidere seu alvo . Minha situação é um pouco diferente.

Meu alvo se move e o atirador se move. Além disso, a velocidade do atirador é adicionada às velocidades das balas, ou seja, as balas disparadas ao deslizar para a direita terão uma velocidade maior na direção da direita.

O que estou tentando fazer é conseguir que o inimigo seja capaz de determinar onde ele precisa atirar para atingir o jogador. Usando a solução SO vinculada, a menos que o jogador e o inimigo estejam imóveis, a diferença de velocidade causará um erro. Como posso evitar isso?


Aqui está a solução apresentada na resposta de estouro de pilha. Tudo se resume a resolver uma equação quadrática da forma:

a * sqr(x) + b * x + c == 0

Note que com sqreu quero dizer quadrado, ao contrário de raiz quadrada. Use os seguintes valores:

a := sqr(target.velocityX) + sqr(target.velocityY) - sqr(projectile_speed)
b := 2 * (target.velocityX * (target.startX - cannon.X)
      + target.velocityY * (target.startY - cannon.Y))
c := sqr(target.startX - cannon.X) + sqr(target.startY - cannon.Y)

Agora podemos analisar o discriminante para determinar se temos uma solução possível.

disc := sqr(b) - 4 * a * c

Se o discriminante for menor que 0, esqueça de atingir seu alvo - seu projétil nunca poderá chegar lá a tempo. Caso contrário, observe duas soluções candidatas:

t1 := (-b + sqrt(disc)) / (2 * a)
t2 := (-b - sqrt(disc)) / (2 * a)

Note que se o disco == 0 então t1 e t2 são iguais.

Azaral
fonte
3
você pode, por favor, tentar condensar sua pergunta. Eu sei que você acha que precisa para dar explicações muito detalhadas, mas realmente eu tenho sobre a meio, e senti que eu estava lendo um livro de texto
gardian06
11
Você pode usar algo como em sq()vez de sqr()? Isso torna muito confuso ler.
sam hocevar
@ gardian06 O código sem nenhuma explicação envolvida está no final da pergunta.
Azaral
@ Sam Hocevar Substituí toda a minha variável * variável por std :: pow (variável 2).
Azaral
Se funcionar quando você estiver parado e o alvo se mover, mas não quando os dois se moverem, subtraia sua velocidade da velocidade do alvo para obter a velocidade relativa e use-a.
George Duckett

Respostas:

5

Ok, vamos colocar um pouco de sanidade nisso. Receio que você não esteja facilitando nada, seu código não é compilado, é inconsistente com relação aos nomes de variáveis ​​( playerVelocityXfica playerXvelocityapós algumas linhas? O que é xVelocity?) E é muito detalhado. É basicamente impossível depurar para que você não faça um esforço considerável.

Então, aqui estão as coisas para corrigir:

Velocidade da bala

A velocidade da bala deve ser 30, ponto final. Não há necessidade dos cálculos que você está fazendo: a mudança do quadro de referência está precisamente lá para evitar a complexidade. Você só adiciona a velocidade do inimigo depois de encontrar uma solução quando volta ao quadro de referência principal.

Validade da solução

Você não está verificando se a timesolução é positiva.

Inúmeros erros de codificação

Você está testando time1e time2sempre usando time1nos resultados.

Você faz o playerXvelocity - yVelocityque é inconsistente.

Você está fazendo em / 2 * avez de / (2.f * a). Este é o pior erro e é por isso que tudo está dando errado.

Você calcula shootxe shootycomo a posição final da bala, enquanto o que você está procurando é a velocidade da bala.

Código fixo

float const bulletSpeed = 30.f;
/* Relative player position */
float const dx = playerX - enemyX;
float const dy = playerY - enemyY;
/* Relative player velocity */
float const vx = playerVelocityX - enemyVelocityX;
float const vy = playerVelocityY - enemyVelocityY;

float const a = vx * vx + vy * vy - bulletSpeed * bulletSpeed;
float const b = 2.f * (vx * dx + vy * dy);
float const c = dx * dx + dy * dy;
float const disc = b * b - 4.f * a * c;

shouldShoot = false;

if (disc >= 0.f)
{
    float t0 = (-b - std::sqrt(disc)) / (2.f * a);
    float t1 = (-b + std::sqrt(disc)) / (2.f * a);
    /* If t0 is negative, or t1 is a better solution, use t1 */
    if (t0 < 0.f || (t1 < t0 && t1 >= 0.f))
        t0 = t1;
    if (t0 >= 0.f)
    {
        /* Compute the ship's heading */
        shootx = vx + dx / t0;
        shooty = vy + dy / t0;
        heading = std::atan2(shooty, shootx) * RAD2DEGREE;
        /* Compute the bullet's velocity by adding the enemy's velocity */
        bulletVelocityX = shootx + enemyVelocityX;
        bulletVelocityY = shooty + enemyVelocityY;

        shouldShoot = true;
    }
}
sam hocevar
fonte
Sim, redigitei meu código e notei que errei algumas coisas. Não é tão inconsistente no meu código atual que prometo lol! Eu notei o "Você está fazendo / 2 * a em vez de / (2.f * a)". ontem à noite e consertou isso. Vou tentar o seu código. Eu vim aqui após cerca de quatro horas tentando descobrir isso e estava em um estado bastante irritado e acho que foi por isso que digitei o código tão horrivelmente.
Azaral
Eu tentei isso em uma saída simples do console e não está funcionando. Copio e colei em um novo projeto. Eu adicionei as variáveis ​​de jogador e inimigo que não são declaradas no seu código. Dentro da área if (t0> = 0.f), adicionei o jogador de cálculo x, ye bala x, y após a linha disparar = true; playerX += playerVelocityX*t0; playerY += playerVelocityY*t0; float bulletX = ((cos(heading * DEGREE2RAD)*bulletSpeed + enemyVelocityX) * t0) + enemyX; float bulletY = ((sin(heading * DEGREE2RAD) * bulletSpeed + enemyVelocityY) * t0) + enemyY; A saída é P = (84.8069,93.0386) E = (88.3793.105.132)
Azaral
Neste ponto, eu não ficaria surpreso se eu fosse apenas um idiota. Eu posso aceitar isso.
Azaral
No exemplo acima, os dados originais são: playerX = 50, playerY = 100, playerVelocityX = 20, playerVelocityY = -4, inimigoX = 145, inimigoY = 67, enemyVelocityX = -5, enemyVelocityY = 10.
Azaral
Não use bulletSpeedneste momento. Substitua cos(heading * DEGREE2RAD) * bulletSpeedpor shootx. O mesmo para shooty. O motivo é que a velocidade do inimigo é adicionada à velocidade da bala, sua velocidade não é mais exatamente bulletSpeed. Na verdade, você não precisa da headingvariável, apenas ajuda na depuração.
sam hocevar
2

Ter um atirador em movimento é idêntico a ter um atirador parado. Subtraia simplesmente o vetor de movimento dos atiradores do vetor de movimento dos alvos.

Target [-5,0]
Shooter [4,1]
Target - Shooter = [-5,0] - [4,1] = [-9,-1]

Calcule o vetor de tiro / ângulo inicial e adicione o vetor de movimento dos alvos ao marcador normalmente.

Daniel Carlsson
fonte
Se você leu minha pergunta e olhou para o código, verá que já estou fazendo isso.
Azaral
@azaral sim e não. você está fazendo isso para obter um de seus valores, e em seguida, fazendo coisas com velocidade
gardian06
@ gardian06 você pode ser mais específico?
Azaral
@Azaral: Fiquei com a impressão de que você tinha um algoritmo de trabalho para quando o atirador estava parado, conforme descrito no link que você forneceu. Se não o seu trabalho, nesse caso, há um bug em que o algoritmo que as necessidades de classificação antes que você precisa se preocupar com um jogo de tiro em movimento (não que a diferença é grande, mas mais simples é mais fácil de depurar)
Daniel Carlsson
@DanielCarlsson, você sabe que esse é um bom ponto em que nunca pensei. Apenas assumi que o algoritmo funcionava.
Azaral
1

Aqui está um exemplo em que eu projetei e implementei uma solução para o problema da segmentação preditiva usando um algoritmo recursivo: http://www.newarteest.com/flash/targeting.html (eu tinha um atirador estacionário, mas a mesma abordagem funcionaria para um atirador em movimento)

Vou ter que experimentar algumas das outras soluções apresentadas, porque parece mais eficiente calculá-lo em uma única etapa, mas a solução que surgiu foi estimar a posição de destino e alimentar o resultado de volta no algoritmo para criar um novo estimativa mais precisa, repetindo várias vezes.

Para a primeira estimativa, eu "atiro" na posição atual do alvo e depois uso a trigonometria para determinar onde o alvo estará quando o tiro atingir a posição disparada. Então, na próxima iteração, eu "disparo" nessa nova posição e determino onde o alvo estará neste momento. Após cerca de 4 repetições, chego a um pixel de precisão.

jhocking
fonte
11
Uma vantagem desta resposta é que, mesmo que não exista uma solução exata, o atirador ainda dispara em torno da posição do jogador. +1 para isso.
sam hocevar
huh Eu nem tinha pensado nisso, bom ponto
jhocking
0

Como você está trabalhando apenas com física 2D (sem velocidade Z), esse problema pode ser bastante simplificado. A maneira mais fácil de fazer isso é parar de pensar nos movimentos de origem e destino em relação às coordenadas do mundo e apenas pensar no destino em movimento em relação à fonte (e manter a fonte estacionária).

Vector TargetInitialPosition = new Vector ( target.X - source.X ,
                                            target.Y - source.Y );
Vector TargetApparentVelocity = new Vector( target.velocityX - source.velocityX ,
                                            target.velocityY - source.velocityY );

Normalmente, a velocidade de uma bala seria muito maior que a velocidade do atirador, portanto, geralmente é assumido que a bala é independente, mas há ocasiões em que isso não é verdade, como disparar de um helicóptero ou avião de combate.

Então precisamos calcular a velocidade da bala:

// Your directional vector MUST be normalized...
Vector BulletVelocity = new Vector( source.directionX * Bullet::StaticSpeed + source.velocityX ,
                                     source.directionY * Bullet::StaticSpeed + source.velocityY );

O problema que você está enfrentando é que o alvo se move quando a bala os atinge.

TargetPosition = TargetInitialPosition + TargetApparentVelocity * t
BulletPosition = BulletInitialPosition + BulletVelocity * t
               = BulletVelocity * t

e resolva o TargetPosition == BulletPosition porque o marcador atingirá o alvo. Agora você tem três incógnitas e apenas duas equações. Podemos remover 't' usando a derivada de primeira ordem:

TargetInitialPosition + ( TargetApparentVelocity - BulletVelocity ) * t == 0
dV / dt = TargetApparentVelocity - BulletVelocity

Agora, para atingir o alvo, você desejaria dV/dt == -TargetInitialPosition * k. A constante deve ser a mesma nas coordenadas X e Y e é o número de segundos que o marcador levará para atingir o alvo.

TargetApparentVelocity.X - BulletVelocity.X == k * -TargetInitialPosition.X
k = ( BulletVelocity.X - TargetApparentVelocity.X ) / TargetInitialPosition.X
----------------------
TargetApparentVelocity.Y - BulletVelocity.Y == k * -TargetInitialPosition.Y
k = ( BulletVelocity.Y - TargetApparentVelocity.Y ) / TargetInitialPosition.Y

torná-los iguais:

( BulletVelocity.X - TargetApparentVelocity.X ) / TargetInitialPosition.X
= ( BulletVelocity.Y - TargetApparentVelocity.Y ) / TargetInitialPosition.Y

ou para expandir as variáveis:

( source.directionX * Bullet::StaticSpeed + source.velocityX - target.velocityX + source.velocityX ) / ( target.X - source.X )
 == ( source.directionY * Bullet::StaticSpeed + source.velocityY - target.velocityY + source.velocityY ) / ( target.Y - source.Y )

Então a álgebra fornece sua equação final:

source.directionY = ( target.velocityY * ( source.X - target.X ) - 2 * source.velocityY * ( source.X - target.X ) + ( Bullet::Speed * source.directionX + 2 * source.velocityX - target.velocityX ) * ( source.Y - target.Y ) ) / ( Bullet::Speed * ( source.X - target.X ) )

A próxima parte é confusa e você decide como deseja implementá-la no seu código, mas apenas substituímos isso e normalizamos o vetor.

sqrt( source.directionX ^ 2 + source.directionY ^ 2 ) == 1

Você acaba com uma equação com apenas um desconhecido (source.directionX) e pode resolvê-lo com directionX e substituí-lo novamente para obter directionY.

Eu não testei nenhum desses códigos e sinta-se à vontade para apontar quaisquer erros matemáticos que cometi, mas a teoria deve ser sólida :).

Boa sorte.

tyler.daniels
fonte
Sim, é apenas 2D. Lendo agora.
Azaral
Por que você está adicionando a velocidade da fonte à velocidade da bala?
sam hocevar
@ SamHocevar É assim que a física funciona. Uma bala disparada de uma arma começa com uma velocidade igual à que a disparou. Em seguida, ele adiciona o vetor gerado pela ação de disparo a esse vetor. A ação está ocorrendo no espaço, para que não haja ar para alterar a velocidade da bala. Se você disparar uma arma no espaço enquanto se move para a direita, ela estará sempre à sua frente até você mudar sua velocidade.
Azaral
11
Desculpe, ele funciona com exemplos, é difícil abstraí-lo para o caso geral. Tudo até (dV / dt = TargetApparentVelocity - BulletVelocity) é uma teoria e o único código que você precisará são estas poucas caixas finais. A álgebra que eu criei é muito complicada, mas o Mathematica foi capaz de resolvê-la. A razão pela qual usamos a derivada de primeira ordem é porque você não se importa quanto tempo leva para a bala atingir, você só quer que ela acerte.
Tyler.daniels
11
@ Azaral não é assim que a física funciona. Em um quadro de referência que se move uniformemente, você precisa subtrair a velocidade da fonte de todas as velocidades, incluindo as da bala. Se você usar a velocidade aparente para o alvo, também precisará usar a velocidade aparente para a bala.
sam hocevar
0

Este é apenas um problema de geometria 3D.

Primeiro você precisa da posição relativa e da velocidade relativa do atirador e do alvo:

Pos = TargetPos - ShooterPos
Vel = TargetVel - ShooterVel

Então você precisa resolver a equação:

Pos + t * Vel = t * FireSpeed * [x , +-sqrt(1-x^2)]

Para te x.

Isto faz:

x = ( PosX + t * VelX ) / ( t * FireSpeed )

( PosY + t * VelY ) / ( t * FireSpeed ) = +-sqrt(1 - (( PosX + t * VelX ) / ( t * FireSpeed ))^2 )
=>
( PosY + t * VelY )^2 / ( t * FireSpeed )^2 = 1 - (( PosX + t * VelX ) / ( t * FireSpeed ))^2
=>
( PosY + t * VelY )^2 + ( PosX + t * VelX )^2 = ( t * FireSpeed )^2
<=>
( Dot(Vel,Vel) - FireSpeed^2 ) * t^2 + 2 * Dot(Vel,Pos) * t + Dot(Pos,Pos) = 0

A última dessas equações é uma equação quadrática simples, que você deve resolver. Para cada resultado positivo, você insere o resultado na equação:

FireVector = Vel + Pos / t

Isso deve fornecer todos os vetores de fogo possíveis, onde té o tempo que o tiro levará.

Observe que se FireSpeedfor maior que a magnitude de Velhaverá apenas uma solução, mas se FireSpeedfor menor, poderá haver duas soluções ou nenhuma, ou, em casos especiais, apenas uma única solução dupla.

Edit: Melhor fazer a matemática corretamente ou esta resposta não será muito boa.

aaaaaaaaaaaa
fonte