This code snippet shows 4 projectile functions I made that could be used by AI player in Worms-like or Scorched Earth-like games:
PRJ_Calculate_Minimum_Launch_Velocity(startx#,starty#,targetx#,targety#,accelx#,accely#)
PRJ_Calculate_Launch_Angle(startx#,starty#,targetx#,targety#,accelx#,accely#,vel#,flag)
PRJ_Calculate_Launch_Velocity_For_Time(startx#,starty#,targetx#,targety#,accelx#,accely#,time#)
PRJ_Calculate_Launch_Angle_For_Time(startx#,starty#,targetx#,targety#,accelx#,accely#,time#)
PRJ_Calculate_Minimum_Launch_Velocity() calculates the minimum amount of velocity with which a projectile much be shot in order to hit the specified target. Note that the starting position and the target don't have to be at the same height. accely# would be -9.8 or whatever you use for gravity. If you simulate wind as a constant horizontal acceleration, you can pass that in as accelx#.
PRJ_Calculate_Launch_Angle() calculates the angle the projectile must be launched at with the given velocity in order to hit the target. vel# is the launch velocity (it must be at least as large as the minimum launch velocity). The last parameter, flag, can be either 0 or 1. If flag is 0, it will return the angle that creates the higher trajectory, else it will return the angle for the lower, more direct trajectory.
The other 2 functions calculate the launch velocity and angle for a given amount of time. This would be handy if you wanted an AI to throw a grenade that takes, say, 3 seconds to explode, these functions would find the trajectory to the target that takes exactly 3 seconds.
In the example below it shows the trajectory with the minimum launch velocity (in green) as well as 10 other trajectories for 5 other launch velocities greater than the minimum. The red trajectory uses the last 2 functions, so an object launched along the path of the red trajectory would always take 14 seconds to reach the target.
Using these functions you should be able to make an AI that tries the trajectory of minimum velocity first, and if that intersects the landscape, tries increasing the velocity until it finds a working trajectory.
To compile you will need D3DFunc installed.
sync on
sync rate 0
set display mode 1024,768,32
px#=0
py#=0
accy#=-9.8
accx#=2
d3d_init
q#=0
do
cls
px#=px#+3*(rightkey()-leftkey())
py#=py#+3*(upkey()-downkey())
tx#=mousex()-screen width()/2
ty#=screen height()/2-mousey()
min#=PRJ_Calculate_Minimum_Launch_Velocity(px#,py#,tx#,ty#,accx#,accy#)
print "Launch Position: (",px#,",",py#,")"
print "Target Position: (",tx#,",",ty#,")"
print "Acceleration: (",accx#,",",accy#,")"
print "Minimum launch velocity Required to hit target: ",min#
a#=wrapvalue(PRJ_Calculate_Launch_Angle(px#,py#,tx#,ty#,accx#,accy#,min#,0))
print "Launch angle: ",a#
vel#=min#
d3d_color 0,0,255,255
gosub draw_trajectory
d3d_color 255,0,0,255
vel#=PRJ_Calculate_Launch_Velocity_For_Time(px#,py#,tx#,ty#,accx#,accy#,14.0)
a#=PRJ_Calculate_Launch_Angle_For_Time(px#,py#,tx#,ty#,accx#,accy#,14.0)
gosub draw_trajectory
vel#=min#*1.12
d3d_color 255,255,255,255
for k=1 to 5
for i=0 to 1
a#=PRJ_Calculate_Launch_Angle(px#,py#,tx#,ty#,accx#,accy#,vel#,i)
gosub draw_trajectory
next i
vel#=vel#*1.12
next k
sync
loop
draw_trajectory:
vx#=vel#*cos(a#)
vy#=vel#*sin(a#)
mx#=px#
my#=py#
lx#=mx#
ly#=my#
c=0
while (1)
inc c
if c>1000 then exit
mx#=mx#+vx#*0.05
my#=my#+vy#*0.05
vy#=vy#+accy#*0.05
vx#=vx#+accx#*0.05
d3d_line screen width()/2+mx#,screen height()/2-my#,screen width()/2+lx#,screen height()/2-ly#
lx#=mx#
ly#=my#
if (mx#<-screen height()/2 or mx#>screen width()/2 or my#<-screen height()/2 or my#>screen height()/2) and c>600 then exit
if (mx#-tx#)^2+(my#-ty#)^2<(vx#^2+vy#^2)*0.08 then exit
endwhile
d3d_line screen width()/2+mx#,screen height()/2-my#,screen width()/2+tx#,screen height()/2-ty#
return
function PRJ_Calculate_Minimum_Launch_Velocity(startx#,starty#,targetx#,targety#,accelx#,accely#)
accel#=sqrt(accelx#^2+accely#^2)
acc_nx#=accelx#/accel#
acc_ny#=accely#/accel#
dx#=targetx#-startx#
dy#=targety#-starty#
rx#=dx#*acc_ny#-dy#*acc_nx#
ry#=dy#*acc_ny#+dx#*acc_nx#
a1#=-90
a2#=atanfull(ry#,rx#)
ang#=atanfull(acc_ny#,acc_nx#)-90
a#=atanfull(sin(a1#)+sin(a2#),cos(a1#)+cos(a2#))
min_v#=sqrt(abs(-accel#*ry#+sqrt((2*accel#*ry#)^2+4*accel#^2*rx#^2)/2))
endfunction min_v#
function PRJ_Calculate_Launch_Velocity_For_Time(startx#,starty#,targetx#,targety#,accelx#,accely#,time#)
vx#=(targetx#-startx#-accelx#*time#^2/2)/time#
vy#=(targety#-starty#-accely#*time#^2/2)/time#
vel#=sqrt(vx#^2+vy#^2)
endfunction vel#
function PRJ_Calculate_Launch_Angle_For_Time(startx#,starty#,targetx#,targety#,accelx#,accely#,time#)
vx#=(targetx#-startx#-accelx#*time#^2/2)/time#
vy#=(targety#-starty#-accely#*time#^2/2)/time#
a#=atanfull(vy#,vx#)
endfunction a#
function PRJ_Calculate_Launch_Angle(startx#,starty#,targetx#,targety#,accelx#,accely#,vel#,flag)
if flag=0 then flag=-1 else flag=1
accel#=sqrt(accelx#^2+accely#^2)
acc_nx#=accelx#/accel#
acc_ny#=accely#/accel#
dx#=targetx#-startx#
dy#=targety#-starty#
rx#=dx#*acc_ny#-dy#*acc_nx#
ry#=dy#*acc_ny#+dx#*acc_nx#
a1#=-90
a2#=atanfull(ry#,rx#)
a#=atanfull(sin(a1#)+sin(a2#),cos(a1#)+cos(a2#))
b#=rx#^2*(vel#^4 - accel#^2*rx#^2 + 2*accel#*vel#^2*ry#)
if b#<0 then b#=0
a1#=atanfull(ry#*vel#^2-accel#*rx#^2,flag*sqrt(b#))
if rx#<0 then a1#=180-a1#
a2#=atanfull(ry#,rx#)
angle#=atanfull(sin(a1#)+sin(a2#),cos(a1#)+cos(a2#))
if flag<0
if rx#*cos(angle#)<0
angle#=angle#+180
endif
endif
angle#=angle#-atanfull(accelx#,accely#)
endfunction angle#