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

在这里插入图片描述

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

实时同步文件: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 // 3
        self.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帧/秒. """
        pass

    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):
        """ 监听鼠标释放事件. """
        pass

    def on_mouse_motion(self, x: float, y: float, dx: float, dy: float):
        """ 监听鼠标移动事件 """
        self.couser_pic.center_x = x
        self.couser_pic.center_y = y


    def 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 // 2
        self.background_image.center_y = self.window.height // 2
        # 重新设置精灵素材宽度和高度
        self.background_image.width = self.window.width
        self.background_image.height = self.window.height

def main():
    # 设置窗体的宽、高、名字、是否支持缩放
    window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True)
    # 设置窗体的 logo 图标
    import pyglet
    window.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_x
        self.change_y = change_y
        # 导弹精灵的初始中心点(init_x,init_y)
        self.center_x = init_x
        self.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 = 0
        if self.center_y > window_height- self.height or self.center_y < self.height:
            self.change_y = 0
            self.total_change_y = 0
        self.center_x += self.change_x
        self.center_y += self.change_y

# arcade_game_202302 流浪气球Ⅰ教学版
class LiuLangQiQiu(arcade.View):
    """ 视图程序,用于在窗体内展示. """

    def __init__(self):
        # 初始化父类的属性
        super().__init__()
        self.background_image = None
        # 气球类
        self.qi_qiu = None
        self.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 // 3
        self.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 = 60
            self.qi_qiu.height = 40
            # self.qi_qiu.scale = 0.5
            if self.qi_qiu.center_x > self.window.width//2:
                self.qi_qiu.change_x = -self.qi_qiu.change_x
            self.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):
        """ 监听鼠标释放事件. """
        pass

    def on_mouse_motion(self, x: float, y: float, dx: float, dy: float):
        """ 监听鼠标移动事件 """
        self.couser_pic.center_x = x
        self.couser_pic.center_y = y

    def 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 // 2
        self.background_image.center_y = self.window.height // 2
        # 重新设置精灵素材宽度和高度
        self.background_image.width = self.window.width
        self.background_image.height = self.window.height

def main():
    # 设置窗体的宽、高、名字、是否支持缩放
    window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True)
    # 设置窗体的 logo 图标
    import pyglet
    window.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_x
        self.change_y = change_y
        # 导弹精灵的初始中心点(init_x,init_y)
        self.center_x = init_x
        self.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 = 0
        if self.center_y > window_height- self.height or self.center_y < self.height:
            self.change_y = 0
            self.total_change_y = 0
        self.center_x += self.change_x
        self.center_y += self.change_y


class 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_x
        self.change_y = change_y
        self.change_y_init = change_y
        # 记录导弹精灵的初始点
        self.init_x = init_x
        self.init_y = init_y
        # 导弹精灵的初始中心点(init_x,init_y)
        self.center_x = init_x
        self.center_y = init_y
        # 要追踪的目标
        self.target_x = target_x
        self.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_x
        self.center_y += self.change_y if self.center_y < target_y else -self.change_y
        self.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 = 0
        elif abs(target_degree_angle) in range(172,188):
            self.angle = 180
        else:
            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)-90
        if (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 = 0
        elif abs(target_degree_angle) in range(172,188):
            self.angle = 180
        else:
            self.angle = target_degree_angle


class 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 = 0

    def 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 += 1
            return False
        else:
            # 收到为True的返回值时 销毁烟雾精灵
            return True


# arcade_game_202302 流浪气球Ⅰ教学版
class LiuLangQiQiu(arcade.View):
    """ 视图程序,用于在窗体内展示. """

    def __init__(self):
        # 初始化父类的属性
        super().__init__()
        self.background_image = None
        self.dao_dan = None
        # 气球类
        self.qi_qiu = None
        self.qi_qiu_list = None
        # 导弹类
        self.dao_dan = None
        self.dao_dan_list = None
        # 弹道导弹类
        self.dan_dao_dao_dan = None
        self.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 = None
        self.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 // 3
        self.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 = 70
        self.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 = 60
            self.qi_qiu.height = 40
            # self.qi_qiu.scale = 0.5
            if self.qi_qiu.center_x > self.window.width//2:
                self.qi_qiu.change_x = -self.qi_qiu.change_x
            self.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 // 3
        init_y = self.window.height // 2
        speed = 1 if abs(x - init_x) < 200 else 3
        # 如果目标点在右侧 则导弹向右飞行 横坐标变化为正数 否则为负数
        change_x = speed if x > self.window.width // 3 else - speed
        dan_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.7
        self.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_x
        yan_wu.center_y = center_y
        self.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):
        """ 监听鼠标释放事件. """
        pass

    def on_mouse_motion(self, x: float, y: float, dx: float, dy: float):
        """ 监听鼠标移动事件 """
        self.couser_pic.center_x = x
        self.couser_pic.center_y = y

    def 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 // 2
        self.background_image.center_y = self.window.height // 2
        # 重新设置精灵素材宽度和高度
        self.background_image.width = self.window.width
        self.background_image.height = self.window.height
        # 设置导弹车精灵素材中心点的位置
        self.dao_dan_che_image.center_x = self.window.width // 3 - 30
        self.dao_dan_che_image.center_y = self.window.height // 2

def main():
    # 设置窗体的宽、高、名字、是否支持缩放
    window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True)
    # 设置窗体的 logo 图标
    import pyglet
    window.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_x
        self.change_y = change_y
        # 导弹精灵的初始中心点(init_x,init_y)
        self.center_x = init_x
        self.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 = 0
        if self.center_y > window_height- self.height or self.center_y < self.height:
            self.change_y = 0
            self.total_change_y = 0
        self.center_x += self.change_x
        self.center_y += self.change_y


class 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_x
        self.change_y = change_y
        self.change_y_init = change_y
        # 记录导弹精灵的初始点
        self.init_x = init_x
        self.init_y = init_y
        # 导弹精灵的初始中心点(init_x,init_y)
        self.center_x = init_x
        self.center_y = init_y
        # 要追踪的目标
        self.target_x = target_x
        self.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_x
        self.center_y += self.change_y if self.center_y < target_y else -self.change_y
        self.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 = 0
        elif abs(target_degree_angle) in range(172,188):
            self.angle = 180
        else:
            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)-90
        if (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 = 0
        elif abs(target_degree_angle) in range(172,188):
            self.angle = 180
        else:
            self.angle = target_degree_angle


class 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 = 0

    def 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 += 1
            return False
        else:
            # 收到为True的返回值时 销毁烟雾精灵
            return True


# arcade_game_202302 流浪气球Ⅰ教学版
class LiuLangQiQiu(arcade.View):
    """ 视图程序,用于在窗体内展示. """

    def __init__(self):
        # 初始化父类的属性
        super().__init__()
        self.background_image = None
        self.dao_dan = None
        # 气球类
        self.qi_qiu = None
        self.qi_qiu_list = None
        # 导弹类
        self.dao_dan = None
        self.dao_dan_list = None
        # 弹道导弹类
        self.dan_dao_dao_dan = None
        self.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 = None
        self.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 // 3
        self.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 = 70
        self.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 = 60
            self.qi_qiu.height = 40
            # self.qi_qiu.scale = 0.5
            if self.qi_qiu.center_x > self.window.width//2:
                self.qi_qiu.change_x = -self.qi_qiu.change_x
            self.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 // 3
        init_y = self.window.height // 2
        speed = 1 if abs(x - init_x) < 200 else 3
        # 如果目标点在右侧 则导弹向右飞行 横坐标变化为正数 否则为负数
        change_x = speed if x > self.window.width // 3 else - speed
        dan_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.7
        self.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_x
        yan_wu.center_y = center_y
        self.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_time
            if self.interval_time > 0.08:
                res = smoke.update()
                self.interval_time = 0
                if 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):
        """ 监听鼠标释放事件. """
        pass

    def on_mouse_motion(self, x: float, y: float, dx: float, dy: float):
        """ 监听鼠标移动事件 """
        self.couser_pic.center_x = x
        self.couser_pic.center_y = y

    def 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 // 2
        self.background_image.center_y = self.window.height // 2
        # 重新设置精灵素材宽度和高度
        self.background_image.width = self.window.width
        self.background_image.height = self.window.height
        # 设置导弹车精灵素材中心点的位置
        self.dao_dan_che_image.center_x = self.window.width // 3 - 30
        self.dao_dan_che_image.center_y = self.window.height // 2

def main():
    # 设置窗体的宽、高、名字、是否支持缩放
    window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True)
    # 设置窗体的 logo 图标
    import pyglet
    window.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_x
        self.change_y = change_y
        # 导弹精灵的初始中心点(init_x,init_y)
        self.center_x = init_x
        self.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 = 0
        if self.center_y > window_height- self.height or self.center_y < self.height:
            self.change_y = 0
            self.total_change_y = 0
        self.center_x += self.change_x
        self.center_y += self.change_y


class 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_x
        self.change_y = change_y
        self.change_y_init = change_y
        # 记录导弹精灵的初始点
        self.init_x = init_x
        self.init_y = init_y
        # 导弹精灵的初始中心点(init_x,init_y)
        self.center_x = init_x
        self.center_y = init_y
        # 要追踪的目标
        self.target_x = target_x
        self.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_x
        self.center_y += self.change_y if self.center_y < target_y else -self.change_y
        self.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 = 0
        elif abs(target_degree_angle) in range(172,188):
            self.angle = 180
        else:
            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)-90
        if (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 = 0
        elif abs(target_degree_angle) in range(172,188):
            self.angle = 180
        else:
            self.angle = target_degree_angle


class 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 = 0

    def 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 += 1
            return False
        else:
            # 收到为True的返回值时 销毁烟雾精灵
            return True


class 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*2
        self.game_info["zhui_zong_dao_dan_pic"].height = font_size*2
        self.game_info["zhui_zong_dao_dan_pic"].center_x = 10 + font_size // 2
        self.game_info["zhui_zong_dao_dan_pic"].center_y = y - font_size * 3.5
        self.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*2
        self.game_info["dan_dao_dao_dan_pic"].height = font_size*2
        self.game_info["dan_dao_dao_dan_pic"].center_x = 10 + font_size * 2 + 5*font_size
        self.game_info["dan_dao_dao_dan_pic"].center_y = y - font_size * 3.5
        self.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 // 2
            self.game_info["game_over_pic"].center_y = y // 2
            self.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 // 2
            self.game_info["game_win_pic"].center_y = y // 2
            self.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 // 2
            self.game_info["game_pause_pic"].center_y = y // 2
            self.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"] = False
            arcade.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"] = False
            arcade.stop_sound(bgm)
            self.game_info["game_win_sound"].play()
        # 游戏暂停时执行
        if self.game_info["game_pause"]:
            return False

        return self.game_info["game_status"]


# arcade_game_202302 流浪气球Ⅰ
class LiuLangQiQiu(arcade.View):
    """ 视图程序,用于在窗体内展示. """

    def __init__(self):
        # 初始化父类的属性
        super().__init__()
        self.background_image = None
        self.dao_dan = None
        # 气球类
        self.qi_qiu = None
        self.qi_qiu_list = None
        # 导弹类
        self.dao_dan = None
        self.dao_dan_list = None
        # 弹道导弹类
        self.dan_dao_dao_dan = None
        self.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 = None
        self.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 // 3
        self.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 = 70
        self.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"] += 1


    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 = 60
            self.qi_qiu.height = 40
            # self.qi_qiu.scale = 0.5
            if self.qi_qiu.center_x > self.window.width//2:
                self.qi_qiu.change_x = -self.qi_qiu.change_x
            self.qi_qiu_list.append(self.qi_qiu)
        # 每次创建过气球后 都将统计到游戏信息面板中
        self.info_panel.game_info["all_qi_qiu_num"] += num

    def 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"] -= 1

    def 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 // 3
            init_y = self.window.height // 2
            speed = 1 if abs(x - init_x) < 200 else 3
            # 如果目标点在右侧 则导弹向右飞行 横坐标变化为正数 否则为负数
            change_x = speed if x > self.window.width // 3 else - speed
            dan_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.7
            self.dan_dao_dao_dan_list.append(dan_dao_dao_dan)
            dan_dao_dao_dan.fire_sound.play()
            # 创建完毕后 剩余弹道导弹数量 -1
            self.info_panel.game_info["surplus_dan_dao_dao_dan_num"] -= 1

    def creat_yan_wu(self,center_x, center_y):
        yan_wu = BaoZhaYanWu()
        yan_wu.center_x = center_x
        yan_wu.center_y = center_y
        self.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()
                    # 剩余气球数量 -1
                    self.info_panel.game_info["surplus_qi_qiu_num"] -= 1
                    # 玩家分数 +1
                    self.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()
                    # 剩余气球数量 -1
                    self.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()
                    # 剩余气球数量 -1
                    self.info_panel.game_info["surplus_qi_qiu_num"] -= 1
                    # 玩家分数 +1
                    self.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_time
                if self.interval_time > 0.08:
                    res = smoke.update()
                    self.interval_time = 0
                    if 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):
        """ 监听鼠标释放事件. """
        pass

    def on_mouse_motion(self, x: float, y: float, dx: float, dy: float):
        """ 监听鼠标移动事件 """
        self.couser_pic.center_x = x
        self.couser_pic.center_y = y


    def 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 // 2
        self.background_image.center_y = self.window.height // 2
        # 重新设置精灵素材宽度和高度
        self.background_image.width = self.window.width
        self.background_image.height = self.window.height
        # 设置导弹车精灵素材中心点的位置
        self.dao_dan_che_image.center_x = self.window.width // 3 - 30
        self.dao_dan_che_image.center_y = self.window.height // 2
        for jing_ling in (self.dan_dao_dao_dan_list,self.dao_dan_list,self.qi_qiu_list):
            # 界面在玩耍时侯如果缩放,会使导弹和气球位置发生相对于地图的偏移
            pass



def main():
    # 设置窗体的宽、高、名字、是否支持缩放
    window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True)
    # 设置窗体的 logo 图标
    import pyglet
    window.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()


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值