大家好,我是J哥,这个不学数学是很难算出来的。但是当我们有电脑的时候就不一样了。依靠计算机极快的计算速度,我们利用微分的思想,加上一点简单的三角学知识就可以实现。好了,废话不多说,我们来看看它的算法原理,看图:由于后面我们要用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#初始发射位置missilevelocity=800#导弹速度time=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)#弧度旋转角度screen.blit(missile,(x1-missile.get_width(),y1-missile.get_height()/2))dis_angle=d_angle-old_angle#dis_angle是需要改变到下一个位置的角度old_angle=d_angle#更新初始角度pygame.display.update()如果只把导弹当成一个粒子,那么上面的算法就够了,我没有rotatethemissile,因为一个质点不管它的头尾都不需要旋转。当然,前提是当你加载的导弹图片较小的时候,似乎问题不大。但是在pygame中旋转并不容易。我们先把图片换成矩形的,然后加个旋转函数看看效果如何Missiled=pygame.transform.rotate(missile,-(d_angle))screen.blit(missiled,(x1-missile.get_width(),y1-missile.get_height()/2))因为图片的坐标点是它的左上角的点,如果我们想让图片的坐标固定在箭头的尖端,那么图片的实际打印位置x减去图片的长度,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),所以我们最后加上相关的算法代码,效果就完美了。大功告成,最后附上所有算法代码importpygame,sysfrommathimport*pygame.init()font1=pygame.font.SysFont('microsoftyaheimmicrosoftyaheiui',23)textc=font1.render('*',True,(250,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()#获取鼠标位置,鼠标需要击中Targetdistance=sqrt(pow(x1-x,2)+pow(y1-y,2))#two-点距离公式部分=速度*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)如果-90<=-fangle<0:A=(x1-宽度+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(导弹,(x1宽度h+(x1-C[0]),y1-height/2+(y1-C[1])))screen.blit(textc,(x,y))#鼠标使用红色*代替pygame.display.update()以上是使用Python模拟导弹自动跟踪的代码示例
