nexo sharp
nexo sharp

Reputation: 19

Projectile Aim Prediction with Target Acceleration and Bullet Deceleration Varying with Angle

I previously asked a question about this topic here, and now I'd want to review if the offered solutions worked, and then frame a new inquiry based on the difficulties and issues that resulted.

So my previous post briefly summarized, what I'm attempting to do is calculate the Impact point of a Bullet and a moving Target in 3D Space based on the Bullet's Position, Target Position, Bulletspeed, Target Velocity and Target Acceleration.

The only variable I was lacking to estimate the impact point was the time it would take the bullet to reach the target, which I could solve using the formula below thanks to this post:

formula

Right now I'm using this C++ quartic solver which gives me the time from the formula above which looks like this:

    double t4calc = (TargetAcceleration.Dot(TargetAcceleration)) / 4;
    double t3calc = (TargetAceleration.Dot(TargetVelocity));
    double t2calc = TargetAcceleration.Dot(relativepos) + velocity.Dot(TargetVelocity) - bulletspeed * bulletspeed;
    double t1calc = 2.0 * destination.Dot(TargetVelocity);
    double tcalc = (relativepos.Dot(relativepos));

    std::complex<double>* solutions = solve_quartic(t3calc / t4calc, t2calc / t4calc, t1calc / t4calc, tcalc / t4calc);

using solutions->real() it returns the time it takes to hit the target in seconds.

The Problem:

Since I assumed that the Bullet's velocity is constant and hence does not contain acceleration, the function above only include the target's acceleration but disregard the bullet's acceleration.

Now I discovered that the Bullet has also an acceleration and that it slows down over time.

Unfortunately, I can't just use a constant acceleration like -10M/s since the bullet's speed and acceleration also fluctuates based on the pitch view angle of the player.

Because using the pitch view angle for a f(x,y) calculation would be tedious, I decided to switch from viewangle to relative height and distance of the target, as my pitch view angle varies depending on how high and far the target is from me.

So, collecting some data and testing around, as well as using curve fitting, I was able to obtain the following function, which gives me the Time the Bullet need to Reach the Target in Seconds, based on the relative height and distance of the Target:

double a0 = 0.28891;
double a1 = 0.00147988;
double a2 = 0.0000116694;

double x1 = DistanceToTarget;
double x2 = Targetheight - Playerheight; //relative height of Target

double TimeToTarget = a0 + (a1 * (x1^1.2)) + (a2 * (x2^1.75)) //Returns Time in seconds it needs to reach the Target (If Target is stationary)

Okay so using the function above I could get the time which the Bullet needs to hit the Target (if the Target is not moving and standing at one point).

But of course I want the Time it needs to hit the Target if the Target is moving and accelerating..

So now I could calculate the bulletspeed in M/s by dividing the current distance to the target by the time obtained from the function above and insert it into the quartic solver function to get the time which the bullet needs to hit the moving and accelerating target based on the predicted position right...

Wrong.. this won't be accurate for the quartic solver function above, since it would use the bulletspeed computed for the target's current distance, while it should be using the bulletspeed calculated for the target's distance at impact. I hope you can follow me here.. if not feel free to ask about it.

Final Question:

So to solve this problem I'm wondering whether I can somehow include this TimeToTarget function into the previous quartic function. Otherwise the speed will be calculated incorrectly and the result will be wrong.

Upvotes: 2

Views: 1501

Answers (1)

Spektre
Spektre

Reputation: 51835

if by slowing you mean just gravity and not air friction like kv^2 or kv^3 then you can compute this very simply using 2 pass approach:

  1. definitions

    for simplicity lets assume bullet is p0,v0 and ball is p1,v1 ... meaning position,velocity and global acceleration a applying on both... so Newton d'Alembert physics dictates:

    v0+=a*dt; p0+=v0*dt;
    v1+=a*dt; p1+=v1*dt;
    

    for updating time by dt ...

  2. estimate bullet speed

    simple direct line of sight is enough for this so:

    v0=normalize(p1-p0)*v;
    

    where v is your bullet start speed.

  3. compute the collision time

    simply by solving t in:

    (p0+(v0*t)+(0.5*a*t*t)) = (p1+(v1*t)+(0.5*a*t*t));
    t = (p1-p0)/(v0-v1);
    

    as this is one solution per each axis use the smallest result above zero from the 3 see get_solution function in the example below

  4. compute difference of position between both p0,p1 after t

    simply by:

    dp=(p1-p0)+((v1-v0)*t);
    

    and correct initial v0 estimate so after t seconds the difference will be zero:

    v0+=dp/t;
    
  5. compute real time

    again either use:

    t = (p1-p0)/(v0-v1);
    

    or if you need to account for radiuses of both objects then:

    t = ((r0+r1-p0+p1)/(v0-v1));
    

However note that this approach will change the |v0| "slightly" so in case its a problem you should fit the v0 using spherical coordinates instead...

Here small C++/VCL example (just adaptation of my previous answer example)

//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#include "GLSL_math.h"
#pragma hdrstop
#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main;
//---------------------------------------------------------------------------
vec3 p0=vec3(  0,  0,  0),v0=vec3(  0,  0,0);   // bullet
vec3 p1=vec3(150,100,  0),v1=vec3(-50,-30,0);   // ball
vec3 a =vec3(0,-10,0);                          // global acceleration
//---------------------------------------------------------------------------
float t0=0.0;               // time to hit
const float r0= 2.0;        // bullet radius
const float r1=15.0;        // ball radius
float x0,y0,z0,x1,y1,z1;    // walls
//---------------------------------------------------------------------------
float get_solution(vec3 tt)
    {
    float t=0.0;
    // t = min(tt)
    if (t<tt.x) t=tt.x;
    if (t<tt.y) t=tt.y;
    if (t<tt.z) t=tt.z;
    return t;
    }
//---------------------------------------------------------------------------
void shoot()
    {
    const float v=250.0;            // bullet start speed
    int i;
    float t;
    vec3 dp;
    p0=vec3(0.5*(x0+x1),y0+r0,0);   // reset shot start position

    // solve t for colision:
    //  (p0+(v0*t)+(0.5*a*t*t)) - (p1+(v1*t)+(0.5*a*t*t)) = r0+r1;
    //  t = (r0+r1-p0+p1)/(v0-v1);

    v0=normalize(p1-p0)*v;          // v0 estimate
    t=get_solution((p1-p0)/(v0-v1));// time estimate
    dp=(p1-p0)+((v1-v0)*t);         // diference after time t
    v0+=dp/t;                       // correct v0 estimate
    t0=get_solution((r0+r1-p0+p1)/(v0-v1)); // real time to collision
    }
//---------------------------------------------------------------------------
void update(double dt)
    {
    int e=0;
    // Newton/d'Lambert simulation
    v0+=a*dt; p0+=v0*dt;
    v1+=a*dt; p1+=v1*dt;
    t0-=dt;
    // bullet timeout
    if (t0<=0.0) e=1;
    // bullet hit the target
    if (length(p1-p0)<=r1+r0) e=1;
    // bullet colision with wall
    if (p0.x<x0+r0) e=1;
    if (p0.x>x1-r0) e=1;
    if (p0.y<y0+r0) e=1;
    if (p0.y>y1-r0) e=1;
    if (p0.z<z0+r0) e=1;
    if (p0.z>z1-r0) e=1;
    // ball colision with wall
    if (p1.x<x0+r1){ p1.x=x0+r1; v1.x=-v1.x; }
    if (p1.x>x1-r1){ p1.x=x1-r1; v1.x=-v1.x; }
    if (p1.y<y0+r1){ p1.y=y0+r1; v1.y=-v1.y; }
    if (p1.y>y1-r1){ p1.y=y1-r1; v1.y=-v1.y; }
    if (p1.z<z0+r1){ p1.z=z0+r1; v1.z=-v1.z; }
    if (p1.z>z1-r1){ p1.z=z1-r1; v1.z=-v1.z; }
    // shoot again if needed
    if (e) shoot();
    }
//---------------------------------------------------------------------------
void TMain::draw()
    {
    if (!_redraw) return;
    float x,y,r;

    // clear buffer
    bmp->Canvas->Brush->Color=clBlack;
    bmp->Canvas->FillRect(TRect(0,0,xs,ys));

    // walls
    bmp->Canvas->Pen->Color=clWhite;
    bmp->Canvas->Brush->Color=clBlack;
    bmp->Canvas->Rectangle(x0,ys-y0,x1,ys-y1);

    // ball
    bmp->Canvas->Pen->Color=clAqua;
    bmp->Canvas->Brush->Color=clBlue;
    x=p1.x; y=ys-p1.y; r=r1;
    bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
    // bullet
    x=p0.x; y=ys-p0.y; r=r0;
    bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);

    bmp->Canvas->Brush->Color=clBlack;
    bmp->Canvas->Font->Color=clYellow;
    bmp->Canvas->TextOutA(0,0,AnsiString().sprintf("time to hit: %.3fs",t0));

    // render backbuffer
    Main->Canvas->Draw(0,0,bmp);
    _redraw=false;
    }
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner)
    {
    bmp=new Graphics::TBitmap;
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    pyx=NULL;
    _redraw=true;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormDestroy(TObject *Sender)
    {
    if (pyx) delete[] pyx;
    delete bmp;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormResize(TObject *Sender)
    {
    xs=ClientWidth;  xs2=xs>>1;
    ys=ClientHeight; ys2=ys>>1;
    bmp->Width=xs;
    bmp->Height=ys;
    if (pyx) delete[] pyx;
    pyx=new int*[ys];
    for (int y=0;y<ys;y++) pyx[y]=(int*) bmp->ScanLine[y];
    _redraw=true;

    int w=32;
    // boundaries
    x0=w; x1=xs-w;
    y0=w; y1=ys-w;
    z0=-100; z1=+100;

    p1=vec3(x1-r1,y1-r1,0);
    shoot();

    update(0.0);
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormPaint(TObject *Sender)
    {
    _redraw=true;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::tim_redrawTimer(TObject *Sender)
    {
    update(0.04);
    _redraw=true;
    draw();
    }
//---------------------------------------------------------------------------

and preview:

preview

again the important stuff is function shoot() which aims and shoot bullet p0,v0 so it hits p1,v1 after t0 seconds... function update(dt) just simulates time and handle the collisions ...

Upvotes: 1

Related Questions