基于python 实现的小游戏

飞机大战游戏 - 代码结构说明(由于文档限制,直接合并一起,支持二次修改)

主要文件:

  • main_game.py # 主游戏文件(当前文件)

核心功能:

  1. 玩家控制:方向键移动,空格键射击

  2. 多种子弹类型:普通、激光、火焰、散射子弹

  3. 波次系统:10个不同波次,每波敌人逐渐增强

  4. 道具系统:随机掉落道具改变子弹类型

  5. 音效系统:射击、击中、爆炸等音效

  6. 视觉效果:粒子效果、颜色变化、星空背景

  7. 难度递增:敌人子弹伤害和速度随波次增加

运行要求:

  • Python 3.6+

  • PyGame 库

  • 支持音频的计算机

安装和运行:

  1. 安装依赖:pip install pygame==2.5.2

  2. 直接运行:python xx.py

游戏操作:

  • ↑↓←→ 方向键:移动飞机

  • 空格键:射击

  • R键:暂停/继续游戏,游戏结束后重新开始

废话不多说:直接上

复制代码
# 导入必要的Python库
import pygame  # 游戏开发库,用于图形、声音、输入处理
import random  # 随机数生成,用于敌人位置、颜色等
import sys  # 系统功能,用于退出程序
import math  # 数学函数(虽然代码中未直接使用,但保留以备扩展)

pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)
# 初始化pygame所有模块
pygame.init()

# 在游戏参数配置部分添加声音相关参数
SOUND_VOLUME = 0.5  # 全局音量 (0.0 - 1.0)
MUSIC_VOLUME = 0.3  # 背景音乐音量

# ==================== 游戏参数配置 ====================
WIDTH, HEIGHT = 700, 800  # 游戏窗口的宽度和高度(像素)
FPS = 60  # 帧率(Frames Per Second),控制游戏运行速度
PLAYER_SPEED = 15  # 玩家飞机的移动速度
BULLET_SPEED = 10  # 子弹的飞行速度
ENEMY_SPEED = 5  # 敌人的移动速度
ENEMY_SPAWN_RATE = 5  # 敌人生成频率(帧数),值越小敌人出现越快
POWERUP_SPAWN_RATE = 300  # 道具生成频率(10秒一次)
BULLET_DURATION = 300  # 特殊子弹持续时间(300帧,约5秒)

# ==================== 颜色定义(RGB格式) ====================
BLACK = (0, 0, 0)  # 黑色 - 用于背景
WHITE = (255, 255, 255)  # 白色 - 用于文字、高光
RED = (255, 50, 50)  # 红色 - 用于敌人、危险提示
BLUE = (50, 150, 255)  # 蓝色 - 用于玩家飞机
GREEN = (50, 255, 100)  # 绿色 - 用于玩家子弹、生命值
YELLOW = (255, 255, 50)  # 黄色 - 用于粒子效果
PURPLE = (200, 50, 255)  # 紫色 - 用于特殊敌人
CYAN = (0, 255, 255)  # 青色 - 用于激光子弹
ORANGE = (255, 165, 0)  # 橙色 - 用于火焰子弹
PINK = (255, 105, 180)  # 粉色 - 用于道具

# ==================== 子弹类型定义 ====================
BULLET_TYPES = {
    "normal": {
        "color": GREEN,
        "damage": 100,  # 修改为25
        "speed": -BULLET_SPEED,
        "width": 4,
        "height": 20,
        "cooldown": 10,
        "name": "普通子弹"
    },
    "laser": {
        "color": CYAN,
        "damage": 100,  # 修改为75
        "speed": -BULLET_SPEED * 1.5,
        "width": 6,
        "height": 20,
        "cooldown": 5,
        "name": "激光子弹"
    },
    "fire": {
        "color": ORANGE,
        "damage": 100,  # 修改为50
        "speed": -BULLET_SPEED * 0.8,
        "width": 8,
        "height": 20,
        "cooldown": 15,
        "name": "火焰子弹"
    },
    "spread": {
        "color": PURPLE,
        "damage": 100,  # 修改为100
        "speed": -BULLET_SPEED,
        "width": 10,
        "height": 20,
        "cooldown": 8,
        "name": "散射子弹"
    }
}

# ==================== 背景颜色定义(不同波次不同颜色) ====================
BACKGROUND_COLORS = {
    1: (0, 0, 0),  # 波次1:黑色
    2: (10, 10, 40),  # 波次2:深蓝色
    3: (20, 0, 20),  # 波次3:深紫色
    4: (40, 20, 0),  # 波次4:深棕色
    5: (0, 20, 20),  # 波次5:深青色
    6: (30, 0, 30),  # 波次6:深洋红
    7: (20, 20, 0),  # 波次7:深橄榄色
    8: (0, 30, 30),  # 波次8:青色
    9: (30, 15, 0),  # 波次9:橙色
    10: (40, 0, 40),  # 波次10:深粉红
}

# ==================== 敌人颜色定义(不同波次不同颜色) ====================
ENEMY_COLORS_BY_WAVE = {
    1: [RED, PURPLE, YELLOW],  # 波次1:红、紫、黄
    2: [(255, 100, 100), (200, 100, 255), (255, 255, 150)],  # 波次2:浅色系
    3: [(150, 50, 50), (150, 50, 150), (150, 150, 50)],  # 波次3:暗色系
    4: [(255, 150, 50), (50, 255, 150), (150, 50, 255)],  # 波次4:鲜艳色
    5: [(200, 0, 0), (0, 200, 0), (0, 0, 200)],  # 波次5:三原色
    6: [(255, 200, 0), (0, 255, 200), (200, 0, 255)],  # 波次6:亮色
    7: [(180, 100, 50), (50, 180, 100), (100, 50, 180)],  # 波次7:混合色
    8: [(220, 220, 0), (0, 220, 220), (220, 0, 220)],  # 波次8:亮对比色
    9: [(255, 100, 0), (0, 255, 100), (100, 0, 255)],  # 波次9:鲜艳混合色
    10: [(255, 50, 100), (100, 255, 50), (50, 100, 255)],  # 波次10:最终波次
}


# 在颜色定义后添加声音加载函数
def load_sounds():
    """加载游戏音效(如果找不到文件,使用模拟音效)"""
    sounds = {}

    try:
        # 射击音效
        sounds["shoot_normal"] = pygame.mixer.Sound("shoot_normal.wav")
        sounds["shoot_laser"] = pygame.mixer.Sound("shoot_laser.wav")
        sounds["shoot_fire"] = pygame.mixer.Sound("shoot_fire.wav")
        sounds["shoot_spread"] = pygame.mixer.Sound("shoot_spread.wav")

        # 击中音效
        sounds["hit_enemy"] = pygame.mixer.Sound("hit_enemy.wav")
        sounds["explosion"] = pygame.mixer.Sound("explosion.wav")

        # 道具音效
        sounds["powerup"] = pygame.mixer.Sound("powerup.wav")

        # 玩家音效
        sounds["player_hit"] = pygame.mixer.Sound("player_hit.wav")
        sounds["game_over"] = pygame.mixer.Sound("game_over.wav")

        # 背景音乐
        pygame.mixer.music.load("bg_music.mp3")

        # 设置音量
        for sound in sounds.values():
            sound.set_volume(SOUND_VOLUME)
        pygame.mixer.music.set_volume(MUSIC_VOLUME)

    except (pygame.error, FileNotFoundError):
        # 如果找不到音频文件,使用PyGame的生成音效功能
        print("音频文件未找到,使用模拟音效...")
        sounds = create_synthetic_sounds()

    return sounds


def create_synthetic_sounds():
    """创建模拟音效(当找不到音频文件时使用)"""
    sounds = {}

    # 生成不同频率的方波音效
    sample_rate = 22050

    # 普通射击音效 (短促高频)
    arr = pygame.sndarray.array(pygame.mixer.Sound(buffer=pygame.mixer.Sound(buffer=bytearray([128] * 1000))))
    sounds["shoot_normal"] = pygame.mixer.Sound(buffer=arr)

    # 激光射击音效 (较长中频)
    arr = pygame.sndarray.array(pygame.mixer.Sound(buffer=pygame.mixer.Sound(buffer=bytearray([150] * 1500))))
    sounds["shoot_laser"] = pygame.mixer.Sound(buffer=arr)

    # 火焰射击音效 (低频)
    arr = pygame.sndarray.array(pygame.mixer.Sound(buffer=pygame.mixer.Sound(buffer=bytearray([100] * 2000))))
    sounds["shoot_fire"] = pygame.mixer.Sound(buffer=arr)

    # 散射射击音效 (多频混合)
    arr = pygame.sndarray.array(pygame.mixer.Sound(buffer=pygame.mixer.Sound(buffer=bytearray([200] * 800))))
    sounds["shoot_spread"] = pygame.mixer.Sound(buffer=arr)

    # 击中音效
    arr = pygame.sndarray.array(pygame.mixer.Sound(buffer=pygame.mixer.Sound(buffer=bytearray([180] * 1200))))
    sounds["hit_enemy"] = pygame.mixer.Sound(buffer=arr)

    # 爆炸音效
    arr = pygame.sndarray.array(pygame.mixer.Sound(buffer=pygame.mixer.Sound(buffer=bytearray([255] * 2500))))
    sounds["explosion"] = pygame.mixer.Sound(buffer=arr)

    # 道具音效
    arr = pygame.sndarray.array(pygame.mixer.Sound(buffer=pygame.mixer.Sound(buffer=bytearray([220] * 1000))))
    sounds["powerup"] = pygame.mixer.Sound(buffer=arr)

    # 玩家被击中音效
    arr = pygame.sndarray.array(pygame.mixer.Sound(buffer=pygame.mixer.Sound(buffer=bytearray([200] * 800))))
    sounds["player_hit"] = pygame.mixer.Sound(buffer=arr)

    # 游戏结束音效
    arr = pygame.sndarray.array(pygame.mixer.Sound(buffer=pygame.mixer.Sound(buffer=bytearray([100] * 3000))))
    sounds["game_over"] = pygame.mixer.Sound(buffer=arr)

    # 设置音量
    for sound in sounds.values():
        sound.set_volume(SOUND_VOLUME)

    # 修复:使用try-except处理背景音乐加载错误
    try:
        # 创建一个简单的循环音效作为背景音乐
        pygame.mixer.music.set_volume(0.1)  # 非常低的音量
        # 这里不加载音乐文件,而是设置为空
        # 在main函数中,我们会检查音乐是否已加载
        print("模拟音效创建成功,背景音乐已禁用")
    except Exception as e:
        print(f"创建背景音乐失败: {e}")

    return sounds


# ==================== Player类 - 玩家飞机 ====================
class Player:
    def __init__(self):
        """初始化玩家飞机的所有属性"""
        self.x = WIDTH // 2  # 初始水平位置(窗口中心)
        self.y = HEIGHT - 100  # 初始垂直位置(距底部100像素)
        self.width = 50  # 飞机宽度
        self.height = 40  # 飞机高度
        self.speed = PLAYER_SPEED  # 移动速度
        self.color = BLUE  # 飞机颜色
        self.health = 100  # 生命值(0-100)
        self.score = 0  # 游戏得分
        self.shoot_cooldown = 0  # 射击冷却计时器(防止连续射击)
        self.invincible = 0  # 无敌时间计数器(被击中后短暂无敌)
        self.bullet_type = "normal"  # 当前子弹类型
        self.bullet_timer = 0  # 特殊子弹持续时间计时器
        self.powerup_effect = None  # 当前道具效果

    def move(self, keys):
        """根据按键控制玩家飞机移动"""
        # 左移:检查左方向键是否按下且未到达左边界
        if keys[pygame.K_LEFT] and self.x > 0:
            self.x -= self.speed
        # 右移:检查右方向键是否按下且未到达右边界
        if keys[pygame.K_RIGHT] and self.x < WIDTH - self.width:
            self.x += self.speed
        # 上移:检查上方向键是否按下且未到达上边界
        if keys[pygame.K_UP] and self.y > 0:
            self.y -= self.speed
        # 下移:检查下方向键是否按下且未到达下边界
        if keys[pygame.K_DOWN] and self.y < HEIGHT - self.height:
            self.y += self.speed

        # 减少射击冷却时间(如果大于0)
        if self.shoot_cooldown > 0:
            self.shoot_cooldown -= 1

        # 减少无敌时间(如果大于0)
        if self.invincible > 0:
            self.invincible -= 1

        # 减少特殊子弹持续时间
        if self.bullet_timer > 0:
            self.bullet_timer -= 1
            # 如果时间用完,恢复为普通子弹
            if self.bullet_timer == 0 and self.bullet_type != "normal":
                self.bullet_type = "normal"
                self.shoot_cooldown = 0

    def shoot(self, bullets, sounds=None):
        """玩家射击,根据当前子弹类型创建子弹并播放音效"""
        # 检查射击冷却是否结束(为0表示可以射击)
        if self.shoot_cooldown == 0:
            bullet_config = BULLET_TYPES[self.bullet_type]

            # 播放射击音效
            if sounds:
                sound_key = f"shoot_{self.bullet_type}"
                if sound_key in sounds:
                    sounds[sound_key].play()

            # 这里开始原有的射击逻辑
            if self.bullet_type == "normal":
                # 普通子弹:单发
                bullets.append(Bullet(
                    self.x,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type
                ))
                # 普通子弹:单发
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,  # 中间
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type
                ))
                # 普通子弹:单发
                bullets.append(Bullet(
                    self.x + self.width - bullet_config["width"],  # 右侧
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type
                ))

            elif self.bullet_type == "laser":
                # 激光子弹:快速单发
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=-30  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=-10  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=0  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=10  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=30  # 向右偏5度
                ))

            elif self.bullet_type == "fire":
                # 火焰子弹:大威力慢速子弹
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=-50  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=-40  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=-30  # 向右偏5度
                ))
                # 火焰子弹:大威力慢速子弹
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=-20  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=-10  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=0  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=10  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=20  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=30  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=40  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=50  # 向右偏5度
                ))

            elif self.bullet_type == "spread":
                # 散射子弹:多发射击
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=-80  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=-70  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=-60  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=-50  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=-40  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=-30  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=-20  # 向左偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=-10  # 向左偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=0  # 直射
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=10  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=20  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=30  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=40  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=50  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=60  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=70  # 向右偏5度
                ))
                bullets.append(Bullet(
                    self.x + self.width // 2 - bullet_config["width"] // 2,
                    self.y,
                    bullet_config["speed"],
                    bullet_config["color"],
                    bullet_config["damage"],
                    self.bullet_type,
                    angle=80  # 向右偏5度
                ))

            # 设置射击冷却时间
            self.shoot_cooldown = bullet_config["cooldown"]

    def change_bullet_type(self, new_type):
        """更改玩家子弹类型"""
        if new_type in BULLET_TYPES:
            self.bullet_type = new_type
            self.bullet_timer = BULLET_DURATION  # 设置持续时间

    def draw(self, screen, wave):
        """在屏幕上绘制玩家飞机和生命值条"""
        # 根据无敌状态选择颜色:无敌时闪烁白色,否则用正常颜色
        if self.invincible > 0 and self.invincible % 4 < 2:
            color = WHITE  # 每4帧切换2次,实现闪烁效果
        else:
            color = self.color

        # 根据波次改变飞机颜色
        wave_color = self.get_wave_color(wave)

        # 绘制飞机主体(三角形):
        points = [
            (self.x + self.width // 2, self.y),  # 机头顶点
            (self.x, self.y + self.height),  # 左下角
            (self.x + self.width, self.y + self.height)  # 右下角
        ]
        pygame.draw.polygon(screen, wave_color, points)  # 绘制填充三角形

        # 绘制机翼(矩形):
        wing_width = 50  # 机翼宽度
        wing_points = [
            (self.x + self.width // 2 - wing_width // 2, self.y + 20),  # 左上
            (self.x + self.width // 2 - wing_width // 2, self.y + self.height - 10),  # 左下
            (self.x + self.width // 2 + wing_width // 2, self.y + self.height - 10),  # 右下
            (self.x + self.width // 2 + wing_width // 2, self.y + 20)  # 右上
        ]
        pygame.draw.polygon(screen, wave_color, wing_points)  # 绘制矩形机翼

        # 绘制驾驶舱(两个同心圆,模拟窗户效果)
        pygame.draw.circle(screen, WHITE, (self.x + self.width // 2, self.y + 15), 8)  # 外层白圈
        pygame.draw.circle(screen, wave_color, (self.x + self.width // 2, self.y + 15), 5)  # 内层使用波次颜色

        # 绘制生命值条(在飞机上方显示)
        health_width = 50  # 生命值条总宽度
        health_height = 6  # 生命值条高度
        health_x = self.x + self.width // 2 - health_width // 2  # 水平居中
        health_y = self.y - 10  # 飞机上方10像素

        # 绘制背景(红色,表示空的血量)
        pygame.draw.rect(screen, RED, (health_x, health_y, health_width, health_height))
        # 绘制当前生命值(绿色,宽度根据生命值比例计算)
        current_health = max(0, self.health)  # 确保生命值不小于0
        pygame.draw.rect(screen, GREEN, (health_x, health_y, health_width * current_health / 100, health_height))

    def get_wave_color(self, wave):
        """根据波次获取飞机颜色"""
        # 波次1-10的颜色循环
        wave_colors = [
            BLUE,  # 波次1:蓝色
            CYAN,  # 波次2:青色
            GREEN,  # 波次3:绿色
            YELLOW,  # 波次4:黄色
            ORANGE,  # 波次5:橙色
            RED,  # 波次6:红色
            PURPLE,  # 波次7:紫色
            PINK,  # 波次8:粉色
            (100, 200, 255),  # 波次9:浅蓝色
            (255, 200, 100)  # 波次10:金色
        ]
        wave_index = (wave - 1) % len(wave_colors)
        return wave_colors[wave_index]


# ==================== Bullet类 - 子弹 ====================
class Bullet:
    def __init__(self, x, y, speed, color, damage, bullet_type, angle=0):
        """初始化子弹属性"""
        self.x = x  # 子弹水平位置
        self.y = y  # 子弹垂直位置
        self.speed = speed  # 子弹速度(正数向下,负数向上)
        self.width = BULLET_TYPES[bullet_type]["width"]
        self.height = BULLET_TYPES[bullet_type]["height"]
        self.color = color  # 子弹颜色
        self.damage = damage  # 子弹伤害
        self.bullet_type = bullet_type  # 子弹类型
        self.angle = angle  # 子弹角度(用于散射)
        self.speed_x = math.sin(math.radians(angle)) * abs(speed) * 0.5  # 水平速度分量
        self.speed_y = speed  # 垂直速度分量

    def update(self):
        """更新子弹位置(每帧调用)"""
        self.x += self.speed_x  # 水平移动(如果有角度)
        self.y += self.speed_y  # 垂直移动

    def draw(self, screen):
        """绘制子弹(根据类型不同绘制)"""
        if self.bullet_type in ["normal", "spread"]:
            # 普通子弹和散射子弹:矩形+发光效果
            pygame.draw.rect(screen, self.color, (self.x, self.y, self.width, self.height))
            # 绘制子弹发光效果(尾部的白色光晕)
            pygame.draw.circle(screen, WHITE,
                               (self.x + self.width // 2, self.y + self.height),
                               self.width // 2 + 1)

        elif self.bullet_type == "laser":
            # 激光子弹:细长发光效果
            pygame.draw.line(screen, self.color,
                             (self.x + self.width // 2, self.y),
                             (self.x + self.width // 2, self.y + self.height),
                             self.width)
            # 激光光晕
            pygame.draw.line(screen, WHITE,
                             (self.x + self.width // 2, self.y),
                             (self.x + self.width // 2, self.y + self.height),
                             max(1, self.width // 2))

        elif self.bullet_type == "fire":
            # 火焰子弹:渐变效果
            # 火焰核心(亮色)
            pygame.draw.rect(screen, self.color, (self.x, self.y, self.width, self.height))
            # 火焰外层(暗色)
            outer_color = (ORANGE[0], ORANGE[1] // 2, 0)  # 深橙色
            pygame.draw.rect(screen, outer_color,
                             (self.x + 1, self.y + 1,
                              self.width - 2, self.height - 2))
            # 火焰尖端
            tip_points = [
                (self.x + self.width // 2, self.y),
                (self.x + self.width // 4, self.y + self.height // 3),
                (self.x + 3 * self.width // 4, self.y + self.height // 3)
            ]
            pygame.draw.polygon(screen, WHITE, tip_points)


# ==================== PowerUp类 - 道具 ====================
class PowerUp:
    def __init__(self):
        """初始化道具属性"""
        self.width = 30  # 道具宽度
        self.height = 30  # 道具高度
        self.x = random.randint(0, WIDTH - self.width)  # 随机水平位置
        self.y = -self.height  # 从屏幕上方开始
        self.speed = random.randint(2, 4)  # 下落速度
        self.type = random.choice(list(BULLET_TYPES.keys())[1:])  # 随机子弹类型(除了普通子弹)
        self.color = PINK  # 道具颜色
        self.flash_timer = 0  # 闪烁计时器

    def update(self):
        """更新道具位置"""
        self.y += self.speed  # 向下移动
        self.flash_timer += 1  # 更新闪烁计时器

    def draw(self, screen):
        """绘制道具(菱形,带闪烁效果)"""
        # 计算闪烁颜色
        if self.flash_timer % 20 < 10:
            draw_color = self.color
        else:
            draw_color = WHITE  # 闪烁为白色

        # 绘制菱形(4个点)
        points = [
            (self.x + self.width // 2, self.y),  # 上顶点
            (self.x + self.width, self.y + self.height // 2),  # 右顶点
            (self.x + self.width // 2, self.y + self.height),  # 下顶点
            (self.x, self.y + self.height // 2)  # 左顶点
        ]
        pygame.draw.polygon(screen, draw_color, points)

        # 绘制边框
        pygame.draw.polygon(screen, WHITE, points, 2)

        # 绘制子弹类型标识
        font = pygame.font.SysFont('simhei', 12)
        type_text = font.render(BULLET_TYPES[self.type]["name"][:2], True, BLACK)
        screen.blit(type_text, (self.x + self.width // 2 - type_text.get_width() // 2,
                                self.y + self.height // 2 - type_text.get_height() // 2))


# ==================== Enemy类 - 敌人飞机 ====================
class Enemy:
    def __init__(self, wave):
        """初始化敌人飞机属性(随机生成)"""
        self.width = random.randint(30, 60)  # 随机宽度(30-60像素)
        self.height = random.randint(30, 50)  # 随机高度(30-50像素)
        self.x = random.randint(0, WIDTH - self.width)  # 随机水平位置
        self.y = -self.height  # 从屏幕上方开始(不可见位置)
        self.speed = random.randint(2, 10)  # 随机速度(2-5像素/帧)
        self.wave = wave  # 记录波次
        # 根据波次选择颜色
        if wave in ENEMY_COLORS_BY_WAVE:
            self.color = random.choice(ENEMY_COLORS_BY_WAVE[wave])
        else:
            self.color = random.choice([RED, PURPLE, YELLOW])  # 默认颜色
        self.health = random.randint(20, 100)  # 随机生命值(20-50)
        self.max_health = self.health  # 记录最大生命值(用于显示比例)
        self.shoot_timer = random.randint(30, 90)  # 随机射击间隔(30-90帧)

        # 新增:根据波次计算敌机子弹的伤害和速度
        # 敌机子弹基础伤害10,每波增加3,最高不超过50
        self.bullet_damage = min(10 + (wave - 1) * 3, 30)
        # 敌机子弹基础速度BULLET_SPEED,每波增加0.5,最高不超过20
        self.bullet_speed = min(BULLET_SPEED + (wave - 1) * 0.3, 10)
        # 敌机射击冷却时间减少,每波减少2帧,最低不低于20帧
        self.shoot_interval = max(30 - (wave - 1) * 2, 20)

    def update(self, bullets, sounds=None):
        """更新敌人状态(移动、射击)"""
        self.y += self.speed  # 向下移动
        self.shoot_timer -= 1  # 减少射击计时器

        # 当射击计时器为0时,敌人发射子弹
        if self.shoot_timer <= 0:
            # 创建子弹:从敌人底部发射,向下飞行(正速度),红色
            # 使用根据波次计算的子弹伤害和速度
            bullets.append(Bullet(self.x + self.width // 2 - 2,
                                  self.y + self.height,
                                  self.bullet_speed,  # 使用根据波次计算的子弹速度
                                  RED,
                                  self.bullet_damage,  # 使用根据波次计算的子弹伤害
                                  "normal"))
            # 重置射击计时器(根据波次调整射击间隔)
            self.shoot_timer = random.randint(self.shoot_interval, self.shoot_interval + 10)

            # 播放敌人射击音效(如果有)
            if sounds and "shoot_normal" in sounds:
                # 稍微降低敌人射击音量
                sounds["shoot_normal"].set_volume(SOUND_VOLUME * 0.5)
                sounds["shoot_normal"].play()
                sounds["shoot_normal"].set_volume(SOUND_VOLUME)

    def draw(self, screen):
        """绘制敌人飞机和生命值条"""
        # 绘制敌人主体(倒三角形,与玩家相反)
        points = [
            (self.x + self.width // 2, self.y + self.height),  # 底部中点
            (self.x, self.y),  # 左上角
            (self.x + self.width, self.y)  # 右上角
        ]
        pygame.draw.polygon(screen, self.color, points)

        # 绘制眼睛(两个白色圆圈,中间黑色瞳孔)
        eye_size = 6  # 眼睛大小
        # 左眼
        pygame.draw.circle(screen, WHITE, (self.x + self.width // 3, self.y + 15), eye_size)
        pygame.draw.circle(screen, BLACK, (self.x + self.width // 3, self.y + 15), eye_size // 2)
        # 右眼
        pygame.draw.circle(screen, WHITE, (self.x + 2 * self.width // 3, self.y + 15), eye_size)
        pygame.draw.circle(screen, BLACK, (self.x + 2 * self.width // 3, self.y + 15), eye_size // 2)

        # 绘制生命值条(在敌人上方显示)
        health_width = self.width  # 生命值条宽度等于敌人宽度
        health_height = 4  # 生命值条高度
        health_x = self.x  # 水平位置对齐敌人
        health_y = self.y - 8  # 敌人上方8像素

        # 绘制背景(红色)
        pygame.draw.rect(screen, RED, (health_x, health_y, health_width, health_height))
        # 绘制当前生命值(绿色,宽度根据生命值比例计算)
        health_ratio = max(0, self.health / self.max_health)  # 生命值比例(0-1)
        pygame.draw.rect(screen, GREEN, (health_x, health_y, health_width * health_ratio, health_height))


# ==================== Particle类 - 粒子效果 ====================
class Particle:
    def __init__(self, x, y, color):
        """初始化粒子属性(用于爆炸、击中效果)"""
        self.x = x  # 粒子水平位置
        self.y = y  # 粒子垂直位置
        self.color = color  # 粒子颜色
        self.size = random.randint(3, 8)  # 随机大小(3-8像素)
        # 随机速度(X和Y方向,-3到3之间)
        self.speed_x = random.uniform(-3, 3)
        self.speed_y = random.uniform(-3, 3)
        self.life = random.randint(20, 40)  # 随机生命周期(20-40帧)

    def update(self):
        """更新粒子状态(移动、缩小、减少生命)"""
        self.x += self.speed_x  # 水平移动
        self.y += self.speed_y  # 垂直移动
        self.life -= 1  # 减少生命值
        self.size = max(0, self.size - 0.1)  # 逐渐缩小(最小为0)

    def draw(self, screen):
        """绘制粒子(如果还有生命)"""
        if self.life > 0:
            # 绘制圆形粒子
            pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), int(self.size))


# ==================== 背景绘制函数 ====================
def draw_background(screen, stars, wave):
    """绘制星空背景并更新星星位置"""
    # 根据波次选择背景颜色
    if wave in BACKGROUND_COLORS:
        bg_color = BACKGROUND_COLORS[wave]
    else:
        bg_color = BACKGROUND_COLORS[10]  # 超过10波使用第10波的颜色

    # 填充背景颜色
    screen.fill(bg_color)

    # 绘制所有星星
    for star in stars:
        # 根据星星类型决定大小:类型0为小星(1像素),类型1为大星(2像素)
        size = 1 if star[2] == 0 else 2
        # 根据波次和星星类型决定亮度
        if wave <= 3:
            brightness = 150 + star[2] * 50  # 前3波较亮
        elif wave <= 6:
            brightness = 120 + star[2] * 40  # 4-6波中等亮度
        else:
            brightness = 100 + star[2] * 30  # 7波以后较暗

        # 根据波次调整星星颜色
        if wave % 3 == 1:
            color = (brightness, brightness, brightness)  # 白色星星
        elif wave % 3 == 2:
            color = (brightness, brightness // 2, brightness // 2)  # 偏红色星星
        else:
            color = (brightness // 2, brightness // 2, brightness)  # 偏蓝色星星

        pygame.draw.circle(screen, color, (star[0], star[1]), size)

    # 更新所有星星位置(向下移动)
    for i in range(len(stars)):
        # 星星向下移动速度根据波次调整
        speed_multiplier = 1 + (wave - 1) * 0.1  # 每波增加10%速度
        stars[i] = (stars[i][0], stars[i][1] + speed_multiplier, stars[i][2])
        # 如果星星移出屏幕底部,重置到顶部
        if stars[i][1] > HEIGHT:
            stars[i] = (random.randint(0, WIDTH), 0, random.randint(0, 1))


# ==================== UI绘制函数 ====================
def draw_ui(screen, player, game_over, wave, powerups, paused=False):
    """绘制用户界面(分数、生命值、游戏结束画面等)"""
    # 创建字体对象
    font = pygame.font.SysFont('simhei', 16)  # 普通字体,24号
    large_font = pygame.font.SysFont('simhei', 18, bold=True)  # 大字体,48号,加粗

    # 绘制分数和波次(左上角)
    score_text = font.render(f'分数: {player.score}', True, WHITE)
    wave_text = font.render(f'波次: {wave}', True, WHITE)
    screen.blit(score_text, (10, 10))  # 在(10,10)位置绘制分数
    screen.blit(wave_text, (10, 40))  # 在(10,40)位置绘制波次

    # 绘制当前波次敌人子弹信息
    if wave > 1:
        # 计算当前波次敌机子弹伤害和速度
        enemy_bullet_damage = min(10 + (wave - 1) * 3, 30)
        enemy_bullet_speed = min(BULLET_SPEED + (wave - 1) * 0.5, 20)
        enemy_info = font.render(f'敌机子弹: 伤害{enemy_bullet_damage} 速度{enemy_bullet_speed:.1f}', True, YELLOW)
        screen.blit(enemy_info, (10, 70))

    # 绘制生命值(右上角)
    health_text = font.render(f'生命值: {player.health}', True, WHITE)
    screen.blit(health_text, (WIDTH - 150, 10))  # 在窗口宽度-120的位置

    # 绘制当前子弹信息
    current_bullet = BULLET_TYPES[player.bullet_type]
    bullet_text = font.render(f'当前子弹: {current_bullet["name"]}', True, current_bullet["color"])
    screen.blit(bullet_text, (WIDTH - 150, 40))

    # 绘制子弹剩余时间(如果是特殊子弹)
    if player.bullet_timer > 0 and player.bullet_type != "normal":
        time_left = player.bullet_timer // FPS  # 转换为秒
        timer_text = font.render(f'剩余时间: {time_left}秒', True, YELLOW)
        screen.blit(timer_text, (WIDTH - 150, 70))

        # 绘制时间进度条
        timer_width = 140
        timer_height = 6
        timer_x = WIDTH - timer_width - 10
        timer_y = 100
        # 背景
        pygame.draw.rect(screen, RED, (timer_x, timer_y, timer_width, timer_height))
        # 进度
        progress = player.bullet_timer / BULLET_DURATION
        pygame.draw.rect(screen, current_bullet["color"],
                         (timer_x, timer_y, timer_width * progress, timer_height))

    # 绘制操作提示(底部居中)- 根据状态显示不同提示
    if paused:
        controls = font.render('游戏暂停 - 按R键继续游戏', True, YELLOW)
    elif game_over:
        controls = font.render('游戏结束 - 按R键重新开始', True, GREEN)
    else:
        controls = font.render('方向键移动,空格射击,R暂停游戏', True, WHITE)

    screen.blit(controls, (WIDTH // 2 - controls.get_width() // 2, HEIGHT - 50))

    # 如果游戏暂停,绘制暂停画面
    if paused and not game_over:
        # 创建半透明黑色覆盖层(150透明度,0-255)
        overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
        overlay.fill((0, 0, 0, 150))  # 黑色,150透明度
        screen.blit(overlay, (0, 0))  # 覆盖整个屏幕

        # 暂停文字
        pause_text = large_font.render('游戏暂停', True, YELLOW)
        continue_text = font.render('按R键继续游戏', True, GREEN)

        # 居中显示暂停文字
        screen.blit(pause_text, (WIDTH // 2 - pause_text.get_width() // 2, HEIGHT // 2 - 60))
        screen.blit(continue_text, (WIDTH // 2 - continue_text.get_width() // 2, HEIGHT // 2))

    # 如果游戏结束,绘制结束画面
    if game_over:
        # 创建半透明黑色覆盖层(200透明度,0-255)
        overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
        overlay.fill((0, 0, 0, 200))  # 黑色,200透明度
        screen.blit(overlay, (0, 0))  # 覆盖整个屏幕

        # 游戏结束文字
        game_over_text = large_font.render('游戏结束!', True, RED)
        # 最终分数
        final_score = font.render(f'最终分数: {player.score}', True, WHITE)
        # 最终波次
        final_wave = font.render(f'到达波次: {wave}', True, WHITE)
        # 重新开始提示
        restart_text = font.render('按R键重新开始', True, GREEN)

        # 居中显示所有文字
        screen.blit(game_over_text, (WIDTH // 2 - game_over_text.get_width() // 2, HEIGHT // 2 - 60))
        screen.blit(final_score, (WIDTH // 2 - final_score.get_width() // 2, HEIGHT // 2 - 20))
        screen.blit(final_wave, (WIDTH // 2 - final_wave.get_width() // 2, HEIGHT // 2 + 20))
        screen.blit(restart_text, (WIDTH // 2 - restart_text.get_width() // 2, HEIGHT // 2 + 60))


# ==================== 主函数 - 游戏主循环 ====================
def main():
    """游戏主函数,包含游戏主循环"""
    # 初始化游戏窗口
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption('飞机大战 - 多种子弹系统')
    clock = pygame.time.Clock()  # 创建时钟对象,用于控制帧率

    # 初始化游戏对象
    player = Player()  # 创建玩家对象
    bullets = []  # 玩家子弹列表
    enemy_bullets = []  # 敌人子弹列表
    enemies = []  # 敌人列表
    particles = []  # 粒子效果列表
    powerups = []  # 道具列表

    # 创建星空背景(100颗随机分布的星星)
    stars = [(random.randint(0, WIDTH), random.randint(0, HEIGHT), random.randint(0, 1))
             for _ in range(100)]

    # 游戏状态变量
    game_over = False  # 游戏是否结束
    paused = False  # 新增:游戏是否暂停
    wave = 1  # 当前波次
    enemy_spawn_timer = 0  # 敌人生成计时器
    powerup_spawn_timer = 0  # 道具生成计时器
    wave_enemies_spawned = 0  # 当前波次已生成的敌人数量
    wave_enemies_target = 5  # 当前波次要生成的敌人总数

    # 添加R键防连按计时器
    r_key_cooldown = 0

    # 记录玩家上一帧的生命值
    player_prev_health = player.health

    # 加载音效
    sounds = load_sounds()

    # 修复:添加检查,确保音乐已加载
    try:
        pygame.mixer.music.play(-1)  # -1表示无限循环
    except pygame.error:
        print("背景音乐不可用,继续游戏(无背景音乐)")

    # ========== 游戏主循环 ==========
    while True:
        # ---------- 事件处理 ----------
        for event in pygame.event.get():
            # 如果点击关闭按钮,退出游戏
            if event.type == pygame.QUIT:
                pygame.quit()  # 关闭pygame
                sys.exit()  # 退出程序

            # 按键事件处理
            if event.type == pygame.KEYDOWN:
                # 空格键:玩家射击(仅当游戏未结束且未暂停时)
                if event.key == pygame.K_SPACE and not game_over and not paused:
                    player.shoot(bullets, sounds)

                # R键:根据游戏状态处理(不区分大小写)
                # pygame.K_r 已经处理了大小写,无论是按R还是r都会触发
                elif event.key == pygame.K_r and r_key_cooldown == 0:
                    if game_over:
                        # 游戏结束时:重新开始游戏
                        player = Player()
                        bullets = []
                        enemy_bullets = []
                        enemies = []
                        particles = []
                        powerups = []
                        game_over = False
                        paused = False  # 确保重新开始后不是暂停状态
                        wave = 1
                        wave_enemies_spawned = 0
                        enemy_spawn_timer = 0
                        powerup_spawn_timer = 0
                        # 重新创建星星背景
                        stars = [(random.randint(0, WIDTH), random.randint(0, HEIGHT), random.randint(0, 1))
                                 for _ in range(100)]
                        r_key_cooldown = 30  # 设置冷却时间,防止立即又暂停
                        # 重新开始背景音乐
                        try:
                            pygame.mixer.music.play(-1)
                        except pygame.error:
                            pass
                        player_prev_health = player.health
                    else:
                        # 游戏进行中:切换暂停/继续状态
                        paused = not paused
                        r_key_cooldown = 30  # 设置冷却时间,防止连续切换
                        if paused:
                            try:
                                pygame.mixer.music.pause()  # 暂停时暂停背景音乐
                            except:
                                pass
                        else:
                            try:
                                pygame.mixer.music.unpause()  # 继续时恢复背景音乐
                            except:
                                pass

        # 减少R键冷却时间
        if r_key_cooldown > 0:
            r_key_cooldown -= 1

        # ---------- 游戏逻辑更新(仅当游戏未结束且未暂停时) ----------
        if not game_over and not paused:
            # 玩家移动(检测当前按下的所有键)
            keys = pygame.key.get_pressed()
            player.move(keys)

            # 持续射击检测(按住空格键)- 使用修复后的方法
            if keys[pygame.K_SPACE]:
                player.shoot(bullets, sounds)

            # 敌人生成逻辑
            enemy_spawn_timer += 1  # 增加计时器
            # 如果计时器达到生成频率且当前波次敌人未生成完
            if enemy_spawn_timer >= ENEMY_SPAWN_RATE and wave_enemies_spawned < wave_enemies_target:
                # 创建敌人时传入当前波次
                enemies.append(Enemy(wave))
                enemy_spawn_timer = 0  # 重置计时器
                wave_enemies_spawned += 1  # 增加已生成敌人数

            # 道具生成逻辑
            powerup_spawn_timer += 1
            if powerup_spawn_timer >= POWERUP_SPAWN_RATE:
                powerups.append(PowerUp())
                powerup_spawn_timer = 0

            # 波次更新逻辑
            # 如果当前波次敌人已生成完且所有敌人都被消灭
            if wave_enemies_spawned >= wave_enemies_target and len(enemies) == 0:
                wave += 1  # 增加波次
                wave_enemies_spawned = 0  # 重置已生成敌人数
                wave_enemies_target = 5 + wave * 2  # 下一波敌人数量增加(5 + 波次×2)
                # 玩家生命值回复(最多回复到100)
                player.health = min(100, player.health + 100)

                # # 显示波次更新信息
                # print(
                #     f"进入第{wave}波!敌机子弹速度:{min(BULLET_SPEED + (wave - 1) * 0.5, 20):.1f},伤害:{min(10 + (wave - 1) * 3, 50)}")

            # ---------- 子弹更新 ----------
            # 玩家子弹更新
            bullets_to_remove = []
            for bullet in bullets:
                bullet.update()  # 更新子弹位置
                # 如果子弹飞出屏幕,标记为需要移除
                if bullet.y < -20 or bullet.y > HEIGHT + 20 or bullet.x < -20 or bullet.x > WIDTH + 20:
                    bullets_to_remove.append(bullet)

            # 移除标记的子弹
            for bullet in bullets_to_remove:
                if bullet in bullets:
                    bullets.remove(bullet)

            # 敌人子弹更新
            enemy_bullets_to_remove = []
            for bullet in enemy_bullets:
                bullet.update()
                if bullet.y < -20 or bullet.y > HEIGHT + 20:
                    enemy_bullets_to_remove.append(bullet)

            for bullet in enemy_bullets_to_remove:
                if bullet in enemy_bullets:
                    enemy_bullets.remove(bullet)

            # ---------- 道具更新 ----------
            powerups_to_remove = []
            for powerup in powerups:
                powerup.update()

                # 如果道具移出屏幕,标记为需要移除
                if powerup.y > HEIGHT:
                    powerups_to_remove.append(powerup)
                    continue

                # 检测玩家是否接到道具
                if (player.x < powerup.x + powerup.width and
                        player.x + player.width > powerup.x and
                        player.y < powerup.y + powerup.height and
                        player.y + player.height > powerup.y):

                    # 改变玩家子弹类型
                    player.change_bullet_type(powerup.type)
                    powerups_to_remove.append(powerup)

                    # 播放获得道具音效
                    if sounds and "powerup" in sounds:
                        sounds["powerup"].play()

                    # 创建获得道具的粒子效果
                    for _ in range(30):
                        particles.append(Particle(
                            powerup.x + powerup.width // 2,
                            powerup.y + powerup.height // 2,
                            BULLET_TYPES[powerup.type]["color"]
                        ))

            # 移除标记的道具
            for powerup in powerups_to_remove:
                if powerup in powerups:
                    powerups.remove(powerup)

            # ---------- 敌人更新 ----------
            enemies_to_remove = []  # 用于存储需要移除的敌人
            enemies_to_process = enemies[:]  # 复制敌人列表用于处理

            for enemy in enemies_to_process:
                enemy.update(enemy_bullets, sounds)  # 更新敌人状态(移动、射击)

                # 如果敌人移出屏幕底部,标记为需要移除
                if enemy.y > HEIGHT:
                    if enemy not in enemies_to_remove:
                        enemies_to_remove.append(enemy)
                    continue  # 跳过后续碰撞检测

                # ---------- 子弹击中敌人检测 ----------
                bullets_to_remove_from_enemy = []  # 用于存储需要移除的子弹
                for bullet in bullets:
                    # 检测子弹是否与敌人矩形重叠(碰撞检测)
                    if (enemy.x < bullet.x < enemy.x + enemy.width and
                            enemy.y < bullet.y < enemy.y + enemy.height):
                        enemy.health -= bullet.damage  # 敌人减少生命值(根据子弹伤害)
                        bullets_to_remove_from_enemy.append(bullet)  # 标记子弹为需要移除

                        # 播放击中音效(非致命伤害)
                        if sounds and "hit_enemy" in sounds and enemy.health > 0:
                            sounds["hit_enemy"].play()

                        # 创建击中粒子效果(根据子弹类型决定数量和颜色)
                        particle_count = 10
                        particle_color = bullet.color
                        if bullet.bullet_type == "fire":
                            particle_count = 20
                            particle_color = ORANGE
                        elif bullet.bullet_type == "laser":
                            particle_count = 5
                            particle_color = CYAN

                        for _ in range(particle_count):
                            particles.append(Particle(bullet.x, bullet.y, particle_color))

                        # 如果敌人生命值耗尽
                        if enemy.health <= 0:
                            player.score += enemy.max_health  # 玩家获得分数
                            if enemy not in enemies_to_remove:
                                enemies_to_remove.append(enemy)  # 标记敌人为需要移除

                            # 播放爆炸音效
                            if sounds and "explosion" in sounds:
                                sounds["explosion"].play()

                            # 创建爆炸粒子效果
                            for _ in range(100):
                                particles.append(Particle(
                                    enemy.x + enemy.width // 2,  # 爆炸中心X
                                    enemy.y + enemy.height // 2,  # 爆炸中心Y
                                    random.choice([RED, ORANGE, YELLOW])  # 随机颜色
                                ))
                            break  # 敌人已被摧毁,跳出子弹循环

                # 移除标记的子弹
                for bullet in bullets_to_remove_from_enemy:
                    if bullet in bullets:
                        bullets.remove(bullet)

                # ---------- 敌人撞击玩家检测 ----------
                # 检查玩家无敌状态,避免连续受伤
                if (player.invincible == 0 and  # 玩家不处于无敌状态
                        # 矩形碰撞检测:检查两个矩形是否重叠
                        player.x < enemy.x + enemy.width and
                        player.x + player.width > enemy.x and
                        player.y < enemy.y + enemy.height and
                        player.y + player.height > enemy.y):

                    player.health -= 10  # 玩家减少生命值
                    player.invincible = 60  # 设置无敌时间(60帧,约1秒)
                    if enemy not in enemies_to_remove:
                        enemies_to_remove.append(enemy)  # 标记敌人为需要移除

                    # 检查玩家是否死亡
                    if player.health <= 0:
                        game_over = True  # 游戏结束
                        break  # 游戏结束,跳出敌人循环

            # 移除标记的敌人
            for enemy in enemies_to_remove:
                if enemy in enemies:
                    enemies.remove(enemy)

            # ---------- 敌人子弹击中玩家检测 ----------
            if player.invincible == 0:  # 仅当玩家不无敌时检测
                enemy_bullets_to_remove_from_player = []
                for bullet in enemy_bullets:
                    # 检测子弹是否与玩家矩形重叠
                    if (player.x < bullet.x < player.x + player.width and
                            player.y < bullet.y < player.y + player.height):

                        # 玩家受到敌机子弹伤害(伤害值根据波次计算)
                        damage = min(10 + (wave - 1) * 3, 50)
                        player.health -= damage
                        player.invincible = 30  # 设置无敌时间(30帧,约0.5秒)
                        enemy_bullets_to_remove_from_player.append(bullet)  # 标记子弹为需要移除

                        # 检查玩家是否死亡
                        if player.health <= 0:
                            game_over = True
                            break  # 游戏结束,跳出子弹循环

                # 移除标记的子弹
                for bullet in enemy_bullets_to_remove_from_player:
                    if bullet in enemy_bullets:
                        enemy_bullets.remove(bullet)

            # 检查玩家是否被击中(生命值减少)
            if player.health < player_prev_health and player.invincible > 50:  # 刚被击中时播放音效
                # 播放玩家被击中音效
                if sounds and "player_hit" in sounds:
                    sounds["player_hit"].play()

            # 更新生命值记录
            player_prev_health = player.health

            # 检查游戏是否结束
            if game_over:
                # 播放游戏结束音效
                if sounds and "game_over" in sounds:
                    sounds["game_over"].play()
                    # 停止背景音乐
                    try:
                        pygame.mixer.music.stop()
                    except:
                        pass

        # ---------- 粒子效果更新 ----------
        # 注意:粒子效果在暂停时也更新,这样暂停时粒子效果不会卡住
        particles_to_remove = []
        for particle in particles:
            particle.update()  # 更新粒子状态
            if particle.life <= 0:  # 如果粒子生命结束
                particles_to_remove.append(particle)  # 标记粒子为需要移除

        for particle in particles_to_remove:
            if particle in particles:
                particles.remove(particle)

        # ========== 绘制阶段 ==========
        # 绘制背景(星空)- 传入当前波次
        draw_background(screen, stars, wave)

        # 绘制游戏对象(按从后到前的顺序)
        for bullet in bullets:
            bullet.draw(screen)
        for bullet in enemy_bullets:
            bullet.draw(screen)
        for powerup in powerups:
            powerup.draw(screen)
        for enemy in enemies:
            enemy.draw(screen)
        for particle in particles:
            particle.draw(screen)
        # 绘制玩家时传入当前波次
        player.draw(screen, wave)

        # 绘制用户界面 - 这里需要传递 paused 参数
        draw_ui(screen, player, game_over, wave, powerups, paused)

        # 更新显示(将绘制的内容显示到屏幕上)
        pygame.display.flip()
        # 控制游戏帧率(60FPS)
        clock.tick(FPS)


# ==================== 程序入口 ====================
if __name__ == "__main__":
    main()

喜欢的小伙伴们可以二次修改,前提是有python 环境

相关推荐
weixin_438077492 分钟前
CS336 Assignment 4 (data): Filtering Language Modeling Data 翻译和实现
人工智能·python·语言模型·自然语言处理
小郭团队3 分钟前
未来PLC会消失吗?会被嵌入式系统取代吗?
c语言·人工智能·python·嵌入式硬件·架构
yesyesido3 分钟前
智能文件格式转换器:文本/Excel与CSV无缝互转的在线工具
开发语言·python·excel
_200_6 分钟前
Lua 流程控制
开发语言·junit·lua
环黄金线HHJX.6 分钟前
拼音字母量子编程PQLAiQt架构”这一概念。结合上下文《QuantumTuan ⇆ QT:Qt》
开发语言·人工智能·qt·编辑器·量子计算
王夏奇6 分钟前
python在汽车电子行业中的应用1-基础知识概念
开发语言·python·汽车
子夜江寒7 分钟前
基于PyTorch的CBOW模型实现与词向量生成
pytorch·python
He_Donglin7 分钟前
Python图书爬虫
开发语言·爬虫·python
天远Date Lab11 分钟前
Python金融风控实战:集成天远多头借贷行业风险版API实现共债预警
大数据·python
Python极客之家12 分钟前
基于深度学习的刑事案件智能分类系统
人工智能·python·深度学习·机器学习·数据挖掘·毕业设计·情感分析