 Allegro.cc Forums » Programming Questions » Math question (2d trig)

 This thread is locked; no one can reply to it.  1   2
 Math question (2d trig)
 Michael Weiss Member #223 April 2000 I have a bit of a tricky math problem and I was wondering if anyone could provide insight.Its about trigonometry and systems of equations.This is all in 2D.It is trivial to have an enemy shoot a bullet at the player. Simple trig.But I want to have an enemy shoot a bullet, not at where the player is, but where they will be.(assuming the player does not change velocity or direction)My inputs are:the player's position: px, pythe player's movement: pxinc, pyincthe origin of the bullet: bx, bythe speed of the bullet: bspeedWhat I want for an output is the angle of the bullet to ensure a collision(or bxinc, byinc which I could calculate from angle)Every method I can think of involves brute force, iterating through angles until I find a solutionThere must be an more elegant solution.I am trying to think of it as where two lines intersect...But each line has a different velocity, so the line length are related to time...and speed?Can anyone of you smart people help point me in the right direction?
 DanielH Member #934 January 2001 You need to find out at what time(t) will they collide.Basic position function: S(t) = S(0) + v(t) * t + 1/2 * a(t) * t^2S(0) = starting positionv(t) = velocity at time t (constant in your case)a(t) = acceleration at time t (zero if velocity is constant)t = timeS(t) = S(0) + v(t)tCan be broken into x,y components:Sx(t) = Sx(0) + v(t)tSy(t) = Sy(0) + v(t)tWhat are the player's and bullet's positions?Player:Px(t) = px + pxinc * tPy(t) = py + pyinc * tBullet:Bx(t) = bx + bxinc * tBy(t) = by + byinc * tthis creates a triangle{"name":"613141","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/6\/6\/66721966b91c1742b7df294159bc4344.png","w":322,"h":372,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/6\/6\/66721966b91c1742b7df294159bc4344"} You can either use x or yPx(t) = Bx(t) or Py(t) = By(t)px + pxinc*t = bx + bxinc*tpx - bx = pxinc*t + bxinc*tpx - bx = t(pxinc - bxinc)t = (px - bx)/(pxinc - bxinc)You can use original formula to then solve for x and y of final position:x(t) = px + pxinc*t = bx + bxinc*ty(t) = py + pyinc*t = by + byinc*tfloat findPosition(float &x, float &y, float px, float py, float pxinc, float pyinc, float bx, float by, float bxinc, float byinc) { float t = (px - bx)/(pxinc - bxinc); x = px + pxinc * t; y = py + pyinc * t; return t; }  It's a mess. Hopefully I didn't miss anything.
 Michael Weiss Member #223 April 2000 Thank you for that. I am working on implementing it.Time was the key. At what time will they collide?UPDATEUnfortunately I was unable to make your method work.All I have for b is initial position and speed.I do not have bxinc or byinc or the angle.That is what I am trying to find.The way I figured out how to do it was:iterate on frame (time period) in a loopfor each frame find- where we guess the player will be- the distance from that position to the bullet origin- find out how far the bullet could travel in the same amount of timeWhen those distances are equal (or close) that is where the will collide.From the frame number that occurred at, I get the players positionThat is where I shoot the bullet.#SelectExpand 1void fire_enemy_bulletb(int EN, int bullet_ans, int p) 2{ 3 al_fixed bx = Efi[EN]; 4 al_fixed by = Efi[EN]; 5 al_fixed bspeed = Efi[EN]; 6 7 al_fixed px = players[p].PX; 8 al_fixed py = players[p].PY; 9 al_fixed pxi = players[p].xinc; 10 al_fixed pyi = players[p].yinc; 11 12 for (int i=1; i<100; i++) 13 { 14 px+=pxi; 15 py+=pyi; 16 17 // calc distance from player's new pos to bullet origin 18 al_fixed xlen = px - bx; // get the x distance between enemy and player 19 al_fixed ylen = py - by; // get the y distance between enemy and player 20 al_fixed hy_dist = al_fixhypot(xlen, ylen); // hypotenuse distance 21 22 // get distance that bullet would travel in same amount of time 23 al_fixed bd = bspeed * i; 24 25 // if they are close enough pull the trigger 26 al_fixed bdif = hy_dist-bd; 27 28 al_fixed ltol = al_ftofix(-5); 29 al_fixed utol = al_ftofix(5); 30 31 if ((bdif < utol) && (bdif > ltol)) 32 { 33 i = 100; // break out of loop 34 35 al_fixed speed = Efi[EN]; // speed 36 al_fixed scaler = al_fixdiv(hy_dist, speed); // get scaler 37 38 al_fixed xinc = al_fixdiv(xlen, scaler); // calc xinc 39 al_fixed yinc = al_fixdiv(ylen, scaler); // calc yinc 40 41 for (int z=0; z<50; z++) // find empty e_bullet 42 if (!e_bullet_active[z]) 43 { 44 e_bullet_active[z] = 1; 45 e_bullet_shape[z] = 1000 + bullet_ans; 46 e_bullet_fx[z] = Efi[EN]; 47 e_bullet_fy[z] = Efi[EN]; 48 e_bullet_fxinc[z] = xinc; 49 e_bullet_fyinc[z] = yinc; 50 z=50; 51 } 52 } 53 } 54} This actually works, but I am sure there is a much better way of doing it.UPDATEThis is the equation I have come up with:https://d1cxvcw9gjxu2x.cloudfront.net/attachments/613142Now the fun part!Solve for t.My skills are not up to the task...
 Edgar Reynaldo Major Reynaldo May 2007 t and angle are related. If you change one, you change the other. You'll have to make an initial guess and hope it's efficient.
 Michael Weiss Member #223 April 2000 I had a method where I just increased time until I found a close enough solution.That took a lot of iterations.And if I wanted it more accurate, I would have to step time even slower, which meant even more iterationsI found a method where I search for t in large steps.Then when I overshoot it, I reverse direction and decrease the step size...I keep doing this until I get close enough.This greatly reduces the amount of values of t I need to test.#SelectExpand 1al_fixed get_distance(al_fixed px, al_fixed py, al_fixed pxinc, al_fixed pyinc, 2 al_fixed bx, al_fixed by, al_fixed b_speed, al_fixed t) 3{ 4 al_fixed px1 = px + al_fixmul(pxinc, t); // get the p position at time t 5 al_fixed py1 = py + al_fixmul(pyinc, t); 6 al_fixed p_distance_to_b = al_fixhypot(px1-bx, py1-by); // distance from p to b 7 al_fixed b_distance = al_fixmul(b_speed, t); // how far will b travel in time t 8 return (p_distance_to_b - b_distance); // difference between distances 9} 10 11void fire_enemy_bulleta(int EN, int bullet_ans, int p) 12{ 13 al_fixed bx = Efi[EN]; 14 al_fixed by = Efi[EN]; 15 al_fixed bspeed = Efi[EN]; 16 17 al_fixed px = players[p].PX; 18 al_fixed py = players[p].PY; 19 al_fixed pxi = players[p].xinc; 20 al_fixed pyi = players[p].yinc; 21 22 al_fixed f0 = al_itofix(0); // the number zero in fixed format 23 al_fixed t = f0; // start time 24 al_fixed tinc = al_itofix(20); // initial time step 25 al_fixed bdif = f0; 26 27 int tries = 0; 28 int done = 0; 29 while (!done) 30 { 31 t+=tinc; 32 bdif = get_distance(px, py, pxi, pyi, bx, by, bspeed, t); 33 if (( bdif < al_itofix(1)) && (bdif > al_itofix(-1))) done = 1; // is the difference with the threshold? 34 35 if (((tinc > f0) && (bdif < f0)) || // overshot while t increasing 36 ((tinc < f0) && (bdif > f0))) // overshot while t decreasing 37 tinc = al_fixdiv(tinc, al_itofix(-2)); // half the increment and reverse direction 38 39 if (tries++ > 100) done = 1; // break out in case something goes wrong 40 } 41 al_fixed px1 = px + al_fixmul(pxi, t); // get player target position based on t 42 al_fixed py1 = py + al_fixmul(pyi, t); 43 fire_enemy_bulletz(EN, bullet_ans, px1, py1); 44} However I consider this entire approach an inelegant brute force method.I can't help but think there is a better way of doing this.My whole approach can be condensed to:Find the time when the player's future position has an equal distance to the bullet origin as the distance the bullet will travel in the same amount of time.Then it is trivial to get the player's future x and y position from the time.I came up with an equation to describe that, but I don't know if it is possible to isolate t on one side (solve for t)https://www.allegro.cc/files/attachment/613143
 DanielH Member #934 January 2001 Michael Weiss said:Find the time when the player's future position has an equal distance to the bullet origin as the time the bullet will travel in the same amount of time.Then it is trivial to get the player's future x and y position from the time. That's what this function does. Returns time. Sets x and y as postion of collision.float findPosition(float &x, float &y, float px, float py, float pxinc, float pyinc, float bx, float by, float bxinc, float byinc) { float t = (px - bx)/(pxinc - bxinc); x = px + pxinc * t; y = py + pyinc * t; return t; }  If you only have bullet position and speed, we can calculate the angle and velocities // bx,by bullet position // x, y collision point float findAngle(float bx, float by, float x, float y) { return atan2(by - y, bx - x); } // you said you know the speed. get velocity from that void calculateBulletVelocity(float &bxinc, floast &byinc, float angle, float speed) { bxinc = speed * cos(angle); byinc = speed * sin(angle); }  What was this about stepping time? Let's say it take 2.47s seconds to collision, but your logic is only accurate to the tenth of a second. There might be a collision miss. Take the 0.07s difference and give the bullet a nudge in the direction.
 Michael Weiss Member #223 April 2000 I don't understand...Your function 'findPosition' needs bxinc and byinc as an input.I don't have those, that is what I am trying to find.Then you say:"If you only have bullet position and speed, we can calculate the angle and velocities"In your function 'findAngle' where do you get x and y?That is also what I am looking for, where the collision takes place.If I knew that, the rest is trivial...Its kind of like a chicken and egg thing here.If I know exactly when the collision happens, I can figure out the rest.If I know exactly where the collision happens, I can figure out the rest.Am I missing something in your explanation? I cannot see how it would work.
 DanielH Member #934 January 2001 You're right. Chicken and egg.I'll get back to you.
 Edgar Reynaldo Major Reynaldo May 2007 Like I said, t and angle are related. You have to guess for one, and then solve for the other. You don't need to iterate anything.The question (and answer) are actually more complicated than either of you are thinking. Solving for t involves solving a quadratic equation.{"name":"613146","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/7\/9\/79dc84ef8bb6bdae06f90f2d63794299.png","w":3072,"h":2275,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/7\/9\/79dc84ef8bb6bdae06f90f2d63794299"} I think what you really want though is to set a time of intercept, and solve for the angle necessary to hit the target. That's beyond me for the moment.
 DanielH Member #934 January 2001 a = distance bullet to collisionb = distance player to collisionc = distance bullet to playerA = angle at playerB = angle at bulletC = angle at collision1. make a line the player is travelling// point 1 = px, py // point 2 = px + pxinc, py + pyinc // pick any x and give corresponding y float getY(float x1, float y1, float x2, float y2, float x) { // m = rise / run float m = (y2 - y1) / (x2 - x1); // y = mx + b, b = y - mx float b = y1 - m * x1; return (m * x + b); }  now pick any point on that line ax, ay float ax = 100f; float ay = getY(px, py, px + pxinc, py + pyinc, ax);  1. calculate number of iterations of pxinc, pyinc to get to point (k)2. calculate number of iterations of bxinc, byinx to get to point (l)What is ratio? float pspeed = sqrt(pxinc^2 + pyinc^2); // already have bullet speed float k = (sqrt((px - ax)^2 + (py - ay)^2)))/pspeed; float l = (sqrt((bx - ax)^2 + (by - ay)^2)))/bspeed; float ratio = l/k; // save for later  calculate angle between 2 linescos(angle) = dotproduct(v1,v2) / (magnitude(v1)*magnitude(v2))v1 = v2 = #SelectExpand 1// line 1 - 1 to 2 2// line 2 - 3 to 2 3// point 2 is the shared point 4float calcAngle(float x1, float y1, float x2, float y2, float x3, float y3) 5{ 6 float vx = x1 - x2; 7 float vy = y1 - y2; 8 float zx = x3 - x2; 9 float zy = y3 - y2; 10 11 float dot = vx * zx + vy * zy; 12 float magv = sqrt(vx^2 + vy^2); 13 float magz = sqrt(zx^2 + zy^2); 14 15 return dot/(magv * magz); 16} 17 18// first calculate angle at player 19float angleA = calcAngle(pxinc, pyinc, px, py, bx, by); 20 21// calculate ratioed angle of temp point and divide by ration to get full angle 22float angleB = calcAngle(ax, ay, bx, by, px, py) / ratio; 23 24// difference is angle C 25float angleC = 180.0f - (angleA + angleB); Use law of sines to calculate the restsin(C)/c = sin(A)/a = sin(B)/bwe know ca = csin(A)/sin(C)b = csin(B)/sin(C)!!!!WARNING!!!!this may all be wrong as I have no way of verifying at the moment.
 Michael Weiss Member #223 April 2000 Edgar, I can't just set a time and figure out the angle.Well maybe I could if I could adjust the speed of the bullet.But I can't, that is fixed.There is a solution (actually two).An exact time when the bullet fired will hit where the player will be be.I can easily find the solution, my code works perfectly.The equation that I posted earlier describes the conditions accurately.I just don't have the mathematical skill to solve it for t.So I have to try different values of t until I get close enough.I don't claim that my solution is the only one. (or even a good one)But I know that it works.My math skills are limited, maybe that equation I posted does not have any easy way to isolate t.Or maybe there is a completely different approach that I have not even thought of.I just don't see how I can pick a value for t and then figure out the angle.EDITOK maybe there is some misunderstanding here.By time, I mean how long it will take the fired bullet to collide.The time that the bullet is fired is always t=0, the speed is constant,we know where the bullet originates...All that is needed is what direction to fire to ensure a collision.
 Edgar Reynaldo Major Reynaldo May 2007 Michael, assuming your theory and your math is correct, I solved for t. You'll still have to apply the quadratic equation to solve it though. t^2(pvx^2 + pvy^2 - bv^2) +t^1(2pxpvx + 2pypvy - 2bxpvx - 2bypvy) +t^0(px^2 + bx^2 + py^2 + by^2 - 2bxpx - 2bypy)  Just apply the quadratic equation now, and you have your values for t.I'm going to work on finding angle from t. My approach would be to set a time (like say a few seconds) and then find the angle necessary to hit that time.
 Edgar Reynaldo Major Reynaldo May 2007 You will have to check your code for division by A=0 and for a negative B^2 - 4AC discriminant.I am coming up with a different, more general solution. I'll let you know when it's done.It's taken me about a week to get the math right but I think I have it. t from theta and theta from t.{"name":"613155","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/d\/8d3e173992e3fdfc9e2b91ce552c5926.png","w":3072,"h":4096,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/d\/8d3e173992e3fdfc9e2b91ce552c5926"} So everything is derived from Dx(t) and Dy(t), the x and y distance between the player and the projectile based on time.(0a) DX(t) = dx + pvx*t - bvz*t*cos(theta) = 0 (0b) DY(t) = dy + pvy*t - bvz*t*sin(theta) = 0  dx and dy are the starting distance between the player and the launcher. pvx and pvy are the x and y velocity of the player. bvz is the magnitude of the velocity of the projectile. t is time and theta is the angle of the launcher's aim.Since we need the x and y distance between the projectile and the player to be 0 we can set them equal to zero and henceforth equal to each other. Then it is a matter of separating t and theta depending on which we want to solve for.The base equation is thus : (1) bvz*t*(cos(theta) - sin(theta)) = (pvy - pvx)*t + (dy - dx)  Solve 0b for sin(theta) and cos(theta) and then form tan(theta) using the two as sin(theta) / cos(theta). Then take the arctangent (2) of both sides and you have the angle necessary. You can also solve for t in one and substitute in the other.UPDATESo the formulas for the angle at time t are as follows :(I wish Matthew would fix those darn MathML thingies or Latex or w/e it was.)sin(theta) = (dy + pvy*t)/bvz*t cos(theta) = (dx + pvx*t)/bvz*t tan(theta) = ((dy/bvz*t + pvy/bvz) / (dx/bvz*t + pvx/bvz)) = DY/DX  Just send that mess to atan2 as atan2(DY,DX);.And then similarly you can also solve for t from a given angle to aim at and it will tell you if it is possible or not.I think the equation is this : (I've been staring at this all week) t^2 * (pvx-pvy)^2 - bvz^2 * (1 - sin(2*theta)) + t^1 * 2*(pvy-pvy)(dx-dy) + t^0 * (dx - dy)^2  Then you have another quadratic equation.\frac{-B +/-sqrt(B^2 - 4AC)}{2*A}I will try to explain better later. Don't want the thread to lock.
 DanielH Member #934 January 2001 If player is headed directly to or directly away, the following won't workTake your 3 points and make a triangle{"name":"613158","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/7\/a7bdfc4beef455bda1d9bdcb5684873a.png","w":615,"h":355,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/7\/a7bdfc4beef455bda1d9bdcb5684873a"} point p = playerpoint b = bulletpoint x = collision (unknown)side a = p to x, distance = t * pvside b = b to x, distance = t * bvside c = p to x, distance we can calculate = sqrt((bx - px)^2 + (by - py)^2)divide all 3 by time (t) to make it easier later ona = pv, which we knowb = bv, which we knowc = sqrt((bx - px)^2 + (by - py)^2)/tanglesB = we can calculate using "angle between two vectors" formulav1 = (bx - px, by - py)v2 = (pvx, pvy)cos(angle) = (v1 . v2)/(||v1||||v2||) // dot product divided by magnitudesdot product:(v1 . v2) = v1.x * v2.x + v1.y * v2.ymagnitude:||v1|| = sqrt(v1.x^2 + v1.y^2)||v2|| = sqrt(v2.x^2 + v2.y^2)Law of Sinessin(B)/b = sin(A)/a = sin(C)/csin(B)/b = sin(A)/arearrangesin(A) = asin(B)/bA = arcsin(asin(B)/b)we know B, b and a so calculate AUse to A and B to calculate final angle CC = pi - A - B (or 180deg - A - B)Law of cosinesc^2 = a^2 + b^2 - 2abcos(C)((bx - px)^2 + (by - py)^2)/t^2 = pv^2 + bv^2 - 2*pv*bv*cos(C)t^2 = ((bx - px)^2 + (by - py)^2) / (pv^2 + bv^2 - 2*pv*bv*cos(C))t = sqrt(((bx - px)^2 + (by - py)^2) / (pv^2 + bv^2 - 2*pv*bv*cos(C)))My head hurts!
 Edgar Reynaldo Major Reynaldo May 2007 DanielHI'm trying to follow your math. Yeah, it makes me dizzy too. ;pI'm writing a simple game to test my theories. Don't want the thread to lock! :X
 DanielH Member #934 January 2001 This is what I was referring to.{"name":"613172","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/6\/a60603168a8cc3dee45134ec2332f6df.png","w":616,"h":574,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/6\/a60603168a8cc3dee45134ec2332f6df"} ***** this graph only works if bullet is to the right of player, Would need alteration otherwise, bullet speed must also not be too low or will never reach player ******What you can adjust:A is angle relative to B (not absolute)p1 is playerp2 is bullet startv1 is player speedv2 is bullet speedOther stuff below that:j = horizontal difference bullet to playerk = vertical difference bullet to playerl = distance bullet to playerF = angle relative to x axesB = bullet angle from player to collisionC = collision angle from player to bulletc = distance using speed (player and bullet) alonet = actual distance / speed distance = number of iterations until collisionp3 and p4 is the point of collision using absolute angles from p1 and p2 respectively
 Edgar Reynaldo Major Reynaldo May 2007 I've been trying to solve this myself and I just can't get it right. I come up with something different every time, and even when I plug in the equation I came up with for Michael Weiss it still doesn't work. My times are negative always.
 DanielH Member #934 January 2001 Sometimes I struggled with these problems as well. Then something clicked.
 Edgar Reynaldo Major Reynaldo May 2007 I know this part is right.(0a) DX(t) = dx + pvx*t - bvz*t*cos(theta) = 0 (0b) DY(t) = dy + pvy*t - bvz*t*sin(theta) = 0 sin(theta) = (dy + pvy*t)/bvz*t cos(theta) = (dx + pvx*t)/bvz*t tan(theta) = ((dy/bvz*t + pvy/bvz) / (dx/bvz*t + pvx/bvz)) = DY/DX  This is the angle at time t. But t is still unknown.So go back to 0a and 0b. Solve for t. I did that, like 5 times. I came up with something different like three times. Grr. I've got a little demo setup that has a plane and a tank and the tank shoots at the plane, but t is negative.Now, next (just now) I read carefully through your last explanation. I follow most of it, but how can you possibly calculate v2.x and v2.y??? You don't know the angle to shoot at! So that leaves B undefined and the whole mess falls apart.After that I visited your link. Neat tutorial. Neat diagram. How do they calculate A? Do they define theta (the angle the bullet travels) ? That's v2.x and v2.y, which is unknown.I'm missing something. DanielH Member #934 January 2001 Similar trianglesAssumptions: bullet speed should be known even if we don't know direction. Normally a constant.Small triangle sides:a = known = player speedb = known = bullet speed - direction unknown but irrelevant at this pointc = unknownSmall triangle angles:A = knownB = unknownC = unknownBig triangle sides:a = t * player speedb = t * bullet speedc = distance p to bUsing small triangle, solve the rest.Law of Sines:sin(A)/a = sin(B)/b = sin(C)/cSince we know b, use it to solve for B.B = arcsin(b * sin(A) / a)Solve CC = 180 - A - BLaw of cosines:c^2 = a^2 + b^2 - 2ab*cos(C)c = sqrt(a^2 + b^2 - 2ab*cos(C))Time t is the ratio of c from large triangle to c from small triangle.t = large c / small cThe angles are relative to each and not absolute to the x/y axes. In my example, I added the offset.
 Edgar Reynaldo Major Reynaldo May 2007 I mostly understand your code, but I don't know where you are getting bv.x and bv.y from for your cos angle trick.Bump so the thread doesn't close. 