基于 Python-pygame 的导弹自动追踪代码实现
自动追踪算法,其实就是解微分方程,利用微分的思想,加上一点简单的三角学知识,就可以实现它。
算法总的思想就是根据上图,把时间t分割成足够小的片段(比如1/1000,这个时间片越小越精确),每一个片段分别构造如上三角形,计算出导弹下一个时间片走的方向(即∠a)和走的路程(即vt=|AC|),这时候目标再在第二个时间片移动了位置,这时刚才计算的C点又变成了第二个时间片的初始点,这时再在第二个时间片上在C点和新的目标点构造三角形计算新的vt,然后进入第三个时间片,如此反复即可。
假定导弹和目标的初始状态下坐标分别是(x1,y1),(x,y),构造出直角三角形ABE,这个三角形用来求∠a的正弦和余弦值,因为vt是自己设置的,我们需要计算A到C点x和y坐标分别移动了多少,移动的值就是AD和CD的长度,这两个分别用vt乘cosa和sina即可。
即:
所以
AC的长度就是导弹的速度乘以时间即 |AC|=vt,然后即可计算出AD和CD的长度,于是这一个时间片过去后,导弹应该出现在新的位置C点,他的坐标就是老的点A的x增加AD和y减去CD。
于是,新的C点坐标就是:
为了更形象,把第一个时间片和第二个时间片放在一起看看:
第一个是时间片构造出的三角形是ABE,经过一个时间片后,目标从B点走到了D点,导弹此时在C点,于是构造新的三角形CDF,重复刚才的计算过程即可,图中的角∠b就是导弹需要旋转的角度,现实中只需要每个时间片修正导弹的方向就可以了,具体怎么让导弹改变方向,这就不是我们需要研究的问题了
那么实现这个过程的代码如下:
# 1.导入库
import pygame,sys
from math import * # 注:*代表导入所有的变量,即使用pi,不需要写为math.pi,直接使用pi
# 2.进行初始化
pygame.init() # 初始化模块
screen = pygame.display.set_mode((800,700),0,32) # 初始化一个准备显示的窗口或屏幕
missile = pygame.image.load('pointer.png').convert_alpha() # 加载与处理图像
x1,y1 = 100,600 # 导弹的初始发射位置
velocity = 1200 # 导弹速度
time = 1/1000 # 每个时间片的长度
clock = pygame.time.Clock() # 创建时钟对象
old_angle = 0
# 3.主程序:
while True:
for event in pygame.event.get(): # 从队列中获取事件
if event.type == pygame.QUIT: # 结束 pygame 模块
sys.exit() # 退出Python
clock.tick(60) # 每秒最多循环60次,即设置帧率为60
x,y = pygame.mouse.get_pos() # 获取鼠标位置,鼠标就是需要打击的目标,即(x,y)
distance = sqrt(pow(x-x1,2)+pow(y-y1,2)) # 两点距离公式,即|AB|
section = velocity*time # 每个时间片需要移动的距离
sina = (y1-y)/distance
cosa = (x-x1)/distance
angle = atan2(y-y1,x-x1) # 两点线段的弧度值,即返回给定的X及Y坐标值的反正切值∠a的弧度制
x1,y1 = (x1+section*cosa,y1-section*sina) # 更新初始坐标A
d_angle = degrees(angle) # 弧度转角度
screen.blit(missile, (x1 , y1 ))
dis_angle = d_angle-old_angle # dis_angle就是到下一个位置需要改变的角度,即∠b
old_angle = d_angle # 更新初始角度
pygame.display.update() # 更新整个待显示的 Surface 对象到屏幕上。
注解:
1.关于pygame.init():
我们已经知道python有一个特殊的“工具包(模块)”叫pygame了。在我们要动手用它完成我们的想法之前,电脑这个强迫症需要我们检查一遍,这个工具包是否完整,能否正常给我们提供帮助。而这个检查的动作,就是pygame.init().
2.关于pygame.display.set_mode((800,700),0,32)
set_mode(resolution=(0,0), flags=0, depth=0) -> Surface
这个函数将创建一个 Surface 对象的显示界面。传入的参数用于指定显示类型。最终创建出来的显示界面将是最大可能地匹配当前操作系统。resolution 参数是一个二元组,表示宽和高。flags 参数是附件选项的集合。depth 参数表示使用的颜色深度。
3.关于pygame.image.load(‘element/red_pointer.png’).convert_alpha()
pygame.image.load()为加载图像。最终返回Surface
使用 convert 可以转换格式,提高 blit 的速度
convert()所指的“格式”并非指文件格式(如 png,jpeg,gif),它是所谓的“像素格式”。它代表了一个surface记录一个特定像素的颜色的方法。如果surface格式跟显示格式不一样,那SDL就要在每次blit的时候去转化它——这是个相当费时的过程。不用关心解释,只要注意到如果想在blit之外获得速度,那你就需要 convert()。但是对于透明图像,效果会不同。如果想提高 blit 的速度,但是也想实现透明效果,可以使用: convert_alpha 相对于convert,保留了图像的 Alpha 通道信息,可以认为是保留了透明的部分,实现了透明转换。
其中 Alpha 通道指:
阿尔法通道是一个8位的灰度通道,该通道用256级灰度来记录图像中的透明度信息,定义透明、不透明和半透明区域,其中白表示不透明,黑表示透明,灰表示半透明。
4.关于pygame.time.Clock()
pygame.time.Clock对象帮助我们确定程序要以多少最大的帧速率运行,在这里面是先创建始终对象,再进行每秒的循环,即确定帧率。
5.关于pygame.event.get()
Pygame 模块是用于处理事件与事件队列。
get() -> Eventlist
get(type) -> Eventlist
get(typelist) -> Eventlist
这将获取并从队列中删除事件。如果指定一个或多个 type 参数,那么只获取并删除指定类型的事件。如果你只从队列中获取和删除指定的事件,那么久而久之,队列可能被你不关注的事件所填满。
6.关于sys.exit()
退出Python。这是通过引发SystemExit 异常来实现的,因此遵循finally语句的子句所指定的清理操作try ,并且可以拦截外层的退出尝试。
7.关于pygame.mouse.get_pos()
获取鼠标光标的位置。
get_pos() -> (x, y)
返回鼠标光标的坐标 (x, y)。这个坐标以窗口左上角为基准点。光标位置可以被定位于窗口之外,但是通常被强制性限制在屏幕内。
8.关于screen.blit(missile, (x1 , y1 ))
这是我们的窗口,由screen = pygame.display.set_mode((width,height))创建。其中screen是画布名称。最终,所有内容都需要绘制到此画布上,以便我们可以看到它。
screen.blit(player,(xpos,ypos))
blit()的功能是把一张图A粘贴到另一张图B上,这意味着B上的图将被A上的图覆盖,且永久不能恢复。blit()的原型是blit(source, dest, area=None),返回值为rect对象,返回被改变的画面区域。
source:一个surface对象,可以理解为一张图,就是你要复制粘贴到B上的A图片。
dest:一个可以标识坐标的东西,可以是一个(x,y)元组。也可以是一个(x,y,height,width)元组,也可以是一个Rect对象,Rect对象可以理解为有位置有大小的矩形。
area:另一个Rect对象。