飞机大战游戏 - 代码结构说明(由于文档限制,直接合并一起,支持二次修改)
主要文件:
- main_game.py # 主游戏文件(当前文件)
核心功能:
-
玩家控制:方向键移动,空格键射击
-
多种子弹类型:普通、激光、火焰、散射子弹
-
波次系统:10个不同波次,每波敌人逐渐增强
-
道具系统:随机掉落道具改变子弹类型
-
音效系统:射击、击中、爆炸等音效
-
视觉效果:粒子效果、颜色变化、星空背景
-
难度递增:敌人子弹伤害和速度随波次增加
运行要求:
-
Python 3.6+
-
PyGame 库
-
支持音频的计算机
安装和运行:
-
安装依赖:pip install pygame==2.5.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 环境