Python制作流浪气球游戏(导弹射击类)

  • 教学课程代码(分步教学版)
    • 1、构建全局通用代码结构
    • 2、构建气球精灵类
    • 3、构建导弹精灵类
    • 4、碰撞检测
    • 5、构建游戏信息类 (最终完整代码)

教学课程代码(分步教学版)

实时同步文件:Python制作流浪气球游戏(射击类)
B站视频教学地址:浪淘三千
(代码和素材是完整的,对应视频也已更新完毕)
以下是静态样式展示:

1、构建全局通用代码结构

"""
本游戏制作了导弹拦截流浪气球的基本代码 有 音效、音乐、精灵、碰撞、轨迹计算 等知识的应用
当基础功能学会以后,可以教大家平面地图文件的绘制和使用 制作更丰富精彩的游戏场景
视频教学地址,会陆续更新 https://space.bilibili.com/455954948
欢迎积极交流更好的改进建议,一起升级游戏   (代码已同步 链接见视频评论区)
"""
import arcade# 常量 窗口大小和标题
SCREEN_WIDTH = 1200
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Arcade 游戏教学 流浪气球"# arcade_game_202302 流浪气球Ⅰ 教学版
class LiuLangQiQiu(arcade.View):""" 视图程序,用于在窗体内展示. """def __init__(self):# 初始化父类的属性super().__init__()self.background_image = None# 鼠标的图片self.couser_pic = None# 游戏背景音乐self.background_sound = arcade.load_sound("../声音文件/忍者神龟背景音乐.mp3")# 在初始化时调用的一些准备工作代码self.set_up()def set_up(self):""" 进行一些游戏准备工作,让游戏主逻辑从这开始,以便于在有需要时重开游戏. """# 背景图片 当作精灵加载进来self.background_image = arcade.Sprite("../图片文件/世界卫星地图.png")# 播放并记录游戏背景音乐self.current_play = self.background_sound.play(volume=0.4, loop=True)# 设置精灵素材中心点的位置self.background_image.center_x = self.window.width // 3self.background_image.center_y = self.window.height // 2# 鼠标图片加载self.couser_pic = arcade.Sprite('../图片文件/瞄准.png', scale=0.2)# 画面渲染绘制def on_draw(self):""" 负责渲染画面 速率: 60帧/秒. """# 清除上一帧绘制的画面self.clear()# 绘制背景图片self.background_image.draw()# 绘制鼠标self.couser_pic.draw()# 控制每次刷新时的变化def on_update(self, delta_time: float):""" 负责逻辑变化 速率: 60帧/秒. """passdef on_mouse_press(self, x, y, button, modifiers):""" 监听鼠标点击事件. """mouse_buttons = {1: "左键", 2: "中键", 4: "右键"}print(f"当前点击的坐标是{x,y}")def on_mouse_release(self, x: float, y: float, button: int, modifiers: int):""" 监听鼠标释放事件. """passdef on_mouse_motion(self, x: float, y: float, dx: float, dy: float):""" 监听鼠标移动事件 """self.couser_pic.center_x = xself.couser_pic.center_y = ydef on_key_press(self,key, modifiers):"""监听键盘按键点击事件modifiers:None = 16,shift= 1(17) ctrl= 2(18) alt= 4(20) """print("---键盘按键按下了:--",key,"--修饰键编号的和是:---", modifiers)def on_key_release(self, key, modifiers):print("---键盘按键抬起了:--",key,"--剩余修饰键编号的和是:---", modifiers)def on_resize(self, width: int, height: int):# 重新设置精灵素材中心点的位置self.background_image.center_x = self.window.width // 2self.background_image.center_y = self.window.height // 2# 重新设置精灵素材宽度和高度self.background_image.width = self.window.widthself.background_image.height = self.window.heightdef main():# 设置窗体的宽、高、名字、是否支持缩放window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True)# 设置窗体的 logo 图标import pygletwindow.set_icon(pyglet.image.load('../图片文件/浪淘三千.png'))# 设置鼠标形状cursor = window.get_system_mouse_cursor(window.CURSOR_HAND)window.set_mouse_cursor(cursor)# 实例化定义的某个窗体start_view = LiuLangQiQiu()# 设置当前应该显示哪个窗体window.show_view(start_view)# 保持程序持续运行arcade.run()if __name__ == "__main__":main()

2、构建气球精灵类

"""
本游戏制作了导弹拦截流浪气球的基本代码 有 音效、音乐、精灵、碰撞、轨迹计算 等知识的应用
当基础功能学会以后,可以教大家平面地图文件的绘制和使用 制作更丰富精彩的游戏场景
视频教学地址,会陆续更新 https://space.bilibili.com/455954948
欢迎积极交流更好的改进建议,一起升级游戏   (代码已同步 链接见视频评论区)
"""
import arcade
import os,random# 常量 窗口大小和标题
SCREEN_WIDTH = 1200
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Arcade 游戏教学 流浪气球"class QiQiu(arcade.Sprite):def __init__(self, filename, change_x, change_y, init_x, init_y):super(QiQiu, self).__init__(filename,hit_box_algorithm = "Simple")# 导弹精灵的飞行变化速度self.change_x = change_xself.change_y = change_y# 导弹精灵的初始中心点(init_x,init_y)self.center_x = init_xself.center_y = init_y# 记录气球单次持续垂直移动的距离self.total_change_y = 0# 是否被追踪 一旦被追踪则不再被别的导弹追踪self.is_tracked = False# 初始中心点不在界面左侧,则是从右向左飞行的,因为原始图片都朝向右侧,向左飞行的要水平翻转if self.center_x > 0:self.append_texture((arcade.load_texture(filename, mirrored=True)))# texture_right_forward = arcade.load_texture(filename, mirrored=True)self.set_texture(1)def update(self, window_height=800):self.total_change_y += self.change_y# 当单次垂直移动距离超过50就重新随机选择方向 这个数字通过自己测试调整得到if abs(self.total_change_y) > 50:# 随机改变气球纵向移动方向self.change_y *= random.choice([-1, 1])# 再次重新计算持续垂直移动的距离self.total_change_y = 0if self.center_y > window_height- self.height or self.center_y < self.height:self.change_y = 0self.total_change_y = 0self.center_x += self.change_xself.center_y += self.change_y# arcade_game_202302 流浪气球Ⅰ教学版
class LiuLangQiQiu(arcade.View):""" 视图程序,用于在窗体内展示. """def __init__(self):# 初始化父类的属性super().__init__()self.background_image = None# 气球类self.qi_qiu = Noneself.qi_qiu_list = None# 所有气球文件self.all_qi_qiu = None# 游戏背景音乐self.background_sound = arcade.load_sound("../声音文件/忍者神龟背景音乐.mp3")# 鼠标的图片self.couser_pic = None# 在初始化时调用的一些准备工作代码self.set_up()def set_up(self,init_qi_qiu_num=3):""" 进行一些游戏准备工作,让游戏主逻辑从这开始,以便于在有需要时重开游戏. """# 背景图片 当作精灵加载进来self.background_image = arcade.Sprite("../图片文件/世界卫星地图.png")# 播放并记录游戏背景音乐self.current_play = self.background_sound.play(volume=0.4, loop=True)# 气球精灵列表self.qi_qiu_list = arcade.SpriteList()# 获取文件夹中所有的飞艇图片self.all_qi_qiu = os.listdir("../图片文件/气球飞艇")print(self.all_qi_qiu)# 设置精灵素材中心点的位置self.background_image.center_x = self.window.width // 3self.background_image.center_y = self.window.height // 2# 初始状态时创建自定义个气球(默认3个)self.creat_qi_qiu(init_qi_qiu_num)self.couser_pic = arcade.Sprite('../图片文件/瞄准.png', scale=0.2)def creat_qi_qiu(self, num):"""创建气球"""for i in range(1, num+1):self.qi_qiu = QiQiu(filename=f"../图片文件/气球飞艇/{random.choice(self.all_qi_qiu)}",change_x=random.choice([0.5,0.5,1,2,2]), change_y=random.choice([1,2]),init_x=random.choice([-80, self.window.width + 200]),init_y=random.randint(200, self.window.height - num*50))self.qi_qiu.width = 60self.qi_qiu.height = 40# self.qi_qiu.scale = 0.5if self.qi_qiu.center_x > self.window.width//2:self.qi_qiu.change_x = -self.qi_qiu.change_xself.qi_qiu_list.append(self.qi_qiu)# 画面渲染绘制def on_draw(self):""" 负责渲染画面 速率: 60帧/秒. """# 清除上一帧绘制的画面self.clear()# 绘制背景图片self.background_image.draw()# 绘制气球精灵组self.qi_qiu_list.draw()# 绘制鼠标self.couser_pic.draw()# 控制每次刷新时的变化def on_update(self, delta_time: float):""" 负责逻辑变化 速率: 60帧/秒. """for qi_qiu_id, qi_qiu in enumerate(self.qi_qiu_list):# 气球移动qi_qiu.update(self.window.height)if qi_qiu.center_x not in range(-230, self.window.width+230):# 气球飞出屏幕一定距离后 则消失qi_qiu.remove_from_sprite_lists()# 再次创建气球self.creat_qi_qiu(1)def on_mouse_press(self, x, y, button, modifiers):""" 监听鼠标点击事件. """mouse_buttons = {1: "左键", 2: "中键", 4: "右键"}print(f"当前点击的坐标是{x,y}")def on_mouse_release(self, x: float, y: float, button: int, modifiers: int):""" 监听鼠标释放事件. """passdef on_mouse_motion(self, x: float, y: float, dx: float, dy: float):""" 监听鼠标移动事件 """self.couser_pic.center_x = xself.couser_pic.center_y = ydef on_key_press(self,key, modifiers):"""监听键盘按键点击事件modifiers:None = 16,shift= 1(17) ctrl= 2(18) alt= 4(20) """print("---键盘按键按下了:--",key,"--修饰键编号的和是:---", modifiers)def on_key_release(self, key, modifiers):print("---键盘按键抬起了:--",key,"--剩余修饰键编号的和是:---", modifiers)def on_resize(self, width: int, height: int):# 重新设置精灵素材中心点的位置self.background_image.center_x = self.window.width // 2self.background_image.center_y = self.window.height // 2# 重新设置精灵素材宽度和高度self.background_image.width = self.window.widthself.background_image.height = self.window.heightdef main():# 设置窗体的宽、高、名字、是否支持缩放window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True)# 设置窗体的 logo 图标import pygletwindow.set_icon(pyglet.image.load('../图片文件/浪淘三千.png'))# 设置鼠标形状cursor = window.get_system_mouse_cursor(window.CURSOR_HAND)window.set_mouse_cursor(cursor)# 实例化定义的某个窗体start_view = LiuLangQiQiu()# 设置当前应该显示哪个窗体window.show_view(start_view)# 保持程序持续运行arcade.run()if __name__ == "__main__":main()

3、构建导弹精灵类

"""
本游戏制作了导弹拦截流浪气球的基本代码 有 音效、音乐、精灵、碰撞、轨迹计算 等知识的应用
当基础功能学会以后,可以教大家平面地图文件的绘制和使用 制作更丰富精彩的游戏场景
视频教学地址,会陆续更新 https://space.bilibili.com/455954948
欢迎积极交流更好的改进建议,一起升级游戏   (代码已同步 链接见视频评论区)
"""
import arcade
import os,random,math# 常量 窗口大小和标题
SCREEN_WIDTH = 1200
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Arcade 游戏教学 流浪气球"class QiQiu(arcade.Sprite):def __init__(self, filename, change_x, change_y, init_x, init_y):super(QiQiu, self).__init__(filename,hit_box_algorithm = "Simple")# 导弹精灵的飞行变化速度self.change_x = change_xself.change_y = change_y# 导弹精灵的初始中心点(init_x,init_y)self.center_x = init_xself.center_y = init_y# 记录气球单次持续垂直移动的距离self.total_change_y = 0# 是否被追踪 一旦被追踪则不再被别的导弹追踪self.is_tracked = False# 初始中心点不在界面左侧,则是从右向左飞行的,因为原始图片都朝向右侧,向左飞行的要水平翻转if self.center_x > 0:self.append_texture((arcade.load_texture(filename, mirrored=True)))# texture_right_forward = arcade.load_texture(filename, mirrored=True)self.set_texture(1)def update(self, window_height=800):self.total_change_y += self.change_y# 当单次垂直移动距离超过50就重新随机选择方向 这个数字通过自己测试调整得到if abs(self.total_change_y) > 50:# 随机改变气球纵向移动方向self.change_y *= random.choice([-1, 1])# 再次重新计算持续垂直移动的距离self.total_change_y = 0if self.center_y > window_height- self.height or self.center_y < self.height:self.change_y = 0self.total_change_y = 0self.center_x += self.change_xself.center_y += self.change_yclass DaoDan(arcade.Sprite):"""目前是设计成了自动搜寻目标 然后攻击的模式"""def __init__(self, filename, change_x, change_y, init_x, init_y, target_x=None, target_y=None):super(DaoDan, self).__init__(filename,hit_box_algorithm = "Simple")# self.load_animated_gif(filename)# 导弹精灵的飞行变化速度self.change_x = change_xself.change_y = change_yself.change_y_init = change_y# 记录导弹精灵的初始点self.init_x = init_xself.init_y = init_y# 导弹精灵的初始中心点(init_x,init_y)self.center_x = init_xself.center_y = init_y# 要追踪的目标self.target_x = target_xself.target_y = target_y# 导弹是否还在地面上(没有发射)self.is_on_ground = True# 是否正在追踪 初始是未开始追踪self.is_tracking = False# 导弹点火发射音效self.fire_sound = arcade.load_sound("../声音文件/火焰推进器.mp3")# 导弹爆炸音效self.explode_sound = arcade.load_sound("../声音文件/中距离爆炸.mp3")def track_target(self, target_x, target_y, change_angle=0.1):# 当被调用的时候,开始追踪目标气球# 计算导弹和气球飞艇之间的弧度值 Π = 3.14(弧度) = 180°(角度) 向左转角度要加 向右转角度要减target_radians_angle = math.atan2(target_y-self.center_y,target_x-self.center_x)# 将上一步计算出的弧度转换为角度值# 都要减去90 因为导弹的初始方向是向上的,所以有一个默认的90°角,减去后恢复到 0°再计算。target_degree_angle = math.degrees(target_radians_angle)-90# print(f"角度是:{target_degree_angle}")self.center_x += self.change_x if self.center_x < target_x else -self.change_xself.center_y += self.change_y if self.center_y < target_y else -self.change_yself.change_y = 0 if abs(self.center_y - target_y) < 5 else self.change_y_init# print("2222", self.center_y, target_y, self.change_y)if abs(target_degree_angle) in range(0,8):self.angle = 0elif abs(target_degree_angle) in range(172,188):self.angle = 180else:self.angle = target_degree_angle# 定义一个弹道导弹类 继承导弹基类 ,因为追踪逻辑不一样,所以需要重写其追踪方法
class DanDaoDaoDan(DaoDan):# def __init__(self):#     super().__init__()def track_target(self):"""开始计算固定飞行轨迹 y = a(x-h)²+k  h 和 k 为抛物线的顶点横纵坐标将鼠标点击的(self.target_x,self.target_y)点作为顶点,则有【抛物线函数 y = a(x-self.target_x)²+self.target_y 】将导弹的起点坐标带入上述方程 可以求出 a 值 于是就可以求出抛物线的函数式【抛物线函数 init_y = a(init_x-self.target_x)²+self.target_y 】a = (init_y - self.target_y)/(init_x-self.target_x)²"""# 如果开口向下,a小于0;开口向上,a大于0.a = (self.init_y - self.target_y)/(self.init_x-self.target_x)**2# print(f"a的值是:{a}")self.center_x += self.change_x# 将横坐标带入方程求纵坐标self.center_y = a*(int(self.center_x - self.target_x)**2)+self.target_y# print(f"{self.center_y} = {a}*({int(self.center_x - self.target_x)})**2+{self.target_y}")# 计算导弹和气球飞艇之间的弧度值 Π = 3.14(弧度) = 180°(角度)target_radians_angle = math.atan2(self.target_y-self.center_y,self.target_x-self.center_x)# 将上一步计算出的弧度转换为角度值# 都要减去90 因为导弹的初始方向是向上的,所以有一个默认的90°角,减去后恢复到 0°再计算。target_degree_angle = math.degrees(target_radians_angle)-90if (self.target_x-self.init_x)*(self.target_x-self.center_x) < 0:# 值小于0 的时候说明导弹飞行中已经超过了设定好的最高点target_degree_angle = target_degree_angle + 180# print(f"角度是:{target_degree_angle}")if abs(target_degree_angle) in range(0,8):self.angle = 0elif abs(target_degree_angle) in range(172,188):self.angle = 180else:self.angle = target_degree_angleclass BaoZhaYanWu(arcade.Sprite):def __init__(self):super(BaoZhaYanWu, self).__init__()# 初始中心点不在界面左侧,则是从右向左飞行的,因为原始图片都朝向右侧,向左飞行的要水平翻转all_explore_pic = os.listdir("../图片文件/images11")for pic_name in all_explore_pic:self.append_texture((arcade.load_texture(f"../图片文件/images11/{pic_name}")))# 记下一共有多少纹理图self.textures_num_len = len(all_explore_pic)self.texture_index_now = 0def update(self, texture_index: int = None):# 如果状态是 True 则开始计算绘制位置if self.texture_index_now < self.textures_num_len:self.set_texture(self.texture_index_now)self.texture_index_now += 1return Falseelse:# 收到为True的返回值时 销毁烟雾精灵return True# arcade_game_202302 流浪气球Ⅰ教学版
class LiuLangQiQiu(arcade.View):""" 视图程序,用于在窗体内展示. """def __init__(self):# 初始化父类的属性super().__init__()self.background_image = Noneself.dao_dan = None# 气球类self.qi_qiu = Noneself.qi_qiu_list = None# 导弹类self.dao_dan = Noneself.dao_dan_list = None# 弹道导弹类self.dan_dao_dao_dan = Noneself.dan_dao_dao_dan_list = None# 所有气球文件self.all_qi_qiu = None# 游戏背景音乐self.background_sound = arcade.load_sound("../声音文件/忍者神龟背景音乐.mp3")# 导弹发射车self.dao_dan_che_image = None# 爆炸烟雾self.explode_smoke = Noneself.explode_smoke_list = None# 鼠标的图片self.couser_pic = None# 在初始化时调用的一些准备工作代码self.set_up()def set_up(self,init_qi_qiu_num=3):""" 进行一些游戏准备工作,让游戏主逻辑从这开始,以便于在有需要时重开游戏. """# 背景图片 当作精灵加载进来self.background_image = arcade.Sprite("../图片文件/世界卫星地图.png")# 游戏背景音乐self.current_play = self.background_sound.play(volume=0.4, loop=True)# 气球精灵列表self.qi_qiu_list = arcade.SpriteList()# 导弹精灵列表self.dao_dan_list = arcade.SpriteList()# 弹道导弹精灵列表self.dan_dao_dao_dan_list = arcade.SpriteList()# 爆炸烟雾精灵self.explode_smoke_list = arcade.SpriteList()# 获取文件夹中所有的飞艇图片self.all_qi_qiu = os.listdir("../图片文件/气球飞艇")print(self.all_qi_qiu)# 设置精灵素材中心点的位置self.background_image.center_x = self.window.width // 3self.background_image.center_y = self.window.height // 2# 导弹发射车 在on_resize()里设置绘制位置self.dao_dan_che_image = arcade.Sprite("../图片文件/东风41.png")self.dao_dan_che_image.width = 70self.dao_dan_che_image.height = 70# 初始状态时创建自定义个气球(默认3个)self.creat_qi_qiu(init_qi_qiu_num)self.couser_pic = arcade.Sprite('../图片文件/瞄准.png', scale=0.2)def creat_qi_qiu(self, num):"""创建气球"""for i in range(1, num+1):self.qi_qiu = QiQiu(filename=f"../图片文件/气球飞艇/{random.choice(self.all_qi_qiu)}",change_x=random.choice([0.5,0.5,1,2,2]), change_y=random.choice([1,2]),init_x=random.choice([-80, self.window.width + 200]),init_y=random.randint(200, self.window.height - num*50))self.qi_qiu.width = 60self.qi_qiu.height = 40# self.qi_qiu.scale = 0.5if self.qi_qiu.center_x > self.window.width//2:self.qi_qiu.change_x = -self.qi_qiu.change_xself.qi_qiu_list.append(self.qi_qiu)def creat_zhui_zong_dao_dan(self):dao_dan = DaoDan("../图片文件/导弹.gif", change_x=4, change_y=3,init_x=self.window.width // 3, init_y=self.window.height // 2)self.dao_dan_list.append(dao_dan)dao_dan.fire_sound.play()def creat_dan_dao_dao_dan(self,x,y):# 弹道导弹  如果剩余的弹道弹数量大于0 就接受命令进行创建init_x = self.window.width // 3init_y = self.window.height // 2speed = 1 if abs(x - init_x) < 200 else 3# 如果目标点在右侧 则导弹向右飞行 横坐标变化为正数 否则为负数change_x = speed if x > self.window.width // 3 else - speeddan_dao_dao_dan = DanDaoDaoDan("../图片文件/弹道导弹.png", change_x=change_x, change_y=3,init_x=init_x, init_y=init_y,target_x=x, target_y=y)dan_dao_dao_dan.scale = 0.7self.dan_dao_dao_dan_list.append(dan_dao_dao_dan)dan_dao_dao_dan.fire_sound.play()def creat_yan_wu(self,center_x, center_y):yan_wu = BaoZhaYanWu()yan_wu.center_x = center_xyan_wu.center_y = center_yself.explode_smoke_list.append(yan_wu)# 画面渲染绘制def on_draw(self):""" 负责渲染画面 速率: 60帧/秒. """# 清除上一帧绘制的画面self.clear()# 绘制背景图片self.background_image.draw()# 导弹发射车self.dao_dan_che_image.draw()# 绘制气球精灵组self.qi_qiu_list.draw()# 绘制导弹精灵组self.dao_dan_list.draw()# 绘制弹道导弹精灵组self.dan_dao_dao_dan_list.draw()# 绘制爆炸烟雾self.explode_smoke_list.draw()# 绘制鼠标self.couser_pic.draw()# 控制每次刷新时的变化def on_update(self, delta_time: float):""" 负责逻辑变化 速率: 60帧/秒. """for qi_qiu_id, qi_qiu in enumerate(self.qi_qiu_list):# 处理气球和自动追踪导弹的移动qi_qiu.update(self.window.height)if qi_qiu_id <= len(self.dao_dan_list)-1:dao_dan = self.dao_dan_list[qi_qiu_id]dao_dan.track_target(qi_qiu.center_x, qi_qiu.center_y, change_angle=-0.5)# else:#     print("请尽快发射导弹")if qi_qiu.center_x not in range(-230, self.window.width+230):# 气球飞出屏幕一定距离后 则消失qi_qiu.remove_from_sprite_lists()# 再次创建气球self.creat_qi_qiu(1)# 处理按下鼠标发射的弹道导弹for dan_dao_dao_dan in self.dan_dao_dao_dan_list:# 处理气球和弹道导弹的移动dan_dao_dao_dan.track_target()# 如果导弹飞出了屏幕 则销毁if dan_dao_dao_dan.center_x not in range(-230, self.window.width+230):dan_dao_dao_dan.remove_from_sprite_lists()def on_mouse_press(self, x, y, button, modifiers):""" 监听鼠标点击事件. """mouse_buttons = {1: "左键", 2: "中键", 4: "右键"}# print(f"当前点击的坐标是{x,y}")# 创建弹道导弹self.creat_dan_dao_dao_dan(x,y)def on_mouse_release(self, x: float, y: float, button: int, modifiers: int):""" 监听鼠标释放事件. """passdef on_mouse_motion(self, x: float, y: float, dx: float, dy: float):""" 监听鼠标移动事件 """self.couser_pic.center_x = xself.couser_pic.center_y = ydef on_key_press(self,key, modifiers):"""监听键盘按键点击事件modifiers:None = 16,shift= 1(17) ctrl= 2(18) alt= 4(20) """print("---键盘按键按下了:--",key,"--修饰键编号的和是:---", modifiers)if key == arcade.key.SPACE:self.creat_zhui_zong_dao_dan()def on_key_release(self, key, modifiers):print("---键盘按键抬起了:--",key,"--剩余修饰键编号的和是:---", modifiers)def on_resize(self, width: int, height: int):# 重新设置精灵素材中心点的位置self.background_image.center_x = self.window.width // 2self.background_image.center_y = self.window.height // 2# 重新设置精灵素材宽度和高度self.background_image.width = self.window.widthself.background_image.height = self.window.height# 设置导弹车精灵素材中心点的位置self.dao_dan_che_image.center_x = self.window.width // 3 - 30self.dao_dan_che_image.center_y = self.window.height // 2def main():# 设置窗体的宽、高、名字、是否支持缩放window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True)# 设置窗体的 logo 图标import pygletwindow.set_icon(pyglet.image.load('../图片文件/浪淘三千.png'))# 设置鼠标形状cursor = window.get_system_mouse_cursor(window.CURSOR_HAND)window.set_mouse_cursor(cursor)# 实例化定义的某个窗体start_view = LiuLangQiQiu()# 设置当前应该显示哪个窗体window.show_view(start_view)# 保持程序持续运行arcade.run()if __name__ == "__main__":main()

4、碰撞检测

"""
本游戏制作了导弹拦截流浪气球的基本代码 有 音效、音乐、精灵、碰撞、轨迹计算 等知识的应用
当基础功能学会以后,可以教大家平面地图文件的绘制和使用 制作更丰富精彩的游戏场景
视频教学地址,会陆续更新 https://space.bilibili.com/455954948
欢迎积极交流更好的改进建议,一起升级游戏   (代码已同步 链接见视频评论区)
"""
import arcade
import os,random,math# 常量 窗口大小和标题
SCREEN_WIDTH = 1200
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Arcade 游戏教学 流浪气球"class QiQiu(arcade.Sprite):def __init__(self, filename, change_x, change_y, init_x, init_y):super(QiQiu, self).__init__(filename,hit_box_algorithm = "Simple")# 导弹精灵的飞行变化速度self.change_x = change_xself.change_y = change_y# 导弹精灵的初始中心点(init_x,init_y)self.center_x = init_xself.center_y = init_y# 记录气球单次持续垂直移动的距离self.total_change_y = 0# 是否被追踪 一旦被追踪则不再被别的导弹追踪self.is_tracked = False# 初始中心点不在界面左侧,则是从右向左飞行的,因为原始图片都朝向右侧,向左飞行的要水平翻转if self.center_x > 0:self.append_texture((arcade.load_texture(filename, mirrored=True)))# texture_right_forward = arcade.load_texture(filename, mirrored=True)self.set_texture(1)def update(self, window_height=800):self.total_change_y += self.change_y# 当单次垂直移动距离超过50就重新随机选择方向 这个数字通过自己测试调整得到if abs(self.total_change_y) > 50:# 随机改变气球纵向移动方向self.change_y *= random.choice([-1, 1])# 再次重新计算持续垂直移动的距离self.total_change_y = 0if self.center_y > window_height- self.height or self.center_y < self.height:self.change_y = 0self.total_change_y = 0self.center_x += self.change_xself.center_y += self.change_yclass DaoDan(arcade.Sprite):"""目前是设计成了自动搜寻目标 然后攻击的模式"""def __init__(self, filename, change_x, change_y, init_x, init_y, target_x=None, target_y=None):super(DaoDan, self).__init__(filename,hit_box_algorithm = "Simple")# self.load_animated_gif(filename)# 导弹精灵的飞行变化速度self.change_x = change_xself.change_y = change_yself.change_y_init = change_y# 记录导弹精灵的初始点self.init_x = init_xself.init_y = init_y# 导弹精灵的初始中心点(init_x,init_y)self.center_x = init_xself.center_y = init_y# 要追踪的目标self.target_x = target_xself.target_y = target_y# 导弹是否还在地面上(没有发射)self.is_on_ground = True# 是否正在追踪 初始是未开始追踪self.is_tracking = False# 导弹点火发射音效self.fire_sound = arcade.load_sound("../声音文件/火焰推进器.mp3")# 导弹爆炸音效self.explode_sound = arcade.load_sound("../声音文件/中距离爆炸.mp3")def track_target(self, target_x, target_y, change_angle=0.1):# 当被调用的时候,开始追踪目标气球# 计算导弹和气球飞艇之间的弧度值 Π = 3.14(弧度) = 180°(角度) 向左转角度要加 向右转角度要减target_radians_angle = math.atan2(target_y-self.center_y,target_x-self.center_x)# 将上一步计算出的弧度转换为角度值# 都要减去90 因为导弹的初始方向是向上的,所以有一个默认的90°角,减去后恢复到 0°再计算。target_degree_angle = math.degrees(target_radians_angle)-90# print(f"角度是:{target_degree_angle}")self.center_x += self.change_x if self.center_x < target_x else -self.change_xself.center_y += self.change_y if self.center_y < target_y else -self.change_yself.change_y = 0 if abs(self.center_y - target_y) < 5 else self.change_y_init# print("2222", self.center_y, target_y, self.change_y)if abs(target_degree_angle) in range(0,8):self.angle = 0elif abs(target_degree_angle) in range(172,188):self.angle = 180else:self.angle = target_degree_angle# 定义一个弹道导弹类 继承导弹基类 ,因为追踪逻辑不一样,所以需要重写其追踪方法
class DanDaoDaoDan(DaoDan):# def __init__(self):#     super().__init__()def track_target(self):"""开始计算固定飞行轨迹 y = a(x-h)²+k  h 和 k 为抛物线的顶点横纵坐标将鼠标点击的(self.target_x,self.target_y)点作为顶点,则有【抛物线函数 y = a(x-self.target_x)²+self.target_y 】将导弹的起点坐标带入上述方程 可以求出 a 值 于是就可以求出抛物线的函数式【抛物线函数 init_y = a(init_x-self.target_x)²+self.target_y 】a = (init_y - self.target_y)/(init_x-self.target_x)²"""# 如果开口向下,a小于0;开口向上,a大于0.a = (self.init_y - self.target_y)/(self.init_x-self.target_x)**2# print(f"a的值是:{a}")self.center_x += self.change_x# 将横坐标带入方程求纵坐标self.center_y = a*(int(self.center_x - self.target_x)**2)+self.target_y# print(f"{self.center_y} = {a}*({int(self.center_x - self.target_x)})**2+{self.target_y}")# 计算导弹和气球飞艇之间的弧度值 Π = 3.14(弧度) = 180°(角度)target_radians_angle = math.atan2(self.target_y-self.center_y,self.target_x-self.center_x)# 将上一步计算出的弧度转换为角度值# 都要减去90 因为导弹的初始方向是向上的,所以有一个默认的90°角,减去后恢复到 0°再计算。target_degree_angle = math.degrees(target_radians_angle)-90if (self.target_x-self.init_x)*(self.target_x-self.center_x) < 0:# 值小于0 的时候说明导弹飞行中已经超过了设定好的最高点target_degree_angle = target_degree_angle + 180# print(f"角度是:{target_degree_angle}")if abs(target_degree_angle) in range(0,8):self.angle = 0elif abs(target_degree_angle) in range(172,188):self.angle = 180else:self.angle = target_degree_angleclass BaoZhaYanWu(arcade.Sprite):def __init__(self):super(BaoZhaYanWu, self).__init__()# 初始中心点不在界面左侧,则是从右向左飞行的,因为原始图片都朝向右侧,向左飞行的要水平翻转all_explore_pic = os.listdir("../图片文件/images11")for pic_name in all_explore_pic:self.append_texture((arcade.load_texture(f"../图片文件/images11/{pic_name}")))# 记下一共有多少纹理图self.textures_num_len = len(all_explore_pic)self.texture_index_now = 0def update(self, texture_index: int = None):# 如果状态是 True 则开始计算绘制位置if self.texture_index_now < self.textures_num_len:self.set_texture(self.texture_index_now)self.texture_index_now += 1return Falseelse:# 收到为True的返回值时 销毁烟雾精灵return True# arcade_game_202302 流浪气球Ⅰ教学版
class LiuLangQiQiu(arcade.View):""" 视图程序,用于在窗体内展示. """def __init__(self):# 初始化父类的属性super().__init__()self.background_image = Noneself.dao_dan = None# 气球类self.qi_qiu = Noneself.qi_qiu_list = None# 导弹类self.dao_dan = Noneself.dao_dan_list = None# 弹道导弹类self.dan_dao_dao_dan = Noneself.dan_dao_dao_dan_list = None# 所有气球文件self.all_qi_qiu = None# 游戏背景音乐self.background_sound = arcade.load_sound("../声音文件/忍者神龟背景音乐.mp3")# 导弹发射车self.dao_dan_che_image = None# 爆炸烟雾self.explode_smoke = Noneself.explode_smoke_list = None# 为了有节奏的控制游戏刷新时个别元素的刷新速度 设置一个记录时间间隔变量self.interval_time = 0# 鼠标的图片self.couser_pic = None# 在初始化时调用的一些准备工作代码self.set_up()def set_up(self,init_qi_qiu_num=3):""" 进行一些游戏准备工作,让游戏主逻辑从这开始,以便于在有需要时重开游戏. """# 背景图片 当作精灵加载进来self.background_image = arcade.Sprite("../图片文件/世界卫星地图.png")# 游戏背景音乐self.current_play = self.background_sound.play(volume=0.4, loop=True)# 气球精灵列表self.qi_qiu_list = arcade.SpriteList()# 导弹精灵列表self.dao_dan_list = arcade.SpriteList()# 弹道导弹精灵列表self.dan_dao_dao_dan_list = arcade.SpriteList()# 爆炸烟雾精灵self.explode_smoke_list = arcade.SpriteList()# 获取文件夹中所有的飞艇图片self.all_qi_qiu = os.listdir("../图片文件/气球飞艇")print(self.all_qi_qiu)# 设置精灵素材中心点的位置self.background_image.center_x = self.window.width // 3self.background_image.center_y = self.window.height // 2# 导弹发射车 在on_resize()里设置绘制位置self.dao_dan_che_image = arcade.Sprite("../图片文件/东风41.png")self.dao_dan_che_image.width = 70self.dao_dan_che_image.height = 70# 初始状态时创建自定义个气球(默认3个)self.creat_qi_qiu(init_qi_qiu_num)self.couser_pic = arcade.Sprite('../图片文件/瞄准.png', scale=0.2)def creat_qi_qiu(self, num):"""创建气球"""for i in range(1, num+1):self.qi_qiu = QiQiu(filename=f"../图片文件/气球飞艇/{random.choice(self.all_qi_qiu)}",change_x=random.choice([0.5,0.5,1,2,2]), change_y=random.choice([1,2]),init_x=random.choice([-80, self.window.width + 200]),init_y=random.randint(200, self.window.height - num*50))self.qi_qiu.width = 60self.qi_qiu.height = 40# self.qi_qiu.scale = 0.5if self.qi_qiu.center_x > self.window.width//2:self.qi_qiu.change_x = -self.qi_qiu.change_xself.qi_qiu_list.append(self.qi_qiu)def creat_zhui_zong_dao_dan(self):dao_dan = DaoDan("../图片文件/导弹.gif", change_x=4, change_y=3,init_x=self.window.width // 3, init_y=self.window.height // 2)self.dao_dan_list.append(dao_dan)dao_dan.fire_sound.play()def creat_dan_dao_dao_dan(self,x,y):# 弹道导弹  如果剩余的弹道弹数量大于0 就接受命令进行创建init_x = self.window.width // 3init_y = self.window.height // 2speed = 1 if abs(x - init_x) < 200 else 3# 如果目标点在右侧 则导弹向右飞行 横坐标变化为正数 否则为负数change_x = speed if x > self.window.width // 3 else - speeddan_dao_dao_dan = DanDaoDaoDan("../图片文件/弹道导弹.png", change_x=change_x, change_y=3,init_x=init_x, init_y=init_y,target_x=x, target_y=y)dan_dao_dao_dan.scale = 0.7self.dan_dao_dao_dan_list.append(dan_dao_dao_dan)dan_dao_dao_dan.fire_sound.play()def creat_yan_wu(self,center_x, center_y):yan_wu = BaoZhaYanWu()yan_wu.center_x = center_xyan_wu.center_y = center_yself.explode_smoke_list.append(yan_wu)# 画面渲染绘制def on_draw(self):""" 负责渲染画面 速率: 60帧/秒. """# 清除上一帧绘制的画面self.clear()# 绘制背景图片self.background_image.draw()# 导弹发射车self.dao_dan_che_image.draw()# 绘制气球精灵组self.qi_qiu_list.draw()# 绘制导弹精灵组self.dao_dan_list.draw()# 绘制弹道导弹精灵组self.dan_dao_dao_dan_list.draw()# 绘制爆炸烟雾self.explode_smoke_list.draw()# 绘制鼠标self.couser_pic.draw()# 控制每次刷新时的变化def on_update(self, delta_time: float):""" 负责逻辑变化 速率: 60帧/秒. """for qi_qiu_id, qi_qiu in enumerate(self.qi_qiu_list):# 处理气球和自动追踪导弹的碰撞及逻辑qi_qiu.update(self.window.height)if qi_qiu_id <= len(self.dao_dan_list)-1:dao_dan = self.dao_dan_list[qi_qiu_id]dao_dan.track_target(qi_qiu.center_x, qi_qiu.center_y, change_angle=-0.5)# else:#     print("请尽快发射导弹")collision = arcade.check_for_collision_with_list(qi_qiu, self.dao_dan_list)if len(collision) > 0:print(f"发生碰撞了,相撞的{qi_qiu_id}号导弹和气球都被移除")# 移除碰到的气球qi_qiu.remove_from_sprite_lists()# 创建气球self.creat_qi_qiu(1)# 移除碰到的追踪导弹for collision_obj in collision:collision_obj.remove_from_sprite_lists()collision_obj.explode_sound.play()# 创建烟雾 坐标为所碰撞气球的坐标self.creat_yan_wu(qi_qiu.center_x,qi_qiu.center_y)if qi_qiu.center_x not in range(-230, self.window.width+230):# 气球飞出屏幕一定距离后 则消失qi_qiu.remove_from_sprite_lists()# 再次创建气球self.creat_qi_qiu(1)# 处理按下鼠标发射的弹道导弹for dan_dao_dao_dan in self.dan_dao_dao_dan_list:# 处理气球和弹道导弹的碰撞及逻辑dan_dao_dao_dan.track_target()collision2 = arcade.check_for_collision_with_list(dan_dao_dao_dan, self.qi_qiu_list)for collision_obj2 in collision2:collision_obj2.remove_from_sprite_lists()dan_dao_dao_dan.remove_from_sprite_lists()dan_dao_dao_dan.explode_sound.play()# 创建气球self.creat_qi_qiu(1)# 创建烟雾self.creat_yan_wu(collision_obj2.center_x, collision_obj2.center_y)# 如果导弹飞出了屏幕 则销毁if dan_dao_dao_dan.center_x not in range(-230, self.window.width+230):dan_dao_dao_dan.remove_from_sprite_lists()# 烟雾的绘制for smoke in self.explode_smoke_list:self.interval_time += delta_timeif self.interval_time > 0.08:res = smoke.update()self.interval_time = 0if res:smoke.remove_from_sprite_lists()def on_mouse_press(self, x, y, button, modifiers):""" 监听鼠标点击事件. """mouse_buttons = {1: "左键", 2: "中键", 4: "右键"}# print(f"当前点击的坐标是{x,y}")# 创建弹道导弹self.creat_dan_dao_dao_dan(x,y)def on_mouse_release(self, x: float, y: float, button: int, modifiers: int):""" 监听鼠标释放事件. """passdef on_mouse_motion(self, x: float, y: float, dx: float, dy: float):""" 监听鼠标移动事件 """self.couser_pic.center_x = xself.couser_pic.center_y = ydef on_key_press(self,key, modifiers):"""监听键盘按键点击事件modifiers:None = 16,shift= 1(17) ctrl= 2(18) alt= 4(20) """print("---键盘按键按下了:--",key,"--修饰键编号的和是:---", modifiers)if key == arcade.key.SPACE:self.creat_zhui_zong_dao_dan()def on_key_release(self, key, modifiers):print("---键盘按键抬起了:--",key,"--剩余修饰键编号的和是:---", modifiers)def on_resize(self, width: int, height: int):# 重新设置精灵素材中心点的位置self.background_image.center_x = self.window.width // 2self.background_image.center_y = self.window.height // 2# 重新设置精灵素材宽度和高度self.background_image.width = self.window.widthself.background_image.height = self.window.height# 设置导弹车精灵素材中心点的位置self.dao_dan_che_image.center_x = self.window.width // 3 - 30self.dao_dan_che_image.center_y = self.window.height // 2def main():# 设置窗体的宽、高、名字、是否支持缩放window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True)# 设置窗体的 logo 图标import pygletwindow.set_icon(pyglet.image.load('../图片文件/浪淘三千.png'))# 设置鼠标形状cursor = window.get_system_mouse_cursor(window.CURSOR_HAND)window.set_mouse_cursor(cursor)# 实例化定义的某个窗体start_view = LiuLangQiQiu()# 设置当前应该显示哪个窗体window.show_view(start_view)# 保持程序持续运行arcade.run()if __name__ == "__main__":main()

5、构建游戏信息类 (最终完整代码)

"""
本游戏制作了导弹拦截流浪气球的基本代码 有 音效、音乐、精灵、碰撞、轨迹计算 等知识的应用
当基础功能学会以后,可以教大家平面地图文件的绘制和使用 制作更丰富精彩的游戏场景
视频教学地址,会陆续更新 https://space.bilibili.com/455954948
欢迎积极交流更好的改进建议,一起升级游戏   (代码已同步 链接见视频评论区)
"""
import arcade
import os,random,math# 常量 窗口大小和标题
SCREEN_WIDTH = 1200
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Arcade 游戏教学 流浪气球"class QiQiu(arcade.Sprite):def __init__(self, filename, change_x, change_y, init_x, init_y):super(QiQiu, self).__init__(filename,hit_box_algorithm = "Simple")# 导弹精灵的飞行变化速度self.change_x = change_xself.change_y = change_y# 导弹精灵的初始中心点(init_x,init_y)self.center_x = init_xself.center_y = init_y# 记录气球单次持续垂直移动的距离self.total_change_y = 0# 是否被追踪 一旦被追踪则不再被别的导弹追踪self.is_tracked = False# 初始中心点不在界面左侧,则是从右向左飞行的,因为原始图片都朝向右侧,向左飞行的要水平翻转if self.center_x > 0:self.append_texture((arcade.load_texture(filename, mirrored=True)))# texture_right_forward = arcade.load_texture(filename, mirrored=True)self.set_texture(1)def update(self, window_height=800):self.total_change_y += self.change_y# 当单次垂直移动距离超过50就重新随机选择方向 这个数字通过自己测试调整得到if abs(self.total_change_y) > 50:# 随机改变气球纵向移动方向self.change_y *= random.choice([-1, 1])# 再次重新计算持续垂直移动的距离self.total_change_y = 0if self.center_y > window_height- self.height or self.center_y < self.height:self.change_y = 0self.total_change_y = 0self.center_x += self.change_xself.center_y += self.change_yclass DaoDan(arcade.Sprite):"""目前是设计成了自动搜寻目标 然后攻击的模式"""def __init__(self, filename, change_x, change_y, init_x, init_y, target_x=None, target_y=None):super(DaoDan, self).__init__(filename,hit_box_algorithm = "Simple")# self.load_animated_gif(filename)# 导弹精灵的飞行变化速度self.change_x = change_xself.change_y = change_yself.change_y_init = change_y# 记录导弹精灵的初始点self.init_x = init_xself.init_y = init_y# 导弹精灵的初始中心点(init_x,init_y)self.center_x = init_xself.center_y = init_y# 要追踪的目标self.target_x = target_xself.target_y = target_y# 导弹是否还在地面上(没有发射)self.is_on_ground = True# 是否正在追踪 初始是未开始追踪self.is_tracking = False# 导弹点火发射音效self.fire_sound = arcade.load_sound("../声音文件/火焰推进器.mp3")# 导弹爆炸音效self.explode_sound = arcade.load_sound("../声音文件/中距离爆炸.mp3")def track_target(self, target_x, target_y, change_angle=0.1):# 当被调用的时候,开始追踪目标气球# 计算导弹和气球飞艇之间的弧度值 Π = 3.14(弧度) = 180°(角度) 向左转角度要加 向右转角度要减target_radians_angle = math.atan2(target_y-self.center_y,target_x-self.center_x)# 将上一步计算出的弧度转换为角度值# 都要减去90 因为导弹的初始方向是向上的,所以有一个默认的90°角,减去后恢复到 0°再计算。target_degree_angle = math.degrees(target_radians_angle)-90# print(f"角度是:{target_degree_angle}")self.center_x += self.change_x if self.center_x < target_x else -self.change_xself.center_y += self.change_y if self.center_y < target_y else -self.change_yself.change_y = 0 if abs(self.center_y - target_y) < 5 else self.change_y_init# print("2222", self.center_y, target_y, self.change_y)if abs(target_degree_angle) in range(0,8):self.angle = 0elif abs(target_degree_angle) in range(172,188):self.angle = 180else:self.angle = target_degree_angle# 定义一个弹道导弹类 继承导弹基类 ,因为追踪逻辑不一样,所以需要重写其追踪方法
class DanDaoDaoDan(DaoDan):# def __init__(self):#     super().__init__()def track_target(self):"""开始计算固定飞行轨迹 y = a(x-h)²+k  h 和 k 为抛物线的顶点横纵坐标将鼠标点击的(self.target_x,self.target_y)点作为顶点,则有【抛物线函数 y = a(x-self.target_x)²+self.target_y 】将导弹的起点坐标带入上述方程 可以求出 a 值 于是就可以求出抛物线的函数式【抛物线函数 init_y = a(init_x-self.target_x)²+self.target_y 】a = (init_y - self.target_y)/(init_x-self.target_x)²"""# 如果开口向下,a小于0;开口向上,a大于0.a = (self.init_y - self.target_y)/(self.init_x-self.target_x)**2# print(f"a的值是:{a}")self.center_x += self.change_x# 将横坐标带入方程求纵坐标self.center_y = a*(int(self.center_x - self.target_x)**2)+self.target_y# print(f"{self.center_y} = {a}*({int(self.center_x - self.target_x)})**2+{self.target_y}")# 计算导弹和气球飞艇之间的弧度值 Π = 3.14(弧度) = 180°(角度)target_radians_angle = math.atan2(self.target_y-self.center_y,self.target_x-self.center_x)# 将上一步计算出的弧度转换为角度值# 都要减去90 因为导弹的初始方向是向上的,所以有一个默认的90°角,减去后恢复到 0°再计算。target_degree_angle = math.degrees(target_radians_angle)-90if (self.target_x-self.init_x)*(self.target_x-self.center_x) < 0:# 值小于0 的时候说明导弹飞行中已经超过了设定好的最高点target_degree_angle = target_degree_angle + 180# print(f"角度是:{target_degree_angle}")if abs(target_degree_angle) in range(0,8):self.angle = 0elif abs(target_degree_angle) in range(172,188):self.angle = 180else:self.angle = target_degree_angleclass BaoZhaYanWu(arcade.Sprite):def __init__(self):super(BaoZhaYanWu, self).__init__()# 初始中心点不在界面左侧,则是从右向左飞行的,因为原始图片都朝向右侧,向左飞行的要水平翻转all_explore_pic = os.listdir("../图片文件/images11")for pic_name in all_explore_pic:self.append_texture((arcade.load_texture(f"../图片文件/images11/{pic_name}")))# 记下一共有多少纹理图self.textures_num_len = len(all_explore_pic)self.texture_index_now = 0def update(self, texture_index: int = None):# 如果状态是 True 则开始计算绘制位置if self.texture_index_now < self.textures_num_len:self.set_texture(self.texture_index_now)self.texture_index_now += 1return Falseelse:# 收到为True的返回值时 销毁烟雾精灵return Trueclass InfoPanel:"""信息板 以后可以优化为游戏公共信息类"""def __init__(self):super(InfoPanel, self).__init__()self.game_info = {}self.set_up()def set_up(self,init_qi_qiu_num=0,game_limit_time=10,zhui_zong_dao_dan_num=10,dan_dao_dao_dan_num=20,difficulty_level=2):self.game_info.clear()self.game_info["game_status"] = True  # 游戏状态(能玩和不能玩)self.game_info["game_pause"] = False  # 游戏暂停状态self.game_info["player_score"] = 0  # 玩家分数self.game_info["player_use_time"] = 0  # 玩家用时self.game_info["game_limit_time"] = game_limit_time  # 游戏限时 单位秒self.game_info["difficulty_level"] = difficulty_level  # 游戏难度(平均几个弹道导弹 VS 一个气球)self.game_info["surplus_dan_dao_dao_dan_num"] = dan_dao_dao_dan_num  # surplus:剩余 弹道导弹数量self.game_info["surplus_zhui_zong_dao_dan_num"] = zhui_zong_dao_dan_num  # 剩余追踪弹的数量# 得分要求为 追踪导弹的数量个数 加上 弹道导弹数量个数的一半self.game_info["player_target_score"] = zhui_zong_dao_dan_num + dan_dao_dao_dan_num//max(1,difficulty_level)  # 玩家目标分数self.game_info["init_qi_qiu_num"] = init_qi_qiu_num  # 初始气球数量设置为self.game_info["surplus_qi_qiu_num"] = 0  # 剩余气球数量self.game_info["all_qi_qiu_num"] = 0  # 全部出现过的气球数量self.game_info["zhui_zong_dao_dan_pic"] = arcade.Sprite("../图片文件/导弹.gif")self.game_info["dan_dao_dao_dan_pic"] = arcade.Sprite("../图片文件/弹道导弹.png")# 游戏胜利图片self.game_info["game_win_pic"] = arcade.Sprite("../图片文件/win.png")# 游戏失败图片self.game_info["game_over_pic"] = arcade.Sprite("../图片文件/game_over.png")# 游戏暂停图片self.game_info["game_pause_pic"] = arcade.Sprite("../图片文件/游戏暂停.png")# 游戏胜利音乐self.game_info["game_win_sound"] = arcade.sound.load_sound("../声音文件/成功.mp3")# 游戏失败音乐self.game_info["game_over_sound_1"] = arcade.sound.load_sound("../声音文件/马里奥游戏结束.mp3")# 游戏失败嘲讽self.game_info["game_over_sound_2"] = arcade.sound.load_sound("../声音文件/马里奥哈哈嘲讽.mp3")# 游戏暂停音效self.game_info["game_pause_sound"] = arcade.sound.load_sound("../声音文件/暂停.mp3")# # 游戏暂停后重新开始音效# self.game_info["game_continue_sound"] = arcade.sound.load_sound("../声音文件/暂停后开始.mp3")def draw_game_info(self,x,y):# x: 屏幕宽度 y:屏幕高度font_size = 20# 第一行# 描述游戏信息的字符串self.game_info["game_info_txt"] = f"难度:{self.game_info['difficulty_level']} vs 1" \f" 得分:{self.game_info['player_score']} /{self.game_info['player_target_score']}"\f" 命中率:{self.game_info['player_score']}/{self.game_info['all_qi_qiu_num']}"\f" 计时:{self.game_info['player_use_time']}/{self.game_info['game_limit_time']}"arcade.Text(self.game_info["game_info_txt"], 10, y - font_size*2,color=arcade.color.SAE, font_size=font_size, width=x, font_name="站酷快乐体2016修订版",bold=True, align="left", anchor_x="left", multiline=False).draw()# 第二行# 绘制追踪导弹信息self.game_info["zhui_zong_dao_dan_pic"].width = font_size*2self.game_info["zhui_zong_dao_dan_pic"].height = font_size*2self.game_info["zhui_zong_dao_dan_pic"].center_x = 10 + font_size // 2self.game_info["zhui_zong_dao_dan_pic"].center_y = y - font_size * 3.5self.game_info["zhui_zong_dao_dan_pic"].draw()arcade.Text(f"X {self.game_info['surplus_zhui_zong_dao_dan_num']}", 10 + font_size*2 , y - font_size *4,color=arcade.color.SAE, font_size=font_size, width=x, font_name="站酷快乐体2016修订版",bold=True, align="left", anchor_x="left", multiline=True).draw()# 绘制弹道导弹信息 (第二行 追踪导弹后面的内容)self.game_info["dan_dao_dao_dan_pic"].width = font_size*2self.game_info["dan_dao_dao_dan_pic"].height = font_size*2self.game_info["dan_dao_dao_dan_pic"].center_x = 10 + font_size * 2 + 5*font_sizeself.game_info["dan_dao_dao_dan_pic"].center_y = y - font_size * 3.5self.game_info["dan_dao_dao_dan_pic"].draw()arcade.Text(f"X {self.game_info['surplus_dan_dao_dao_dan_num']}", 10 + font_size * 2 + 6*font_size, y - font_size *4,color=arcade.color.SAE, font_size=font_size, width=x, font_name="站酷快乐体2016修订版",bold=True, align="left", anchor_x="left", multiline=True).draw()# 游戏失败时持续执行if not self.game_info["game_status"] and self.game_info["player_use_time"] >= self.game_info["game_limit_time"]:self.game_info["game_over_pic"].center_x = x // 2self.game_info["game_over_pic"].center_y = y // 2self.game_info["game_over_pic"].draw()arcade.Text(f"~~按右侧 SHIFT 键重新开始~~", x//2,y//2 - 200,color=arcade.color.WHITE, font_size=font_size*2, width=x, font_name="站酷快乐体2016修订版",bold=True, align="left", anchor_x="center", multiline=False).draw()# 游戏胜利时持续执行if not self.game_info["game_status"] and (self.game_info["player_score"] >= self.game_info['player_target_score']):self.game_info["game_win_pic"].center_x = x // 2self.game_info["game_win_pic"].center_y = y // 2self.game_info["game_win_pic"].draw()# 触发游戏暂停时持续执行if self.game_info["game_status"] and self.game_info["game_pause"]:self.game_info["game_pause_pic"].center_x = x // 2self.game_info["game_pause_pic"].center_y = y // 2self.game_info["game_pause_pic"].draw()arcade.Text(f"~~按 Enter 键继续~~", x // 2, y // 2 - 200,color=arcade.color.WHITE, font_size=font_size*2, width=x, font_name="站酷快乐体2016修订版",bold=True, align="left", anchor_x="center", multiline=False).draw()def check_game_status(self, bgm):# 游戏失败时执行if self.game_info["game_status"] and self.game_info["player_use_time"] >= self.game_info["game_limit_time"]:self.game_info["game_status"] = Falsearcade.stop_sound(bgm)self.game_info["game_over_sound_1"].play()self.game_info["game_over_sound_2"].play()# 游戏胜利时执行if self.game_info["game_status"] and (self.game_info["player_score"] >= self.game_info['player_target_score']):self.game_info["game_status"] = Falsearcade.stop_sound(bgm)self.game_info["game_win_sound"].play()# 游戏暂停时执行if self.game_info["game_pause"]:return Falsereturn self.game_info["game_status"]# arcade_game_202302 流浪气球Ⅰ
class LiuLangQiQiu(arcade.View):""" 视图程序,用于在窗体内展示. """def __init__(self):# 初始化父类的属性super().__init__()self.background_image = Noneself.dao_dan = None# 气球类self.qi_qiu = Noneself.qi_qiu_list = None# 导弹类self.dao_dan = Noneself.dao_dan_list = None# 弹道导弹类self.dan_dao_dao_dan = Noneself.dan_dao_dao_dan_list = None# 所有气球文件self.all_qi_qiu = None# 游戏背景音乐self.background_sound = arcade.load_sound("../声音文件/忍者神龟背景音乐.mp3")# 导弹发射车self.dao_dan_che_image = None# 爆炸烟雾self.explode_smoke = Noneself.explode_smoke_list = None# 为了有节奏的控制游戏刷新时个别元素的刷新速度 设置一个记录时间间隔变量self.interval_time = 0# 游戏信息面板self.info_panel = None# 鼠标的图片self.couser_pic = None# 在初始化时调用的一些准备工作代码self.set_up()# 有规律的调用time_clock函数,每次调用间隔设置为1秒arcade.schedule(self.time_clock, 1)def set_up(self,init_qi_qiu_num = 5,zhui_zong_dao_dan_num = 20,dan_dao_dao_dan_num=30,difficulty_level=3):"difficulty_level:难度等级 代表平均至少用几个弹道导弹就击中一个气球 ,会以此来计算玩家的目标分数 数字越大越简单 最小是1"""" 进行一些游戏准备工作,让游戏主逻辑从这开始,以便于在有需要时重开游戏. """# 实例化游戏信息类self.info_panel = InfoPanel()self.info_panel.set_up(init_qi_qiu_num=init_qi_qiu_num,game_limit_time=zhui_zong_dao_dan_num * 1 + dan_dao_dao_dan_num*2,zhui_zong_dao_dan_num=zhui_zong_dao_dan_num, dan_dao_dao_dan_num=dan_dao_dao_dan_num,difficulty_level=difficulty_level)# 游戏背景音乐self.current_play = self.background_sound.play(volume=0.4, loop=True)# 气球精灵列表self.qi_qiu_list = arcade.SpriteList()# 导弹精灵列表self.dao_dan_list = arcade.SpriteList()# 弹道导弹精灵列表self.dan_dao_dao_dan_list = arcade.SpriteList()# 爆炸烟雾精灵self.explode_smoke_list = arcade.SpriteList()# 获取文件夹中所有的飞艇图片self.all_qi_qiu = os.listdir("../图片文件/气球飞艇")print(self.all_qi_qiu)# 背景图片 当作精灵加载进来self.background_image = arcade.Sprite("../图片文件/世界卫星地图.png")# 设置精灵素材中心点的位置self.background_image.center_x = self.window.width // 3self.background_image.center_y = self.window.height // 2# 导弹发射车 在on_resize()里设置绘制位置self.dao_dan_che_image = arcade.Sprite("../图片文件/东风41.png")self.dao_dan_che_image.width = 70self.dao_dan_che_image.height = 70# 初始状态时创建自定义个气球(默认3个)self.creat_qi_qiu(init_qi_qiu_num)self.couser_pic = arcade.Sprite('../图片文件/瞄准.png', scale=0.2)# 括号里的这个参数必须写,他是一个时间间隔,代表上次调用 到本次调用之间的时间差 不严格等于我们设置的1秒def time_clock(self, delta_time):# print(delta_time)# 因为我们要更改TIME_CLOCK的值,所以使用global 让函数可以改变这个全局变量if self.info_panel.game_info["game_status"] and not self.info_panel.game_info["game_pause"]:self.info_panel.game_info["player_use_time"] += 1def creat_qi_qiu(self, num):"""创建气球"""for i in range(1, num+1):self.qi_qiu = QiQiu(filename=f"../图片文件/气球飞艇/{random.choice(self.all_qi_qiu)}",change_x=random.choice([0.5,0.5,1,2,2]), change_y=random.choice([1,2]),init_x=random.choice([-80, self.window.width + 200]),init_y=random.randint(200, self.window.height - num*50))self.qi_qiu.width = 60self.qi_qiu.height = 40# self.qi_qiu.scale = 0.5if self.qi_qiu.center_x > self.window.width//2:self.qi_qiu.change_x = -self.qi_qiu.change_xself.qi_qiu_list.append(self.qi_qiu)# 每次创建过气球后 都将统计到游戏信息面板中self.info_panel.game_info["all_qi_qiu_num"] += numdef creat_zhui_zong_dao_dan(self):# 追踪导弹  如果剩余的追踪弹数量大于0 就接受命令进行创建if self.info_panel.game_info["surplus_zhui_zong_dao_dan_num"] > 0:dao_dan = DaoDan("../图片文件/导弹.gif", change_x=4, change_y=3,init_x=self.window.width // 3, init_y=self.window.height // 2)self.dao_dan_list.append(dao_dan)dao_dan.fire_sound.play()self.info_panel.game_info["surplus_zhui_zong_dao_dan_num"] -= 1def creat_dan_dao_dao_dan(self,x,y):# 弹道导弹  如果剩余的弹道弹数量大于0 就接受命令进行创建if self.info_panel.game_info["surplus_dan_dao_dao_dan_num"] > 0:init_x = self.window.width // 3init_y = self.window.height // 2speed = 1 if abs(x - init_x) < 200 else 3# 如果目标点在右侧 则导弹向右飞行 横坐标变化为正数 否则为负数change_x = speed if x > self.window.width // 3 else - speeddan_dao_dao_dan = DanDaoDaoDan("../图片文件/弹道导弹.png", change_x=change_x, change_y=3,init_x=init_x, init_y=init_y,target_x=x, target_y=y)dan_dao_dao_dan.scale = 0.7self.dan_dao_dao_dan_list.append(dan_dao_dao_dan)dan_dao_dao_dan.fire_sound.play()# 创建完毕后 剩余弹道导弹数量 -1self.info_panel.game_info["surplus_dan_dao_dao_dan_num"] -= 1def creat_yan_wu(self,center_x, center_y):yan_wu = BaoZhaYanWu()yan_wu.center_x = center_xyan_wu.center_y = center_yself.explode_smoke_list.append(yan_wu)# 画面渲染绘制def on_draw(self):""" 负责渲染画面 速率: 60帧/秒. """# 清除上一帧绘制的画面self.clear()# 绘制背景图片self.background_image.draw()# 导弹发射车self.dao_dan_che_image.draw()# 绘制气球精灵组self.qi_qiu_list.draw()# 绘制导弹精灵组self.dao_dan_list.draw()# 绘制弹道导弹精灵组self.dan_dao_dao_dan_list.draw()# 绘制爆炸烟雾self.explode_smoke_list.draw()# 绘制游戏信息self.info_panel.draw_game_info(self.window.width, self.window.height)# 绘制鼠标self.couser_pic.draw()# 控制每次刷新时的变化def on_update(self, delta_time: float):""" 负责逻辑变化 速率: 60帧/秒. """if self.info_panel.check_game_status(self.current_play):# 如果检测游戏的状态为True 则继续刷新 否则不再执行后续逻辑for qi_qiu_id, qi_qiu in enumerate(self.qi_qiu_list):# 处理气球和自动追踪导弹的碰撞及逻辑qi_qiu.update(self.window.height)if qi_qiu_id <= len(self.dao_dan_list)-1:dao_dan = self.dao_dan_list[qi_qiu_id]dao_dan.track_target(qi_qiu.center_x, qi_qiu.center_y, change_angle=-0.5)# else:#     print("请尽快发射导弹")collision = arcade.check_for_collision_with_list(qi_qiu, self.dao_dan_list)if len(collision) > 0:print(f"发生碰撞了,相撞的{qi_qiu_id}号导弹和气球都被移除")# 移除碰到的气球qi_qiu.remove_from_sprite_lists()# 剩余气球数量 -1self.info_panel.game_info["surplus_qi_qiu_num"] -= 1# 玩家分数 +1self.info_panel.game_info["player_score"] += 1# 创建气球self.creat_qi_qiu(1)# 移除碰到的追踪导弹for collision_obj in collision:collision_obj.remove_from_sprite_lists()collision_obj.explode_sound.play()# 追踪导弹数量-1 因为在创建时候就减去了1 所以这里不再减# self.info_panel.game_info["surplus_zhui_zong_dao_dan_num"] -= 1# 创建烟雾 坐标为所碰撞气球的坐标self.creat_yan_wu(qi_qiu.center_x,qi_qiu.center_y)if qi_qiu.center_x not in range(-230, self.window.width+230):# 气球飞出屏幕一定距离后 则消失qi_qiu.remove_from_sprite_lists()# 剩余气球数量 -1self.info_panel.game_info["surplus_qi_qiu_num"] -= 1# 再次创建气球self.creat_qi_qiu(1)# 处理按下鼠标发射的弹道导弹for dan_dao_dao_dan in self.dan_dao_dao_dan_list:# 处理气球和弹道导弹的碰撞及逻辑dan_dao_dao_dan.track_target()collision2 = arcade.check_for_collision_with_list(dan_dao_dao_dan, self.qi_qiu_list)for collision_obj2 in collision2:collision_obj2.remove_from_sprite_lists()dan_dao_dao_dan.remove_from_sprite_lists()dan_dao_dao_dan.explode_sound.play()# 剩余气球数量 -1self.info_panel.game_info["surplus_qi_qiu_num"] -= 1# 玩家分数 +1self.info_panel.game_info["player_score"] += 1# 创建气球self.creat_qi_qiu(1)# 创建烟雾self.creat_yan_wu(collision_obj2.center_x, collision_obj2.center_y)# 如果导弹飞出了屏幕 则销毁if dan_dao_dao_dan.center_x not in range(-230, self.window.width+230):dan_dao_dao_dan.remove_from_sprite_lists()# 将原来创建气球的时机改为每当有一个气球消失 就创建一个 下面代码不再使用# if len(self.qi_qiu_list) <= 3:#     self.creat_qi_qiu(3)for smoke in self.explode_smoke_list:self.interval_time += delta_timeif self.interval_time > 0.08:res = smoke.update()self.interval_time = 0if res:smoke.remove_from_sprite_lists()def on_mouse_press(self, x, y, button, modifiers):""" 监听鼠标点击事件. """mouse_buttons = {1: "左键", 2: "中键", 4: "右键"}# print(f"当前点击的坐标是{x,y}")if self.info_panel.game_info["game_status"] and not self.info_panel.game_info["game_pause"]:# 创建弹道导弹self.creat_dan_dao_dao_dan(x,y)def on_mouse_release(self, x: float, y: float, button: int, modifiers: int):""" 监听鼠标释放事件. """passdef on_mouse_motion(self, x: float, y: float, dx: float, dy: float):""" 监听鼠标移动事件 """self.couser_pic.center_x = xself.couser_pic.center_y = ydef on_key_press(self,key, modifiers):"""监听键盘按键点击事件modifiers:None = 16,shift= 1(17) ctrl= 2(18) alt= 4(20) """print("---键盘按键按下了:--",key,"--修饰键编号的和是:---", modifiers)if key == arcade.key.SPACE and self.info_panel.game_info["game_status"] \and not self.info_panel.game_info["game_pause"]:self.creat_zhui_zong_dao_dan()if key == arcade.key.RSHIFT and not self.info_panel.game_info["game_status"]:# 让游戏重新开始  并且重置屏幕大小为上一次的大小self.set_up()self.on_resize(self.window.width,self.window.height)def on_key_release(self, key, modifiers):print("---键盘按键抬起了:--",key,"--剩余修饰键编号的和是:---", modifiers)print("---键盘按下之前:--",self.info_panel.game_info["game_pause"])if key == arcade.key.ENTER and self.info_panel.game_info["game_status"]:self.info_panel.game_info["game_pause"] = not self.info_panel.game_info["game_pause"]self.info_panel.game_info["game_pause_sound"].play()print("---键盘按下之后:--", self.info_panel.game_info["game_pause"])def on_resize(self, width: int, height: int):# 重新设置精灵素材中心点的位置self.background_image.center_x = self.window.width // 2self.background_image.center_y = self.window.height // 2# 重新设置精灵素材宽度和高度self.background_image.width = self.window.widthself.background_image.height = self.window.height# 设置导弹车精灵素材中心点的位置self.dao_dan_che_image.center_x = self.window.width // 3 - 30self.dao_dan_che_image.center_y = self.window.height // 2for jing_ling in (self.dan_dao_dao_dan_list,self.dao_dan_list,self.qi_qiu_list):# 界面在玩耍时侯如果缩放,会使导弹和气球位置发生相对于地图的偏移passdef main():# 设置窗体的宽、高、名字、是否支持缩放window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True)# 设置窗体的 logo 图标import pygletwindow.set_icon(pyglet.image.load('../图片文件/浪淘三千.png'))# 设置鼠标形状cursor = window.get_system_mouse_cursor(window.CURSOR_HAND)window.set_mouse_cursor(cursor)# 实例化定义的某个窗体start_view = LiuLangQiQiu()# 设置当前应该显示哪个窗体window.show_view(start_view)# 保持程序持续运行arcade.run()if __name__ == "__main__":main()

【日常点滴019】Python制作流浪气球游戏(导弹射击类)相关推荐

  1. python拼图_利用python制作拼图小游戏的全过程

    开发工具 Python版本:3.6.4 相关模块: pygame模块: 以及一些Python自带的模块 关注公众号:Python学习指南,回复"拼图"即可获取源码 环境搭建 安装P ...

  2. python可视化迷宫求解_如何用 Python 制作一个迷宫游戏

    相信大家都玩过迷宫的游戏,对于简单的迷宫,我们可以一眼就看出通路,但是对于复杂的迷宫,可能要仔细寻找好久,甚至耗费数天,然后可能还要分别从入口和出口两头寻找才能找的到通路,甚至也可能找不到通路. 虽然 ...

  3. 教你用python制作猜数游戏

    大家好!我又回来了!今天我教大家怎样用python制作猜数游戏.在此之前我们先了解一下什么是python版猜数游戏. 了解 python版猜数游戏是指您先输入下限值与上限值,然后系统随机生成一个在此范 ...

  4. python制作飞机大战游戏准备工作相关知识点

    python制作飞机大战游戏准备工作相关知识点 1.初始化背景 `import pygame` : 导入pygame模块 `pygame.init()` : 导入并初始化所有 `pygame` 模块, ...

  5. matlab制作打地鼠游戏,Python制作打地鼠小游戏

    原标题:Python制作打地鼠小游戏 导语 大家好,欢迎来到 Crossin的编程教室 ! 打地鼠游戏相信大家都知道,这里就不多介绍了,反正就是不停地拿锤子打洞里钻出来的地鼠呗~ 今天就给大家分享一个 ...

  6. 利用python制作拼图_利用python制作拼图小游戏的全过程

    开发工具 Python版本:3.6.4 相关模块: pygame模块: 以及一些Python自带的模块 关注公众号:Python学习指南,回复"拼图"即可获取源码 环境搭建 安装P ...

  7. python制作简易阴阳师游戏脚本(基础问题篇)

    正值过年的关卡,疫情严重,在这里盼望武汉马上恢复起来,武汉加油!!! python制作简易阴阳师游戏脚本(代码篇) 在一动不动的这几天里,受朋友的邀请写了个阴阳师刷御魂的脚本. 因个人水平有限,希望各 ...

  8. 用python制作好玩的游戏_太好玩了!我用 Python 制作一款小游戏!

    超级玛丽.魂斗罗.坦克大战.冒险岛--提到这些游戏,"80后"."90后"滔滔不绝,掀起一波回忆杀. 最近不少媒体发文称:"小霸王被申请破产" ...

  9. python手机版做小游戏代码大全-python简单小游戏代码 怎么用Python制作简单小游戏...

    1.Python猜拳小游戏代码: 2.import random #导入随机模块 3. 4.num = 1 5.yin_num = 0 6.shu_num = 0 7.while num <= ...

最新文章

  1. 突破屏蔽限制,自己的网站使劲弹新IE窗口
  2. 产线数字化软件源码_数字化工厂规划的十大核心要素
  3. PJzhang:英国通信总部GCHQ开源产品-网络瑞士军刀CyberChef
  4. mysql添加表字段_mysql命令添加表字段
  5. php 逗号编码,php有几种编码
  6. sql 查询结果自定义排序
  7. 20165332第六周学习总结
  8. 【NOIP2015】【Luogu2670】扫雷游戏(搜索,字符串输入输出)
  9. 关于STM8L系列低功耗井盖板设计记录【云南昆明电子设计开发工程师】
  10. JavaScript点击背景图片切换
  11. 虚拟机ubuntu的联网方法(NET)
  12. 工业相机像元与像素之间的关系
  13. 从零开始iOS8编程【HelloWorld】
  14. Linux系统运维常用命令集锦
  15. javafx 教程_集成JavaFX和Swing(修订版)
  16. 回到计算机桌面快捷键,win10系统如何使用返回桌面快捷键?快速返回到电脑桌面的方法...
  17. 月薪 800 到 2w 加
  18. Tkinter写一个音乐下载器
  19. Xshell的使用方法(初学者)
  20. 港股打新丨放弃药明巨诺,搞蚂蚁金服

热门文章

  1. NeuroSolutions for Excel这个功能可以实现多种神经网络嘛?
  2. 机器人的视听觉交叉感知技术研究
  3. HiJack利用iPhone耳机插孔制作廉价传感器
  4. [Unity]编辑器中第一次结束游戏后第二次开始游戏崩溃的问题
  5. 弘辽科技:猜你喜欢要怎么样入池呢?
  6. 计算机基础(27)——办公助手之电脑快捷键
  7. Android8.0 高通PDAF 调试 - actuator sensitivity
  8. bootstrap 幻灯片的实现
  9. 服务器跳转后访问网页怎么设置,什么是域名URL转发?如何设置
  10. 【网站搭建】cloudflare实现显示url转发(301永久转发)