Code archives/3D Graphics - Maths/General solution to the lead target problem
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
Okay...I posted another function a little earlier which was for a specific situation. This code is more general. Assuming you have two entities moving in any direction at a constant speed then there may exist a direction to fire a bullet which has a fixed muzzle speed which will hit the target. Eg....think of air combat games where you need to lead the target in order to hit it. This will solve that problem of where to aim. Note there are cases where no solution exists. This is documented in the code. I've included an example. The maths behind it is based on parametrising the position vectors in terms of time, then applying a constraint of a fixed muzzle velocity and solving for the time since we now have an additional degree of freedom required. The equation to solve ends up being the quadratic formula but in 3 dimensions. Have a look below for yourself. | |||||
;Function to find the velocity needed to hit a moving target, from a moving platform for a fixed muzzle velocity. ;Because of the constraint of a fixed muzzle velocity we need a degree of freedom, in this case I have chosen the ;time to hit the target, though another possibility would have been the velocity of the moving platform - however in ;practice this is less easy to control for. ; ;Note only calculateMuzzleVelocity is useful for the end user....the other functions are for internal use only..... Dim MuzzleVelocity#(3) ;unfortunately because I cannot return an array with a blitz3d/plus function I have to define an array to receive the values from the function...if there is ; a better solution I don't know it.....banks are nice but lead to memory leaks if forgotten about, types are overkill for this.....alternatively returning ;a comma separated string would work but be highly inefficient...... ;simple example... Works in both 2D and 3D..... example2D() Function calculateMuzzleVelocity(posx#,posy#,posz#,velx#,vely#,velz#,targetx#,targety#,targetz#,targetvelx#,targetvely#,targetvelz#,maximummuzzlevel#) ;Pass in the values above....should make perfect sense.... ; ;Positions of platform (posx,posy,posz), velocities of platform (velx,vely,velz) ;Positions of target (targetx,targety,targetz), velocities of target (targetvelx,targetvely,targetvelz) ; ;Maximum muzzle velocity - ie the maximum speed the bullet is allowed to travel at - not including any acquired velocity from the moving platform..... ; ;Returns: ; ;false if unable to hit target (and does not alter contents of array) ; ;true if able to hit target (and populates array with velocity in x,y,z directions...) ;Oh...the 3rd index in muzzle velocity is actually the time - not a spatial coordinate....useful number to have ; T# = getT(getD(targetx,posx),getD(targety,posy),getD(targetz,posz),getVD(targetvelx,velx),getVD(targetvely,vely),getVD(targetvelz,velz),maximummuzzlevel) If(T=-1 Or T=0) Then ;;not possible to hit target.... Return False EndIf MuzzleVelocity(0) = getVm(targetx,posx,targetvelx,velx,T) MuzzleVelocity(1) = getVm(targety,posy,targetvely,vely,T) MuzzleVelocity(2) = getVm(targetz,posz,targetvelz,velz,T) MuzzleVelocity(3) = T Return True; End Function Function getVm#(target#,platform#,vtarget#,vplatform#,T#) ;used in all 3 coordinate axes...hence no specific axis mentioned... ;do not call for T=0.... ; ;Technically - you shouldn't need to call this - it is another helper function like the ones below.. ; ; ; ;Parameters ; ;target = position of target on axis ;platform = position of target on axis ;vtarget = velocity of target on axis ;vplatform = velocity of target on axis ; ;Return value ; ;Muzzle velocity along the axis required to hit target at time T. (T is calculated elsewhere...see further down) ; If(T<>0) Then Return (target - platform + (vtarget - vplatform)*T)/T EndIf End Function Function getD#(target#,platform#) ;helper function...no need to call this...effectively a private method Return target - platform End Function Function getVD#(vtarget#,vplatform#) ;unfortunate name for a function but oh well...don't worry you won't need to call this...another private method... Return vtarget - vplatform End Function Function getT#(Dx#,Dy#,Dz#,VDx#,VDy#,VDz#,L#) ;function to calculate the appropriate time needed to satisfy the equation. ;Note - sometimes there is no solution, and sometimes there is multiple solutions and sometimes there is a single solution. ;When there is more than one solution there may be a nonsensical solution - this is ignored. ;In the event there are two realistic solutions then the earlier one is returned... ; ;Note - you should never have to call this function - if blitz had a way of describing private methods - this would be a private method. DxDx# = Dx*Dx DyDy# = Dy*Dy DzDz# = Dz*Dz DxVDx# = Dx*VDx DyVDy# = Dy*VDy DzVDz# = Dz*VDz VDxVDx# = VDx*VDx VDyVDy# = VDy*VDy VDzVDz# = VDz*VDz LL# = L*L If(LL = VDxVDx + VDyVDy + VDzVDz) ;impossible to hit.....ie will take eternity to do so.... Return -1 EndIf BB# = (DxVDx + DyVDy + DzVDz)*(DxVDx + DyVDy + DzVDz) AC# = (LL - VDxVDx - VDyVDy - VDzVDz)*(DxDx + DyDy + DzDz) If(BB+AC<0) Then Return -1 ;impossible to hit....various possible reasons.... EndIf T1# = ((DxVDx + DyVDy + DzVDz) + Sqr(BB+AC))/(LL-(VDxVDx+VDyVDy+VDzVDz)) T2# = ((DxVDx + DyVDy + DzVDz) - Sqr(BB+AC))/(LL-(VDxVDx+VDyVDy+VDzVDz)) If(T1<0) Then If(T2>0) Then Return T2 EndIf EndIf If(T2<0) Then If(T1>0) Then Return T1 EndIf EndIf If(T1<T2) Then Return T1 Return T2 End Function ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EXAMPLE 2D Function example2D() Graphics 512,512,0,2 SetBuffer BackBuffer() ;let's do some little interactive demo ...have a couple of 'dots' fly around and shoot at each other...... Local px#,py#,pvx#,pvy#,pax#,pay# Local qx#,y#,qvx#,qvy#,qax#,qay# Local spd# Local maxspd# = 4.0 ;arbitrary Local bx#,by#,bvx#,bvy# Local blife = 0 Local maxmuzzlevel# = 10.0 ;arbitrary Local startchase = False px = Rnd(128)+256 py = Rnd(128)+256 qx = Rnd(128)+256 qy = Rnd(128)+256 pvx = 0 pvy = 0 qvx = 0 qvy = 0 MoveMouse 256,256 Repeat Cls px = px + pvx py = py + pvy pvx = pvx + pax pvy = pvy + pay spd = Sqr(pvx*pvx+pvy*pvy) If(startchase = True) Then ;;;; pax = (qx - px) * 0.01 pay = (qy - py) * 0.01 EndIf If(spd>maxspd) Then pvx = pvx * maxspd/spd pvy = pvy * maxspd/spd EndIf qx = qx + qvx qy = qy + qvy qvx = qvx + qax qvy = qvy + qay spd = Sqr(qvx*qvx+qvy*qvy) If(MouseDown(1)) Then startchase = True qax = (MouseX() - qx) * 0.01 qay = (MouseY() - qy) * 0.01 EndIf If(spd>maxspd) Then qvx = qvx * maxspd/spd qvy = qvy * maxspd/spd EndIf If(bx<0 Or by<0 Or bx>511 Or by>511) Then blife = 0 If(blife>0) Then blife = blife-1 bx = bx + bvx by = by + bvy Color 255,255,0 Rect bx-1,by-1,3,3,1 Else ;see if we should shoot....and see where we should at! If(calculateMuzzleVelocity(px,py,0,pvx,pvy,0,qx,qy,0,qvx,qvy,0,maxmuzzlevel)) bx = px by = py bvx = pvx + MuzzleVelocity(0) ;VX bvy = pvy + MuzzleVelocity(1) ;VY blife = MuzzleVelocity(3) ;TIME EndIf EndIf Color 255,0,255 Rect px-1,py-1,3,3,1 Color 0,255,0 Rect qx-1,qy-1,3,3,1 Flip Until KeyHit(1) EndGraphics End Function |
Comments
| ||
Fixed a minor typo. In a check for division by zero I had added two numbers instead of multiplying. Other than that it's all good. Oh....I could probably replace one of the square roots. Currently it uses two but one of them is unnecessary. ..the two lines where T1 and T2 are calculated I should really use a variable containing the result of the square root rather than calculating the same value twice. |
| ||
Note - replace the above example with this to see a more 'interesting' example - click the screen and watch......see it in action.... |
Code Archives Forum