This topic is 4336 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Ok so I've been scouring the net for an answer to a projectile leading problem. I haven't had much luck finding stuff, but I found a solution here that I would like to know the origins of. This is a really old thread I'm referencing, but it's about all I can find on the subject. The following was contributed by member TerranFury about 4 years ago: I did this quickly, so I may have made a mistake, but I think I solved the 3D equivalent. The solution is a vector of magnitude k, where k is the speed of the projectile. I use C-style struct notation to refer to A (the target) and B (the projectile). A 'v' member is a vector; each vector has x, y, and z component members. Most things should be self explanatory; if you have any questions post them. I think I've fixed any errors... D = B.initpos - A.initpos L = |A.v|2 - k2 G = 2(A.v * D) t = |[G + sqrt(G2 - 4L * |D|)] / (2L)| B.v = A.v - D/t So my question is, where does this derivation come from. How exactly does he arrive at the quadratic equation for this problem? Is there some basic algabraic or geometric principle I'm missing here? Any explanation at all would be very appreciated. Thanks :)

##### Share on other sites
someone knows the answer to this i'm sure. I've seen this equation in a few places, I just want to know where it comes from...bump :)

##### Share on other sites

(My solution is a bit different, but I guess the same principles apply.)
Basically, you have to solve the equation system:

(I)  A + tV = B + tW(II) V.V = k^2("." is dot product, "^" is power)A: initial position of projectile V: velocity vector of projectile (unknown)B: initial position of targetW:  velocity vector of targetk: speed of projectile (scalar)t: time to impact (unknown, scalar)We assume that D has a length > 0 and therefore |t| > 0.Divide (I) by t:A/t + V = B/t + W==> V = (B - A)/t + W = Du + W, where D = B-A and u = 1/tusing (II):==> k^2 = V.V = (D.D)u^2 + 2u(D.W) + (W . W)==> u^2 + 2u(D.W)/(D.D) + [(W.W) - k^2]/(D.D) = 0Solve this quadratic equation for u. Discard  negative solutions. If you still have two solutions, use the larger one (-> smaller t). Then V = Du + W.

##### Share on other sites
I actually found a more intuitive answer to the problem. But i'm still unclear on the derivation. Here's an answer given by jack_1313:

//starting position
//bullet speed
//target position
//target velocity
Vector2D LeadTarget( Vector2D sp, float bs, Vector2D tp, Vector2D tv )
{
Vector2D D = tp - sp;
float E = D.Dot( D );
float F = 2 * tv.Dot( D );
float G = bs * bs - tv.Dot( tv );
float t = ( F + sqrt( F * F + 4 * G * E) ) / ( G * 2 );

return D / t + tv;
}

ok so the first part's easy: Vector2D D = tp - sp; this just makes the vector from the source to the target.

the next line requires pretty good knowledge of dot products and vectors. float E = D.Dot(D);

Dotting a vector with itself will result in the squared magnitude of that vectore (the length squared). So this is the squared distance between the source and the target (squared so we can avoid square roots until the end).

this is where it gets hairy... float F = 2 * tv.Dot(D); so he projects the targets velocity vector onto the vector we just created (D). He then multiplies it by 2. I'm thinking this is just him 'leading' the target (not sure why 2 is the right choice, seems rather arbitrary).

then comes the big dog: float G = bs * bs - tv.Dot(tv); again he dots the vector with itself giving us the squared magnitude of the vector. He then subtracts this squared distance from the squared speed of the bullet. I'm not sure why he does this....(I'm thinking all of this has something to do with Pythag)

then he uses the quadratic equation to find at which point the line segments intersect (quadratic equation returns the root of a polynomial, i.e. when it crosses the x axis, thus making y == 0). When this value is 0, we have an intersection and thus we know the time.

the return value is also baffling me...he divides the original distance by the time at which they would intersect, and then adds to it the targets velocity vector.

Sorry for the long post, but this is a very interesting problem, i'm suprised there's not more discussion on it since it's a fairly common issue (or at least I would think so).

##### Share on other sites
Hmm i remember i solved it once but don't remember exactly how. Anyway, it's not too complicated, i'll re-solve:

First, use coordinate frame where projectile is fired from 0,0
g is gravity (9.8) , t is time, v is velocity, α is angle, c=cos(α), s=sin(α), u is speed of projectile, p is target position (relatively to launch point of course). Later we will use T=tan(α) , don't confuse with time.

We have equations of motion of projectile:
x=v.x*t
y=v.y*t - g*t2/2

so from there we can make equations
v.x*t = p.x
v.y*t - g*t2/2 = p.y
v.x2 + v.y2 = u2

we can rewrite equations as

u*c*t = p.x
u*s*t - g*t2/2 = p.y
c2+s2 = 1

let's try solving it, from first equation we can get t=p.x/(u*c) and substitute into second thus eliminating t
u*s*p.x/(u*c) - g*(p.x/(u*c))2/2 = p.y

p.x*s/c - g*(p.x/(u*c))2/2 = p.y
c2+s2 = 1

if i recall correctly i spotted s/c = tan(α) and decided to solve for tangent.
********************** some useful identity
before doing that we need to find how to get c^2 from tangent so we can then replace c^2 by some expression with only tangent it it:
tan(α) = sin(α)/cos(α) = sqrt(1-cos2(α))/cos(α) = sqrt(1/cos2(α) -1)
so
1/cos2(α) - 1 = tan2(α)
Just lovely, even better, we eliminate the division too!
1/cos2(α)=tan2(α)+1
******************

Let, to shorten things a little, T=tan(α) . We won't use time anymore so that shouldn't be confusing.

so we get
p.x*T - g*p.x2/(2*u2) * 1/c2 = p.y
then finally
p.x*T - g*p.x2/(2*u2) * (T2+1) - p.y = 0

-g*p.x2/(2*u2) * T2 + p.x*T - (p.y+g*p.x2/(2*u2)) = 0

and we have

A=-g*p.x2/(2*u2)
B=p.x
C=-(p.y+g*p.x2/(2*u2))

T= (-B (+-) sqrt(B2 - 4*A*C))/(2*A)

Simplify as you feel like.
Then, to get velocity vector take (1,T) , normalize it, and multiply by speed.
Note: it is unlikely that above contain mistakes, but i'll probably recheck it later to make sure. If you wish you can write unit-test to make sure.

edit:
whoops, i gave solution to wrong problem.[lol]

You're just shooting at moving target, without any gravity? that's very easy. In fact i've been misleaded by mention of ballistics etc. Will post next one.

I never make stupid mistakes, only very, very clever ones... -- Dr Who.
edit: fixed missing square root typo
edit: and another typo in same place.

[Edited by - Dmytry on April 7, 2006 2:26:49 AM]

##### Share on other sites
Whoops, i misunderstood what you want. I thought you have gravity and want to find firing angle...[lol]
if you just want shooting at moving target without any gravity, that's actually quite easy. We just get ray-sphere intersection in the end(it's kind of sort of intuitive really).

We need to find some v that for some time t both equations is true:
v . v = speed^2
v*t=target_p+target_v*t;
divide both sides[of second equation] by t and let u=1/t

v . v = speed^2
v=target_p*u+target_v;
It's just ray-sphere intersection; combine equations and you get
|target_p*u+target_v|2=speed2

All allowed values of v is on sphere with center in 0,0,0 and radius speed; we just need to find point on this sphere that is point of intersection of sphere with line (target_p*u+target_v). If you can derive ray-sphere intersection yourself, problem solved.
edit: and there's derivations for ray-sphere intersection.
|target_p*u+target_v|2=speed2
unroll magnitude squared into dot product
(target_p*u+target_v).(target_p*u+target_v)=speed2
open braces (vector algebra there) we get quadratic
(target_p . target_p)*u^2 + 2*target_p.target_v * u + target_v.target_v - speed2 = 0
so we get
A=target_p . target_p
B=2 * target_p.target_v
C=target_v.target_v - speed2
and just plug into quadratic and simplify.
Then when you have u use
v=target_p*u+target_v;

[Edited by - Dmytry on April 2, 2006 12:06:53 PM]

##### Share on other sites
Quote:
 Original post by DmytryA=-g*p.x2/(2*u2)B=p.xC=(p.y+g*p.x2/(2*u2))T= (-B2 (+-) (B2 - 4*A*C))/(2*A)I never make stupid mistakes, only very, very clever ones... -- Dr Who.

I'm more interested in the gravity model but should the final derivation not read:

T= (-B2 (+-) sqrt(B2 - 4*A*C))/(2*A)

##### Share on other sites
Quote:
Original post by niX@BB
Quote:
 Original post by DmytryA=-g*p.x2/(2*u2)B=p.xC=(p.y+g*p.x2/(2*u2))T= (-B2 (+-) (B2 - 4*A*C))/(2*A)I never make stupid mistakes, only very, very clever ones... -- Dr Who.

I'm more interested in the gravity model but should the final derivation not read:

T= (-B2 (+-) sqrt(B2 - 4*A*C))/(2*A)

whoops, typo! [lol]

##### Share on other sites
Whoops seems I've spotted another typo...

Quote:
 C=(p.y+g*p.x2/(2*u2))

C=-(p.y+g*p.x2/(2*u2))

I'm still trying to get these equations working- I'll try posting some of the source I've derived from this post tomorrow.

##### Share on other sites
lol. i'm is not sure it all works now so i'll be writing test. Actually i wasn't very carefull as it was written just to give idea on the derivations assuming person already has all the working code.
edit: another typo. instead of -B^2 in quadratic there of course should be -B , (i spotted it when i were writing code for test. i usually check things when writing to the code) In my defense, it is rather painful to write B< sup >2< /sup > in the HTML and even more painful to look through, so i were copypasting 2
Another thing, p.x must be positive (which, though, comes automatically if you're working in 3D and are using horizontal distance).

[Edited by - Dmytry on April 7, 2006 3:10:08 AM]

##### Share on other sites
Okay, there it is:
#include <iostream>#include <cmath>const double pi=3.1415926535897932384626433832795;const double g=9.8;double speed=20;double x=23.456789, y=8.45678901;using namespace std;int main(){    double A=-g*x*x/(2*speed*speed);    double B=x;    double C=-(y + g*x*x/(2*speed*speed));    double d=B*B - 4*A*C;    if(d<0){        cout<<"can't reach"<<endl;        return 0;    }    double T1=(-B + sqrt(d))/(2.0*A);    double T2=(-B - sqrt(d))/(2.0*A); // there put velocity-finding code    double v1x=1.0, v1y=T1;    double r1=sqrt(v1x*v1x + v1y*v1y);    v1x=(speed/r1)*v1x;    v1y=(speed/r1)*v1y;    double v2x=1.0, v2y=T2;    double r2=sqrt(v2x*v2x + v2y*v2y);    v2x=(speed/r2)*v2x;    v2y=(speed/r2)*v2y;    // now test:    double time1=x/v1x;    cout<<"angle1 ="<<atan2(v1y,v1x)*180.0/pi<<endl;    cout<<"time1="<<time1<<endl;    double height1=v1y*time1-g*time1*time1*0.5;    cout<<"erro1="<<height1-y<<endl;    cout<<"angle2 ="<<atan2(v2y,v2x)*180.0/pi<<endl;    double time2=x/v2x;    cout<<"time2="<<time2<<endl;    double height2=v2y*time2-g*time2*time2*0.5;    cout<<"error2="<<height2-y<<endl;	return 0;}

It computes two firing velocities and shots at them. I haven't fixed any other typos than ones i mentioned in post above, so it worked right from beginning.
Output:
angle1 =40.7211time1=1.5475erro1=-3.55271e-15angle2 =69.1045time2=3.28835error2=0

So it works right,- 3.55271e-15 is just floating point errors.

Some notes on actually implementing this thing: x needs to be positive, so if you are working in 2D: if it is negative, make it positive, then after computing velocities negate x component of velocities.

If you need to make it work in 3D: Assuming y is up, you'll need to do that:
// inputs: launcher_position, target_position, speed// outputs: shot_vel1 , shot_vel2 // first is shot at higher angle (arrives at target slower,// second is shot at lower angle (arrives at target faster),// return value: true if can hit, false otherwise.bool ComputeProjectileVel(const Vec3 &launcher_position, const Vec3 &target_position, double speed, Vec3 &shot_vel1, Vec3 &shot_vel2){Vec3 target_vec=target_position-launcher_position;// then x will be horizontal distancedouble x=sqrt(target_vec.x*target_vec.x+target_vec.z*target_vec.z);// and y is vertical distancedouble y=target_vec.y;// there the root computing code:    double A=-g*x*x/(2*speed*speed);    double B=x;    double C=-(y + g*x*x/(2*speed*speed));    double d=B*B - 4*A*C;    if(d<0){ // if we can't reach target        return false;    }    double T1=(-B + sqrt(d))/(2.0*A);    double T2=(-B - sqrt(d))/(2.0*A); shot_vel1=Vec3d(target_vec.x, T1*x, target_vec.z);// x is horizontal distance, T is tangent of firing angle// make length of shot velocity be equal to speed (if you just need direction, normalize instead) shot_vel1*=speed/shot_vel1.Length(); shot_vel2=Vec3d(target_vec.x, T2*x, target_vec.z); shot_vel2*=speed/shot_vel2.Length(); return true;}

Note: ComputeProjectileVel is untested but i looked through it carefully.
Another note: may have division by zero if target and launch points is equal. If you want, handle it accurately.

Third node: if you want it to hit target accurately in the game, you'll need to accurately simulate flight of projectile itself, as in
projectile_position+=projectile_velocity+Vec3(0,-g*dt*dt*0.5,0);
projectile_velocity.y-=g*dt;
(or even just find position of projectile at time t right from the equations of motion.)

[Edited by - Dmytry on April 7, 2006 3:54:15 AM]

##### Share on other sites
After alot of wrestling last night I also finally noticed the B2 +/- sqrt(B2 - 4AC) bug... nasty!

anyway here is the code I promised - it is a fully functional win32 OpenGL app showing the path of the projectile the initial launch position and the target (your mouse cursor).

Zoom in and out with q/a and raise and lower the launch position with w/s.

[cool][cool][cool]
Three thumbs up for Dmytry's sweet solution!!
(minus a thumb for all the typo's)[grin]

/************************** * Includes * **************************/#include <windows.h>#include <gl/gl.h>#include <math.h>#define DISPLAY_WIDTH 600#define DISPLAY_HEIGHT 600/************************** * Function Declarations * **************************//************************** * Structs * **************************/class vector{public:	float x, y;	void Normalize (void)	{		float len = sqrt ((x*x)+(y*y));		if (!len)			return;		len = 1.0f/len;		x *= len;		y *= len;	}};LRESULT CALLBACK WndProc (HWND hWnd, UINT message,WPARAM wParam, LPARAM lParam);void EnableOpenGL (HWND hWnd, HDC *hDC, HGLRC *hRC);void DisableOpenGL (HWND hWnd, HDC hDC, HGLRC hRC);void RenderFrame (void);void UpdateFrame (void);void CalculateLaunchVector (vector &vecLaunch1, vector &vecLaunch2, const vector &vecPos, const vector &vecTargPos, const float fSpeed);void DrawPath (const vector &vecPos, const vector &vecTargPos, const vector &vecDir);/************************** * Global Variables * **************************/unsigned int iElapsedTime = 0;float fZoom = 15.0f;vector vecMousePos = {0.0f, 0.0f};vector vecPos = {0.0f, 0.0f};vector vecTarg = {0.0f, 0.0f};vector vecL1 = {0.0f, 1.0f};vector vecL2 = {0.0f, 1.0f};/************************** * WinMain * **************************/int WINAPIWinMain (HINSTANCE hInstance,		 HINSTANCE hPrevInstance,		 LPSTR lpCmdLine,		 int iCmdShow){	WNDCLASS wc;	HWND hWnd;	HDC hDC;	HGLRC hRC;	MSG msg;	BOOL bQuit = FALSE;	RECT rect;	unsigned int iCurrentTime, iLastTime = 0;	/* register window class */	wc.style = CS_OWNDC;	wc.lpfnWndProc = WndProc;	wc.cbClsExtra = 0;	wc.cbWndExtra = 0;	wc.hInstance = hInstance;	wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);	wc.hCursor = LoadCursor (NULL, IDC_ARROW);	wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);	wc.lpszMenuName = NULL;	wc.lpszClassName = "BallisticTest";	RegisterClass (&wc);	rect.left = 0;	rect.top = 0;	rect.right = DISPLAY_WIDTH;	rect.bottom = DISPLAY_HEIGHT;	/* Calculate window size that will ensure the correct client area */	AdjustWindowRectEx (&rect, WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);	/* create main window */	hWnd = CreateWindow (	  "BallisticTest", "Ballistic Test",	  WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE,	  GetSystemMetrics (SM_CXSCREEN)/2-(rect.right-rect.left)/2,	  GetSystemMetrics (SM_CYSCREEN)/2-(rect.bottom-rect.top)/2,	  rect.right-rect.left, rect.bottom-rect.top,	  NULL, NULL, hInstance, NULL);	/* enable OpenGL for the window */	EnableOpenGL (hWnd, &hDC, &hRC);	SetBkMode (hDC, TRANSPARENT);	SetTextColor (hDC, RGB(255, 255, 255));	/* Initialize iLastTime */	iLastTime = GetTickCount ();	/* program main loop */	while (!bQuit)	{		/* check for messages */		if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))		{			/* handle or dispatch messages */			if (msg.message == WM_QUIT)			{				bQuit = TRUE;			}			else			{				TranslateMessage (&msg);				DispatchMessage (&msg);			}		}		else		{			iCurrentTime = GetTickCount ();			iElapsedTime = iLastTime - iCurrentTime;			UpdateFrame ();			RenderFrame ();			char *m1 = "Zoom: 'q'\\'a'";			char *m2 = "Elevation: 'w'\\'s'";			TextOut (hDC, 0, 0, m1, strlen (m1));			TextOut (hDC, 0, 12, m2, strlen (m2));			SwapBuffers (hDC);			iLastTime = iCurrentTime;		}	}	/* shutdown OpenGL */	DisableOpenGL (hWnd, hDC, hRC);	/* destroy the window explicitly */	DestroyWindow (hWnd);	return msg.wParam;}/******************** * Window Procedure * ********************/LRESULT CALLBACKWndProc (HWND hWnd, UINT message,		 WPARAM wParam, LPARAM lParam){	switch (message)	{	case WM_CREATE:		return 0;	case WM_CLOSE:		PostQuitMessage (0);		return 0;	case WM_DESTROY:		return 0;	case WM_KEYDOWN:		switch (wParam)		{		case VK_ESCAPE:			PostQuitMessage(0);			return 0;		}		return 0;	case WM_MOUSEMOVE:		vecMousePos.x = (float)LOWORD(lParam)/(float)DISPLAY_WIDTH;		vecMousePos.y = 1.0f-((float)HIWORD(lParam)/(float)DISPLAY_HEIGHT);		return 0;	default:		return DefWindowProc (hWnd, message, wParam, lParam);	}}/******************* * Enable OpenGL * *******************/voidEnableOpenGL (HWND hWnd, HDC *hDC, HGLRC *hRC){	PIXELFORMATDESCRIPTOR pfd;	int iFormat;	/* get the device context (DC) */	*hDC = GetDC (hWnd);	/* set the pixel format for the DC */	ZeroMemory (&pfd, sizeof (pfd));	pfd.nSize = sizeof (pfd);	pfd.nVersion = 1;	pfd.dwFlags = PFD_DRAW_TO_WINDOW |	  PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;	pfd.iPixelType = PFD_TYPE_RGBA;	pfd.cColorBits = 24;	pfd.cDepthBits = 16;	pfd.iLayerType = PFD_MAIN_PLANE;	iFormat = ChoosePixelFormat (*hDC, &pfd);	SetPixelFormat (*hDC, iFormat, &pfd);	/* create and enable the render context (RC) */	*hRC = wglCreateContext( *hDC );	wglMakeCurrent( *hDC, *hRC );}/****************** * Disable OpenGL * ******************/voidDisableOpenGL (HWND hWnd, HDC hDC, HGLRC hRC){	wglMakeCurrent (NULL, NULL);	wglDeleteContext (hRC);	ReleaseDC (hWnd, hDC);}/****************** * UpdateFrame * ******************/voidUpdateFrame (void){	// Check if the launch platform should be raised	if (GetKeyState ('W') & 0x80000000)		vecPos.y += 0.1f;	if (GetKeyState ('S') & 0x80000000)		vecPos.y -= 0.1f;	if (GetKeyState ('Q') & 0x80000000)		fZoom += 0.1f;	if (GetKeyState ('A') & 0x80000000)		fZoom -= 0.1f;	// Calc vecTarg based on fZoom	vecTarg.x = (vecMousePos.x*2.0f - 1.0f) * fZoom;	vecTarg.y = (vecMousePos.y*2.0f - 1.0f) * fZoom + fZoom * 0.9f;	CalculateLaunchVector (vecL1, vecL2, vecPos, vecTarg, 20.0f);}/****************** * RenderFrame * ******************/voidRenderFrame (void){	glClearColor (0.0f, 0.0f, 0.0f, 0.0f);	glClear (GL_COLOR_BUFFER_BIT);	glPushMatrix ();	// Zoom in and out based on the Maximum range	glScalef (1.0f/fZoom, 1.0f/fZoom, 1.0f);	glTranslatef (0.0f, fZoom * -0.9f, 0.0f);	// Draw Horizon	glBegin (GL_LINES);	glColor3f (0.0f, 0.5f, 0.0f);	glVertex2f (-10000.0f, 0.0f); glVertex2f (10000.0f, 0.0f);	glEnd ();	// Draw Launch Vectors	glBegin (GL_LINES);	glColor3f (0.0f, 1.0f, 0.0f);	glVertex2f (vecPos.x, vecPos.y); glVertex2f (vecPos.x + vecL1.x, vecPos.y + vecL1.y);	glColor3f (0.0f, 0.0f, 1.0f);	glVertex2f (vecPos.x, vecPos.y); glVertex2f (vecPos.x + vecL2.x, vecPos.y + vecL2.y);	glEnd ();	// Draw Base	glBegin (GL_TRIANGLES);	glColor3f (0.5f, 0.5f, 0.5f);	glVertex2f (vecPos.x, vecPos.y + 0.75f);	glVertex2f (vecPos.x + 0.435f, vecPos.y);	glVertex2f (vecPos.x - 0.435f, vecPos.y);	glEnd ();	// Draw Target	glBegin (GL_LINES);	glColor3f (1.0f, 0.0f, 0.0f);	glVertex2f (vecTarg.x-0.5f, vecTarg.y-0.5f); glVertex2f (vecTarg.x+0.5f, vecTarg.y+0.5f);	glVertex2f (vecTarg.x-0.5f, vecTarg.y+0.5f); glVertex2f (vecTarg.x+0.5f, vecTarg.y-0.5f);	glEnd ();	glColor3f (0.0f, 1.0f, 0.0f);	DrawPath (vecPos, vecTarg, vecL1);	glColor3f (0.0f, 0.0f, 1.0f);	DrawPath (vecPos, vecTarg, vecL2);	glPopMatrix ();}/****************** * CalculateLaunchVector * ******************/void CalculateLaunchVector (vector &vecLaunch1, vector &vecLaunch2, const vector &vecLaunchPos, const vector &vecTargPos, const float fSpeed){	float A, B, C, disc, g = 9.8;	vector targ;	targ.x = vecTargPos.x - vecLaunchPos.x;	targ.y = vecTargPos.y - vecLaunchPos.y;	/* A=-g*p.x2/(2*u2) */	A = (-g * targ.x * targ.x) / (2.0f * fSpeed * fSpeed);	/* B=p.x */	B = fabs(targ.x);	/* C=-(p.y+g*p.x2/(2*u2)) */	C = -(targ.y + (g * targ.x * targ.x) / (2.0f * fSpeed * fSpeed));	/* T= (-B2 (+-) sqrt(B2 - 4*A*C))/(2*A) */	disc = B*B - 4.0f*A*C;	if (disc < 0)		return; // out of range	disc = (float)sqrt (disc);	if (targ.x>0)	{		vecLaunch1.x = 1.0f;		vecLaunch2.x = 1.0f;	}	else	{		vecLaunch1.x = -1.0f;		vecLaunch2.x = -1.0f;	}	vecLaunch1.y = (-B + disc)/(2.0f * A);	vecLaunch2.y = (-B - disc)/(2.0f * A);	vecLaunch1.Normalize ();	vecLaunch2.Normalize ();	vecLaunch1.x *= fSpeed;	vecLaunch1.y *= fSpeed;	vecLaunch2.x *= fSpeed;	vecLaunch2.y *= fSpeed;}/****************** * DrawPath * ******************/void DrawPath (const vector &vecLaunchPos, const vector &vecTargPos, const vector &vecDir){	float g = 9.8, tt, t, dt;	vector targ, c, p, v;	targ.x = vecTargPos.x - vecLaunchPos.x;	targ.y = vecTargPos.y - vecLaunchPos.y;	tt = targ.x / vecDir.x;	dt = tt/20.0f;	t = 0.0f;	p.x = vecLaunchPos.x;	p.y = vecLaunchPos.y;	v.x = vecDir.x;	v.y = vecDir.y;	glBegin (GL_LINES);	for (int i=0; i<20; i++)	{		t += dt;		c.x = vecLaunchPos.x + v.x * t;		c.y = vecLaunchPos.y + v.y * t - (g*t*t) * 0.5f;		if (i%2)		{			glVertex2f (c.x, c.y);			glVertex2f (p.x, p.y);		}		p.x = c.x;		p.y = c.y;	}	glEnd ();}

edit: included dymtry's fixes

[Edited by - niX_BB on April 7, 2006 6:21:40 AM]

##### Share on other sites
Cool.

Good i didn't make typos in actual derivations (it was originally written just to show derivations so the correct derivations plus typos in the end).

Some very minor correction, for 2D use, if target may be to the left of launch point, you'll need to use
B=fabs(targ.x);
and
vecLaunch1.x = targ.x>0 ? 1.0f : -1.0f;

vecLaunch2.x = targ.x>0 ? 1.0f : -1.0f;
(in 3D not needed because x is always positive)
Also, it will have division by zero problem if targ.x is very small or zero, so if this may happen you need to handle it specially (e.g. just make it fire vertically)

edit: speaking about typos, i see you have uncovered some typos/bugs in forum code? (@ in name) [lol]

##### Share on other sites
Ahh, that's much better - I noticed that the path projection function was drawing in negative time, but didn't try to figure out why. (I spent my time on the 3D solution)

The 3D solution is much more elegant and will solve for the 2D case anyway.

Thank you for all your help Dmytry, this was an interesting learning exercise.

-nix

edit: oh and my nick problems actually stem from the underscore... the admins fixed it and deleted my temp nick.