自动跟踪算法,我们做射击游戏的时候经常用到。这个听起来很宏大的东西,其实并不是军事科学的专利。从数学上讲,就是求解微分方程。没有一些数学基础很难计算这个。但是当我们有电脑的时候就不一样了。依靠计算机极快的计算速度,我们利用微分的思想,加上一点简单的三角学知识就可以实现。好了,废话不多说,我们来看看它的算法原理,看图:由于后面我们要用pygame来演示,它的坐标系是y轴向下,所以这里我们也使用y向下坐标系统。算法的总体思路是按照上图把时间t分成足够小的片段(比如1/1000,时间片越小越准确),每个片段构造为上面的三角形,计算导弹的下一个时间片。方向(即∠a)和行进的距离(即vt=|AC|),此时目标在第二个时间片内移动,刚刚计算出的点C成为第二个时间片的点初始点,此时在第二个时间片中,在C点和新的目标点之间构造一个三角形,计算出一个新的vt,然后进入第三个时间片,以此类推。假设导弹和目标的初始坐标分别为(x1,y1),(x,y),构造一个直角三角形ABE,用这个三角形求∠a的正弦和余弦,因为vt是自己设定,我们需要计算A点到C点的x和y坐标移动了多少,移动的值就是AD和CD的长度,可以通过cos(a)和sin乘以vt(A)。计算sin(a)和cos(a),正弦与斜线比较,余弦与斜线相邻,斜边可以用两点距离公式计算,即AC的长度为导弹的速度乘以时间,即|AC|=vt,然后可以计算出AD和CD的长度,所以经过这个时间片后,导弹应该出现在新的C点,它的坐标是旧点A的x加上AD和y减去CD。因此,C点的新坐标为:只要重复这个操作,好吧,为了图像更生动,我们把第一个时间片和第二个时间片放在一起:第一个是时间片,构建的三角形是安倍。经过一个时间片,目标从B点走到D点,导弹在C点,所以构造一个新的三角形CDF,重复刚才的计算过程。图中的角∠b是导弹需要旋转的角度。实际上,只需要在每个时间片修正导弹的方向即可。如何改变导弹的方向并不是我们需要研究的问题。由于我们是使用Python的pygame库来制作Play小游戏,那么我们就使用pygame来演示这个效果,效果如下图所示:非常简单的代码如下:importpygame,sysfrommathimport*pygame。init()screen=pygame.display.set_mode((800,700),0,32)missile=pygame.image.load('element/red_pointer.png').convert_alpha()x1,y1=100,600#初始发射位置themissilevelocity=800#themissilespeedtime=1/1000#每个时间片的长度clock=pygame.time.Clock()old_angle=0whileTrue:foreventinpygame.event.get():ifevent.type==pygame.QUIT:sys.exit()clock.tick(300)x,y=pygame.mouse.get_pos()#获取鼠标位置,鼠标为需要击中的目标distance=sqrt(pow(x1-x,2)+pow(y1-y,2))#两点距离公式section=velocity*time#每个时间片移动的距离sina=(y1-y)/distancecosa=(x-x1)/distanceangle=atan2(y-y1,x-x1)#两点线段x1的弧度值,y1=(x1+section*cosa,y1-section*sina)d_angle=degrees(angle)#radiananglescreen.blit(导弹,(x1-missile.get_width(),y1-missile.get_height()/2))dis_angle=d_angle-old_angle#dis_angle是下一个位置需要改变的角度。old_angle=d_angle#更新初始角度Rotation,因为质点不需要旋转。当然,前提是当你加载的导弹图片较小的时候,似乎问题不大。但是在pygame中旋转并不容易。我们先把图片换成矩形,然后加上旋转功能看看效果如何Missiled=pygame.transform.rotate(missile,-(d_angle))screen.blit(missiled,(x1-missile.get_width(),y1-missile.get_height()/2))因为图片的坐标点是它的左上角点,如果我们想让图片的坐标固定在箭头的尖端,那么实际打印的位置x图片的of减少了图片的长度,y的宽度减少了一半就可以了。但是实际运行效果并不好:大体方向是一致的,但是图片箭头的尖点并不一直跟随鼠标,这是为什么。研究了一下,发现是图片旋转机制的问题。来看看旋转后的图片长什么样子:旋转后的图片变成了蓝色范围,根据旋转角度的不同,变化出来的图片大小也不同。我们再看旋转90的情况,发现不仅旋转后的画面面积变大了,导弹头的位置也发生了变化。那么我们应该如何解决这个问题呢?思路是,图像每次旋转后,找到旋转图像的头部位置(图中绿色箭头点),然后移动绿色图像的打印位置,向下,x,y移动分别计算两个导弹头的距离,使得旋转后的导弹头与我们参与计算的导弹头的实际位置对齐,移动后应该是这样的:这样两个导弹头的点是一致的。接下来,我们分析寻找旋转导弹头部的算法。根据旋转角度的不同,旋转角度的参数在不同的象限是不同的,所以我们将它们分为四种情况:1、2象限、3、4象限。它的旋转只是正负0-180度,所以3、4象限都是负角。在显示图片的时候,我们把它移动到screen.blit(missiled,(x1-width+(x1-C[0]),y1-height/2+(y1-C[1])))where(x1-width,y1-height/2)其实就是上图中的(x1,y1),所以我们最后加上相关的算法代码,效果就完美了。大功告成,最后附上所有算法代码0,0))screen=pygame.display.set_mode((800,700),0,32)missile=pygame.image.load('element/rect1.png').convert_alpha()height=missile.get_height()width=missile.get_width()pygame.mouse.set_visible(0)x1,y1=100,600#初始导弹发射位置velocity=800#导弹速度time=1/1000#每个时间片的长度clock=pygame.time.Clock()A=()B=()C=()whileTrue:foreventinpygame.event.get():ifevent.type==pygame.QUIT:sys.exit()clock.tick(300)x,y=pygame.mouse。get_pos()#获取鼠标位置,鼠标为命中目标distance=sqrt(pow(x1-x,2)+pow(y1-y,2))#两点距离公式section=velocity*time#每个时间片需要移动的距离sina=(y1-y)/distancecosa=(x-x1)/distanceangle=atan2(y-y1,x-x1)#两点间线段的弧度值fangle=degrees(angle)#弧度转角x1,y1=(x1+section*cosa,y1-section*sina)missiled=pygame.transform.rotate(missile,-(fangle))if0<=-fangle<=90:A=(width*cosa+x1-width,y1-height/2)B=(A[0]+height*sina,A[1]+height*cosa)if90<-fangle<=180:A=(x1-width,y1-height/2+height*(-cosa))B=(x1-width+height*sina,y1-height/2)if-90<=-fangle<0:A=(x1-width+missiled.get_width(),y1-height/2+missiled.get_height()-height*cosa)B=(A[0]+height*sina,y1-height/2+missiled.get_height())if-180<-fangle<-90:A=(x1-width-height*sina,y1-height/2+missiled.get_height())B=(x1-width,A[1]+height*cosa)C=((A[0]+B[0])/2,(A[1]+B[1])/2)screen.fill((0,0,0))screen.blit(missiled,(x1-width+(x1-C[0]),y1-height/2+(y1-C[1])))screen.blit(textc,(x,y))#鼠标使用红色*代替pygame.display.update()上面是用Python模拟导弹自动跟踪的代码示例
