Hey I have the following problem:
I have two 3 dimensional vectors (locations).
Vector3 Person = {100, 0, 50};
Vector3 Ball = {200, 50, 100};
Now let's say I would shoot the ball with a gun. In this example, the pistol bullet moves at a constant velocity, with no bullet drop.
I have the velocity and acceleration of the ball, and the speed of the bullet:
Vector3 VelocityBall = {-10, -10, 0};
Vector3 AccelerationBall = {0, -10, 0};
float velocityBullet = 140; //M/s
Now, using the above information, I want to calculate how many seconds it will take for the pistol to hit the ball.
For this I found the following formula using magnitudes instead of vectors since time is not a vector but scalar.
Positon(ball) Velocty(ball)^time 1/2(Acceleration(ball) * time^2) = Position(bullet) Velocity(bullet) * time //resolve t
Using code it would look like that:
Vector3 Personpos = Vector3(100, 0, 50);
Vector3 Ballpos = Vector3(200,50, 100);
Vector3 Bulletveloc = Vector3(140,0,0);
Vector3 Ballveloc = Vector3(-10,-10,0);
Vector3 Ballacceleration = Vector3(0,-10,0);
float pos1 = Personpos.Length();
float pos2 = Ballpos.Length();
float vel1 = Bulletveloc.Length();
float vel2 = Ballveloc.Length();
float acc1 = Ballacceleration.Length();
float calc1 = (2 * acc1 * pos1); //acc1 will be 10 instead of -10 which is wrong
float calc2 = (2 * acc1 * pos2); //acc1 will be 10 instead of -10 which is wrong
float calc3 = pow(vel1, 2);
float calc4 = (2 * vel1 * vel2);
float calc5 = pow(vel2, 2);
float calc6 = vel1;
float calc7 = vel2;
float calc8 = (acc1); //here acc1 can be positive which is correct
float time = (sqrt(calc1 - calc2 calc3 - calc4 calc5) calc6 - calc7) / (acceleration2).Length();
this also works... but only as long as there are no negative numbers in the acceleration, otherwise the result is wrong.
The problem is that when I calculate the magnitude of the acceleration vector {-10, -10, 0}, a positive length comes out instead of a negative length which then falsifies the result. because except below the fraction line, the vector length must also be negative if the vector is negative.
Can I solve this somehow? Are there possibly simpler methods for this?
CodePudding user response:
Your approach misses the physics.
The problem you have is a collision detection of 2 objects moving along straight lines in 3D. However, the equation you're using is valid only in 1D.
The main difference is that in 3D a solution hardly ever exists, while in 1D it exists almost always, save for situations where the lack of solution is easy to predict (e.g., the initial velocities of the two bodies have opposite directions and the acceleration is 0).
One possible way of deriving the proper solution might look like this:
- write down an equation for the position (in 3D) of the bullet as a function of time
- write down an equation for the position (in 3D) of the person as a function of time
- write down the formula for the distance between them at time t (from Pythagoras formula). Actually, to simplify math and programming, consider the square of this distance, which will save you from dealing with square roots. This formula should be in the form
f(t) = 0
. - Find
t
that minimizesf(t)
. Preferably, by solving a differential equationf'(t) = 0
. You can use WolframALpha.com to find this derivative. - The collision occurs if this minimal value of
f(t)
is less then some threshold value, interpreted as the sum of the "radii" of the bullet and the person.
CodePudding user response:
As I mentioned in the comment you approached this as scalar problem instead of 3D which brings up some problems... What you can do instead is something like this (C /VCL):
//$$---- 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( 10, 10, 0),a0=vec3(0, 0,0),v0=vec3( 0, 0,0); // bullet
vec3 p1=vec3(150,100, 0),a1=vec3(0,-10,0),v1=vec3(-10,-10,0); // ball
//---------------------------------------------------------------------------
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
//---------------------------------------------------------------------------
void shoot()
{
float v=140.0; // bullet speed
float t;
vec3 p;
int i;
p0=vec3(x0 10,y0 10,0); // shot start position
for (p=p1,i=0;i<3;i ) // iterate few times to improve precision
{
t=length(p-p0)/v; // t = time to hit ball
p=p1 (v1*t) (0.5*a1*t*t); // p = ball position after t
v0=normalize(p-p0)*v;
}
t0=(length(p-p0)-r0-r1)/v; // t0 = time to hit ball
}
//---------------------------------------------------------------------------
void update(double dt)
{
int e=0;
// Newton/d'Lambert simulation
v0 =a0*dt; p0 =v0*dt;
v1 =a1*dt; p1 =v1*dt;
t0-=dt;
// 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->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.02);
_redraw=true;
draw();
}
//---------------------------------------------------------------------------
The only important stuff is function update
and shoot
. The update(dt)
just apply time dt
to your variables and shoot tries to aim your gun and shoot the bullet using your constant speed and also computes the time to hit t0
which is what you want. Of coarse the formula used works only if the bullet actually hit the target... To test for that you would need to solve this in each dimension separately and then compare the times if they match within margin for error (ignoring infinite solutions)
The code above is simple single form app with single timer (20ms) on it with this preview: