Python游戏开发入门:Pygame实战
目录
- Python游戏开发入门:Pygame实战
- 1. Pygame简介与环境搭建
- 1.1 什么是Pygame
- 1.2 环境安装与配置
- 2. Pygame基础概念
- 2.1 游戏循环与事件处理
- 2.2 坐标系与基础图形
- 3. 实战项目:太空射击游戏
- 3.1 游戏设计与架构
- 4. 高级游戏开发技巧
- 4.1 物理引擎集成
- 4.2 音效与音乐系统
- 5. 完整游戏项目:平台跳跃游戏
- 5.1 游戏设计与实现
- 6. 完整代码结构与部署
- 6.1 项目文件结构
- 6.2 配置与设置文件
- 6.3 主程序入口
- 7. 总结与进阶学习
- 7.1 Pygame开发要点总结
- 7.2 数学基础回顾
- 7.3 进阶学习路径
- 7.4 性能优化建议
- 7.5 社区资源推荐
- 7.6 下一步学习建议
『宝藏代码胶囊开张啦!』—— 我的 CodeCapsule 来咯!✨
写代码不再头疼!我的新站点 CodeCapsule 主打一个 “白菜价”+“量身定制”!无论是卡脖子的毕设/课设/文献复现,需要灵光一现的算法改进,还是想给项目加个“外挂”,这里都有便宜又好用的代码方案等你发现!低成本,高适配,助你轻松通关!速来围观 👉 CodeCapsule官网
Python游戏开发入门:Pygame实战
1. Pygame简介与环境搭建
1.1 什么是Pygame
Pygame是一个开源的Python模块集,专门用于开发2D游戏和多媒体应用程序。它构建在SDL(Simple DirectMedia Layer)库之上,提供了对图形、声音、输入设备等游戏开发核心功能的简单访问接口。
Pygame的核心优势:
- 跨平台支持(Windows、macOS、Linux)
- 简单易学的API设计
- 活跃的社区和丰富的文档
- 完全免费和开源
- 与Python生态完美集成
1.2 环境安装与配置
# setup_environment.py
"""
Pygame开发环境设置脚本
"""
import sys
import subprocess
import importlib
from pathlib import Pathdef check_python_version():"""检查Python版本"""version = sys.version_infoprint(f"Python版本: {version.major}.{version.minor}.{version.micro}")if version.major < 3 or (version.major == 3 and version.minor < 7):print("❌ 需要Python 3.7或更高版本")return Falsereturn Truedef install_pygame():"""安装Pygame及相关依赖"""packages = ["pygame==2.5.2","numpy==1.24.3","pillow==10.0.1", # 图像处理"pymunk==6.7.0", # 物理引擎"pyopengl==3.1.7", # 3D图形支持"noise==1.2.2" # 噪声生成(用于地形等)]for package in packages:try:print(f"安装 {package}...")subprocess.check_call([sys.executable, "-m", "pip", "install", package])print(f"✅ {package} 安装成功")except subprocess.CalledProcessError:print(f"❌ {package} 安装失败")return Falsereturn Truedef verify_installation():"""验证安装结果"""try:import pygameimport numpy as npfrom PIL import Imageprint("✅ 核心库导入成功")print(f"Pygame版本: {pygame.version.ver}")print(f"NumPy版本: {np.__version__}")print(f"PIL版本: {Image.__version__}")# 测试Pygame初始化pygame.init()print("✅ Pygame初始化成功")pygame.quit()return Trueexcept ImportError as e:print(f"❌ 导入失败: {e}")return Falsedef create_project_structure():"""创建项目目录结构"""directories = ["assets/images","assets/sounds", "assets/fonts","src/game","src/entities","src/levels","src/utils","config","docs"]for directory in directories:Path(directory).mkdir(parents=True, exist_ok=True)print(f"创建目录: {directory}")# 创建示例文件sample_files = {"src/main.py": "# 游戏主入口文件","src/game/__init__.py": "# 游戏核心模块","src/entities/player.py": "# 玩家角色模块","config/settings.py": "# 游戏配置","README.md": "# Pygame项目说明"}for file_path, content in sample_files.items():with open(file_path, "w", encoding="utf-8") as f:f.write(content)print(f"创建文件: {file_path}")def main():"""主安装流程"""print("🎮 Pygame开发环境安装")print("=" * 50)if not check_python_version():returnif not install_pygame():print("❌ 依赖安装失败")returnif not verify_installation():print("❌ 环境验证失败")returncreate_project_structure()print("\n" + "=" * 50)print("🎉 环境设置完成!")print("接下来可以开始Pygame游戏开发了!")if __name__ == "__main__":main()
2. Pygame基础概念
2.1 游戏循环与事件处理
游戏开发的核心是游戏循环(Game Loop),它持续运行直到游戏结束。每个循环周期包含三个主要步骤:
- 处理输入:检测玩家操作
- 更新游戏状态:根据输入和游戏逻辑更新对象
- 渲染输出:将游戏状态绘制到屏幕
# src/game/core.py
"""
Pygame游戏核心框架
"""
import pygame
import sys
from typing import Dict, List, Callable, Any
from enum import Enumclass GameState(Enum):"""游戏状态枚举"""RUNNING = 1PAUSED = 2GAME_OVER = 3MENU = 4class PygameEngine:"""Pygame游戏引擎基类"""def __init__(self, title: str, width: int = 800, height: int = 600, fps: int = 60):"""初始化游戏引擎Args:title: 窗口标题width: 屏幕宽度height: 屏幕高度 fps: 帧率"""# 初始化Pygamepygame.init()# 屏幕设置self.screen_width = widthself.screen_height = heightself.screen = pygame.display.set_mode((width, height))pygame.display.set_caption(title)# 游戏时钟self.clock = pygame.time.Clock()self.fps = fps# 游戏状态self.game_state = GameState.RUNNINGself.running = True# 颜色定义self.colors = {'black': (0, 0, 0),'white': (255, 255, 255),'red': (255, 0, 0),'green': (0, 255, 0),'blue': (0, 0, 255),'gray': (128, 128, 128)}# 事件处理器字典self.event_handlers = {}self.key_handlers = {}# 游戏对象列表self.game_objects = []print(f"🎮 游戏引擎初始化完成: {title}")def handle_events(self):"""处理所有事件"""for event in pygame.event.get():# 退出事件if event.type == pygame.QUIT:self.running = False# 键盘按下事件elif event.type == pygame.KEYDOWN:self.handle_keydown(event.key)# 键盘释放事件 elif event.type == pygame.KEYUP:self.handle_keyup(event.key)# 鼠标事件elif event.type == pygame.MOUSEBUTTONDOWN:self.handle_mouse_click(event.button, event.pos)# 自定义事件处理if event.type in self.event_handlers:for handler in self.event_handlers[event.type]:handler(event)def handle_keydown(self, key: int):"""处理键盘按下事件"""if key in self.key_handlers:for handler in self.key_handlers[key]:handler()# 全局快捷键if key == pygame.K_ESCAPE:self.running = Falseelif key == pygame.K_p:self.toggle_pause()def handle_keyup(self, key: int):"""处理键盘释放事件"""passdef handle_mouse_click(self, button: int, position: tuple):"""处理鼠标点击事件"""passdef toggle_pause(self):"""切换暂停状态"""if self.game_state == GameState.RUNNING:self.game_state = GameState.PAUSEDprint("游戏暂停")elif self.game_state == GameState.PAUSED:self.game_state = GameState.RUNNINGprint("游戏继续")def update(self, delta_time: float):"""更新游戏状态Args:delta_time: 距离上一帧的时间(秒)"""if self.game_state != GameState.RUNNING:return# 更新所有游戏对象for obj in self.game_objects[:]: # 使用切片创建副本以避免在迭代时修改列表if hasattr(obj, 'update'):obj.update(delta_time)def render(self):"""渲染游戏画面"""# 清空屏幕self.screen.fill(self.colors['black'])# 渲染所有游戏对象for obj in self.game_objects:if hasattr(obj, 'render'):obj.render(self.screen)# 显示帧率self.show_fps()# 更新显示pygame.display.flip()def show_fps(self):"""显示帧率"""fps_text = f"FPS: {int(self.clock.get_fps())}"font = pygame.font.Font(None, 36)text_surface = font.render(fps_text, True, self.colors['white'])self.screen.blit(text_surface, (10, 10))def add_event_handler(self, event_type: int, handler: Callable):"""添加事件处理器"""if event_type not in self.event_handlers:self.event_handlers[event_type] = []self.event_handlers[event_type].append(handler)def add_key_handler(self, key: int, handler: Callable):"""添加键盘事件处理器"""if key not in self.key_handlers:self.key_handlers[key] = []self.key_handlers[key].append(handler)def add_game_object(self, obj: Any):"""添加游戏对象"""self.game_objects.append(obj)def remove_game_object(self, obj: Any):"""移除游戏对象"""if obj in self.game_objects:self.game_objects.remove(obj)def run(self):"""运行游戏主循环"""print("🚀 开始游戏循环...")previous_time = pygame.time.get_ticks()while self.running:# 计算时间增量current_time = pygame.time.get_ticks()delta_time = (current_time - previous_time) / 1000.0 # 转换为秒previous_time = current_time# 游戏循环的三个核心步骤self.handle_events()self.update(delta_time)self.render()# 控制帧率self.clock.tick(self.fps)self.cleanup()def cleanup(self):"""清理资源"""pygame.quit()print("🎯 游戏结束")# 基础游戏实体类
class GameObject:"""游戏对象基类"""def __init__(self, x: float, y: float):"""初始化游戏对象Args:x: x坐标y: y坐标"""self.x = xself.y = yself.velocity_x = 0self.velocity_y = 0self.active = Truedef update(self, delta_time: float):"""更新对象状态"""# 基础位置更新(基于速度)self.x += self.velocity_x * delta_timeself.y += self.velocity_y * delta_timedef render(self, surface: pygame.Surface):"""渲染对象"""passdef get_position(self) -> tuple:"""获取位置"""return (self.x, self.y)def set_position(self, x: float, y: float):"""设置位置"""self.x = xself.y = ydef get_velocity(self) -> tuple:"""获取速度"""return (self.velocity_x, self.velocity_y)def set_velocity(self, vx: float, vy: float):"""设置速度"""self.velocity_x = vxself.velocity_y = vydef demonstrate_basic_engine():"""演示基础游戏引擎"""class DemoGame(PygameEngine):"""演示游戏"""def __init__(self):super().__init__("Pygame基础演示", 800, 600, 60)# 添加一个测试对象self.test_object = GameObject(400, 300)self.test_object.velocity_x = 50 # 向右移动self.add_game_object(self.test_object)# 注册键盘事件self.add_key_handler(pygame.K_SPACE, self.on_space_press)def on_space_press(self):"""空格键按下事件"""print("空格键被按下!")# 改变测试对象方向self.test_object.velocity_x *= -1def update(self, delta_time: float):super().update(delta_time)# 边界检测if self.test_object.x <= 0 or self.test_object.x >= self.screen_width:self.test_object.velocity_x *= -1# 运行演示游戏game = DemoGame()game.run()if __name__ == "__main__":demonstrate_basic_engine()
2.2 坐标系与基础图形
Pygame使用笛卡尔坐标系,但Y轴方向向下为正。理解坐标系是游戏开发的基础。
# src/game/graphics.py
"""
图形绘制模块
"""
import pygame
import math
from typing import Tuple, List, Optional
from .core import GameObjectclass Vector2:"""2D向量类"""def __init__(self, x: float = 0.0, y: float = 0.0):self.x = xself.y = ydef __add__(self, other: 'Vector2') -> 'Vector2':return Vector2(self.x + other.x, self.y + other.y)def __sub__(self, other: 'Vector2') -> 'Vector2':return Vector2(self.x - other.x, self.y - other.y)def __mul__(self, scalar: float) -> 'Vector2':return Vector2(self.x * scalar, self.y * scalar)def magnitude(self) -> float:"""计算向量长度"""return math.sqrt(self.x * self.x + self.y * self.y)def normalize(self) -> 'Vector2':"""归一化向量"""mag = self.magnitude()if mag > 0:return Vector2(self.x / mag, self.y / mag)return Vector2()def to_tuple(self) -> Tuple[float, float]:"""转换为元组"""return (self.x, self.y)@classmethoddef from_angle(cls, angle: float, magnitude: float = 1.0) -> 'Vector2':"""从角度创建向量"""rad = math.radians(angle)return cls(math.cos(rad) * magnitude, math.sin(rad) * magnitude)class ShapeRenderer:"""图形渲染器"""def __init__(self, surface: pygame.Surface):self.surface = surfacedef draw_circle(self, center: Tuple[float, float], radius: float, color: Tuple[int, int, int], width: int = 0):"""绘制圆形"""pygame.draw.circle(self.surface, color, (int(center[0]), int(center[1])), int(radius), width)def draw_rect(self, rect: pygame.Rect, color: Tuple[int, int, int], width: int = 0, border_radius: int = 0):"""绘制矩形"""pygame.draw.rect(self.surface, color, rect, width, border_radius)def draw_line(self, start: Tuple[float, float], end: Tuple[float, float],color: Tuple[int, int, int], width: int = 1):"""绘制直线"""pygame.draw.line(self.surface, color, (int(start[0]), int(start[1])), (int(end[0]), int(end[1])), width)def draw_polygon(self, points: List[Tuple[float, float]], color: Tuple[int, int, int], width: int = 0):"""绘制多边形"""int_points = [(int(p[0]), int(p[1])) for p in points]pygame.draw.polygon(self.surface, color, int_points, width)def draw_ellipse(self, rect: pygame.Rect, color: Tuple[int, int, int], width: int = 0):"""绘制椭圆"""pygame.draw.ellipse(self.surface, color, rect, width)def draw_arc(self, rect: pygame.Rect, start_angle: float, end_angle: float,color: Tuple[int, int, int], width: int = 1):"""绘制圆弧"""pygame.draw.arc(self.surface, color, rect, math.radians(start_angle), math.radians(end_angle), width)class Sprite(GameObject):"""精灵类 - 带图像的游戏对象"""def __init__(self, x: float, y: float, image: Optional[pygame.Surface] = None):super().__init__(x, y)self.image = imageself.rect = pygame.Rect(x, y, 0, 0)if self.image:self.rect = self.image.get_rect(center=(x, y))else:# 如果没有图像,创建一个默认的矩形self.rect = pygame.Rect(x - 20, y - 20, 40, 40)def render(self, surface: pygame.Surface):if self.image:surface.blit(self.image, self.rect)else:# 绘制默认矩形pygame.draw.rect(surface, (255, 255, 255), self.rect)def set_image(self, image: pygame.Surface):"""设置图像"""self.image = imageold_center = self.rect.centerself.rect = image.get_rect()self.rect.center = old_centerdef get_center(self) -> Tuple[float, float]:"""获取中心位置"""return self.rect.centerclass AnimatedSprite(Sprite):"""动画精灵类"""def __init__(self, x: float, y: float):super().__init__(x, y)self.frames = [] # 动画帧列表self.current_frame = 0self.animation_speed = 0.1 # 帧切换速度self.animation_timer = 0.0self.loop = True # 是否循环播放def add_frame(self, image: pygame.Surface):"""添加动画帧"""self.frames.append(image)if len(self.frames) == 1:self.set_image(image)def update(self, delta_time: float):super().update(delta_time)if len(self.frames) > 1:self.animation_timer += delta_timeif self.animation_timer >= self.animation_speed:self.animation_timer = 0.0self.current_frame += 1if self.current_frame >= len(self.frames):if self.loop:self.current_frame = 0else:self.current_frame = len(self.frames) - 1self.set_image(self.frames[self.current_frame])def demonstrate_graphics():"""演示图形功能"""import pygamepygame.init()screen = pygame.display.set_mode((800, 600))pygame.display.set_caption("图形绘制演示")clock = pygame.time.Clock()renderer = ShapeRenderer(screen)running = True# 创建一些图形对象shapes = [{'type': 'circle', 'pos': (100, 100), 'radius': 30, 'color': (255, 0, 0)},{'type': 'rect', 'rect': pygame.Rect(200, 80, 60, 40), 'color': (0, 255, 0)},{'type': 'line', 'start': (300, 100), 'end': (400, 150), 'color': (0, 0, 255)},{'type': 'polygon', 'points': [(500, 80), (550, 120), (450, 120)], 'color': (255, 255, 0)},{'type': 'ellipse', 'rect': pygame.Rect(600, 80, 80, 40), 'color': (255, 0, 255)}]angle = 0while running:for event in pygame.event.get():if event.type == pygame.QUIT:running = False# 清屏screen.fill((0, 0, 0))# 绘制静态图形for shape in shapes:if shape['type'] == 'circle':renderer.draw_circle(shape['pos'], shape['radius'], shape['color'])elif shape['type'] == 'rect':renderer.draw_rect(shape['rect'], shape['color'])elif shape['type'] == 'line':renderer.draw_line(shape['start'], shape['end'], shape['color'])elif shape['type'] == 'polygon':renderer.draw_polygon(shape['points'], shape['color'])elif shape['type'] == 'ellipse':renderer.draw_ellipse(shape['rect'], shape['color'])# 绘制旋转的圆弧(动态效果)angle = (angle + 2) % 360arc_rect = pygame.Rect(350, 250, 100, 100)renderer.draw_arc(arc_rect, 0, angle, (255, 255, 255), 3)# 显示说明文字font = pygame.font.Font(None, 36)text = font.render("Pygame图形绘制演示", True, (255, 255, 255))screen.blit(text, (250, 400))pygame.display.flip()clock.tick(60)pygame.quit()if __name__ == "__main__":demonstrate_graphics()
3. 实战项目:太空射击游戏
3.1 游戏设计与架构
# src/game/space_shooter.py
"""
太空射击游戏 - 主游戏文件
"""
import pygame
import random
import math
from typing import List, Tuple, Optional
from .core import PygameEngine, GameObject, GameState
from .graphics import Sprite, AnimatedSprite, Vector2class Player(Sprite):"""玩家飞船类"""def __init__(self, x: float, y: float):super().__init__(x, y)self.speed = 300 # 移动速度(像素/秒)self.health = 100self.max_health = 100self.shoot_cooldown = 0.2 # 射击冷却时间(秒)self.shoot_timer = 0.0self.invulnerable = False # 无敌状态self.invulnerable_timer = 0.0self.invulnerable_duration = 1.0 # 无敌时间(秒)# 创建玩家飞船图形(简单的三角形)self.surface = pygame.Surface((40, 40), pygame.SRCALPHA)pygame.draw.polygon(self.surface, (0, 255, 255), [(20, 0), (0, 40), (40, 40)])self.set_image(self.surface)def update(self, delta_time: float):super().update(delta_time)# 处理射击冷却if self.shoot_timer > 0:self.shoot_timer -= delta_time# 处理无敌时间if self.invulnerable:self.invulnerable_timer -= delta_timeif self.invulnerable_timer <= 0:self.invulnerable = False# 闪烁效果(无敌时)if self.invulnerable:if int(self.invulnerable_timer * 10) % 2 == 0:self.image.set_alpha(128) # 半透明else:self.image.set_alpha(255) # 不透明else:self.image.set_alpha(255)def move(self, dx: float, dy: float, delta_time: float):"""移动玩家"""new_x = self.rect.centerx + dx * self.speed * delta_timenew_y = self.rect.centery + dy * self.speed * delta_time# 边界检查new_x = max(20, min(780, new_x))new_y = max(20, min(580, new_y))self.rect.center = (new_x, new_y)def shoot(self) -> Optional['Bullet']:"""发射子弹"""if self.shoot_timer <= 0 and not self.invulnerable:self.shoot_timer = self.shoot_cooldownreturn Bullet(self.rect.centerx, self.rect.top, 0, -1)return Nonedef take_damage(self, damage: int):"""受到伤害"""if not self.invulnerable:self.health -= damageself.invulnerable = Trueself.invulnerable_timer = self.invulnerable_durationif self.health <= 0:self.health = 0self.active = Falsedef heal(self, amount: int):"""恢复生命值"""self.health = min(self.max_health, self.health + amount)class Bullet(Sprite):"""子弹类"""def __init__(self, x: float, y: float, dx: float, dy: float, speed: float = 500, is_player_bullet: bool = True):super().__init__(x, y)self.direction = Vector2(dx, dy).normalize()self.speed = speedself.is_player_bullet = is_player_bulletself.damage = 25 if is_player_bullet else 10# 创建子弹图形self.surface = pygame.Surface((4, 12), pygame.SRCALPHA)color = (255, 255, 0) if is_player_bullet else (255, 0, 0)pygame.draw.rect(self.surface, color, (0, 0, 4, 12))self.set_image(self.surface)def update(self, delta_time: float):# 根据方向移动self.rect.x += self.direction.x * self.speed * delta_timeself.rect.y += self.direction.y * self.speed * delta_time# 检查是否超出屏幕if (self.rect.bottom < 0 or self.rect.top > 600 or self.rect.right < 0 or self.rect.left > 800):self.active = Falseclass Enemy(Sprite):"""敌人类"""def __init__(self, x: float, y: float, enemy_type: str = "basic"):super().__init__(x, y)self.enemy_type = enemy_typeself.speed = 100self.health = 30self.shoot_cooldown = 2.0self.shoot_timer = random.uniform(0, self.shoot_cooldown)# 根据敌人类型设置属性if enemy_type == "basic":self.speed = 100self.health = 30self.score_value = 10color = (255, 100, 100)elif enemy_type == "fast":self.speed = 200self.health = 20self.score_value = 15color = (255, 150, 50)elif enemy_type == "tank":self.speed = 50self.health = 80self.score_value = 25color = (150, 150, 255)# 创建敌人图形size = 30 if enemy_type == "basic" else 25 if enemy_type == "fast" else 40self.surface = pygame.Surface((size, size), pygame.SRCALPHA)pygame.draw.circle(self.surface, color, (size//2, size//2), size//2)# 添加细节if enemy_type == "tank":pygame.draw.circle(self.surface, (100, 100, 255), (size//2, size//2), size//4)self.set_image(self.surface)def update(self, delta_time: float):super().update(delta_time)# 向下移动self.rect.y += self.speed * delta_time# 简单的左右摆动self.rect.x += math.sin(pygame.time.get_ticks() * 0.001) * 50 * delta_time# 射击计时self.shoot_timer -= delta_time# 检查是否超出屏幕if self.rect.top > 600:self.active = Falsedef shoot(self, target_x: float, target_y: float) -> Optional[Bullet]:"""向目标发射子弹"""if self.shoot_timer <= 0:self.shoot_timer = self.shoot_cooldown# 计算方向dx = target_x - self.rect.centerxdy = target_y - self.rect.centerydirection = Vector2(dx, dy).normalize()return Bullet(self.rect.centerx, self.rect.bottom, direction.x, direction.y, 300, False)return Nonedef take_damage(self, damage: int):"""受到伤害"""self.health -= damageif self.health <= 0:self.active = Falsereturn True # 敌人被消灭return Falseclass Particle(Sprite):"""粒子效果类"""def __init__(self, x: float, y: float, color: Tuple[int, int, int]):super().__init__(x, y)self.color = colorself.lifetime = 1.0 # 粒子寿命(秒)self.age = 0.0self.velocity = Vector2.from_angle(random.uniform(0, 360), random.uniform(50, 200))self.size = random.uniform(2, 6)# 创建粒子表面self.surface = pygame.Surface((int(self.size*2), int(self.size*2)), pygame.SRCALPHA)pygame.draw.circle(self.surface, color, (int(self.size), int(self.size)), int(self.size))self.set_image(self.surface)def update(self, delta_time: float):super().update(delta_time)# 移动粒子self.rect.x += self.velocity.x * delta_timeself.rect.y += self.velocity.y * delta_time# 更新生命周期self.age += delta_time# 淡出效果alpha = 255 * (1 - self.age / self.lifetime)self.image.set_alpha(int(alpha))# 缩小效果scale = 1 - self.age / self.lifetimenew_size = max(1, int(self.size * scale * 2))if new_size != self.image.get_width():self.surface = pygame.Surface((new_size, new_size), pygame.SRCALPHA)pygame.draw.circle(self.surface, self.color, (new_size//2, new_size//2), new_size//2)old_center = self.rect.centerself.set_image(self.surface)self.rect.center = old_center# 检查生命周期结束if self.age >= self.lifetime:self.active = Falseclass Explosion(AnimatedSprite):"""爆炸效果类"""def __init__(self, x: float, y: float, size: float = 50.0):super().__init__(x, y)self.size = size# 创建爆炸动画帧self.create_explosion_frames()self.animation_speed = 0.05self.loop = Falsedef create_explosion_frames(self):"""创建爆炸动画帧"""num_frames = 8colors = [(255, 255, 0), (255, 150, 0), (255, 0, 0)]for i in range(num_frames):frame_size = int(self.size * (1 + i * 0.2))frame = pygame.Surface((frame_size, frame_size), pygame.SRCALPHA)# 绘制爆炸环progress = i / (num_frames - 1)color_idx = min(2, int(progress * len(colors)))color = colors[color_idx]radius = int(frame_size * 0.4 * (1 - progress * 0.5))pygame.draw.circle(frame, color, (frame_size//2, frame_size//2), radius)# 添加光晕for j in range(3):glow_radius = radius + j * 3alpha = 100 - j * 30glow_color = (*color, alpha)glow_surface = pygame.Surface((frame_size, frame_size), pygame.SRCALPHA)pygame.draw.circle(glow_surface, glow_color,(frame_size//2, frame_size//2), glow_radius)frame.blit(glow_surface, (0, 0), special_flags=pygame.BLEND_ALPHA_SDL2)self.add_frame(frame)class SpaceShooterGame(PygameEngine):"""太空射击游戏主类"""def __init__(self):super().__init__("太空射击游戏", 800, 600, 60)# 游戏状态self.score = 0self.level = 1self.enemies_killed = 0self.game_over = False# 创建玩家self.player = Player(400, 500)self.add_game_object(self.player)# 对象分组self.bullets = []self.enemies = []self.particles = []# 生成计时器self.spawn_timer = 0.0self.spawn_interval = 2.0 # 敌人生成间隔(秒)# 注册输入处理self.setup_input_handlers()# 加载资源self.load_resources()print("🚀 太空射击游戏初始化完成!")def setup_input_handlers(self):"""设置输入处理器"""# 键盘持续按下检测self.keys_pressed = {pygame.K_LEFT: False,pygame.K_RIGHT: False,pygame.K_UP: False,pygame.K_DOWN: False,pygame.K_SPACE: False}# 键盘按下事件self.add_key_handler(pygame.K_LEFT, lambda: self.set_key_state(pygame.K_LEFT, True))self.add_key_handler(pygame.K_RIGHT, lambda: self.set_key_state(pygame.K_RIGHT, True))self.add_key_handler(pygame.K_UP, lambda: self.set_key_state(pygame.K_UP, True))self.add_key_handler(pygame.K_DOWN, lambda: self.set_key_state(pygame.K_DOWN, True))self.add_key_handler(pygame.K_SPACE, lambda: self.set_key_state(pygame.K_SPACE, True))# 键盘释放事件self.add_event_handler(pygame.KEYUP, self.handle_key_up)def set_key_state(self, key: int, state: bool):"""设置按键状态"""if key in self.keys_pressed:self.keys_pressed[key] = statedef handle_key_up(self, event):"""处理键盘释放事件"""if event.key in self.keys_pressed:self.keys_pressed[event.key] = Falsedef load_resources(self):"""加载游戏资源"""# 这里可以加载图像、声音等资源# 暂时使用程序生成的图形passdef update(self, delta_time: float):if self.game_state != GameState.RUNNING:returnsuper().update(delta_time)# 处理玩家输入self.handle_player_input(delta_time)# 生成敌人self.spawn_enemies(delta_time)# 检测碰撞self.check_collisions()# 清理不活跃的对象self.cleanup_objects()# 检查游戏结束条件self.check_game_over()# 更新关卡难度self.update_level()def handle_player_input(self, delta_time: float):"""处理玩家输入"""dx, dy = 0, 0if self.keys_pressed[pygame.K_LEFT]:dx -= 1if self.keys_pressed[pygame.K_RIGHT]:dx += 1if self.keys_pressed[pygame.K_UP]:dy -= 1if self.keys_pressed[pygame.K_DOWN]:dy += 1# 归一化对角线移动if dx != 0 and dy != 0:dx *= 0.7071 # 1/√2dy *= 0.7071self.player.move(dx, dy, delta_time)# 自动射击(按住空格)if self.keys_pressed[pygame.K_SPACE]:bullet = self.player.shoot()if bullet:self.bullets.append(bullet)self.add_game_object(bullet)def spawn_enemies(self, delta_time: float):"""生成敌人"""self.spawn_timer -= delta_timeif self.spawn_timer <= 0:self.spawn_timer = self.spawn_interval# 根据关卡调整生成参数max_enemies = min(5 + self.level // 2, 15)if len(self.enemies) < max_enemies:# 随机选择敌人类型enemy_types = ["basic"] * 10 + ["fast"] * (self.level) + ["tank"] * (max(0, self.level - 2))enemy_type = random.choice(enemy_types)# 生成位置x = random.randint(50, 750)enemy = Enemy(x, -50, enemy_type)self.enemies.append(enemy)self.add_game_object(enemy)def check_collisions(self):"""检测碰撞"""# 玩家子弹与敌人碰撞for bullet in self.bullets[:]:if not bullet.active or not bullet.is_player_bullet:continuefor enemy in self.enemies[:]:if (enemy.active and bullet.active and bullet.rect.colliderect(enemy.rect)):# 敌人受到伤害if enemy.take_damage(bullet.damage):# 敌人被消灭self.score += enemy.score_valueself.enemies_killed += 1self.create_explosion(enemy.rect.centerx, enemy.rect.centery)bullet.active = Falsebreak# 敌人子弹与玩家碰撞for bullet in self.bullets[:]:if not bullet.active or bullet.is_player_bullet:continueif (bullet.active and self.player.active and bullet.rect.colliderect(self.player.rect)):self.player.take_damage(bullet.damage)bullet.active = False# 创建击中效果self.create_hit_effect(self.player.rect.centerx, self.player.rect.centery)# 敌人与玩家碰撞for enemy in self.enemies[:]:if (enemy.active and self.player.active and enemy.rect.colliderect(self.player.rect)):self.player.take_damage(20)enemy.take_damage(50)self.create_explosion(enemy.rect.centerx, enemy.rect.centery)def create_explosion(self, x: float, y: float):"""创建爆炸效果"""explosion = Explosion(x, y)self.add_game_object(explosion)# 添加粒子效果for _ in range(20):color = random.choice([(255, 255, 0), (255, 150, 0), (255, 0, 0)])particle = Particle(x, y, color)self.particles.append(particle)self.add_game_object(particle)def create_hit_effect(self, x: float, y: float):"""创建击中效果"""for _ in range(10):color = (255, 255, 255)particle = Particle(x, y, color)self.particles.append(particle)self.add_game_object(particle)def cleanup_objects(self):"""清理不活跃的对象"""# 从游戏引擎中移除不活跃的对象self.game_objects = [obj for obj in self.game_objects if obj.active]# 更新对象列表self.bullets = [b for b in self.bullets if b.active]self.enemies = [e for e in self.enemies if e.active]self.particles = [p for p in self.particles if p.active]def check_game_over(self):"""检查游戏结束条件"""if not self.player.active:self.game_over = Trueself.game_state = GameState.GAME_OVERdef update_level(self):"""更新关卡难度"""# 每消灭10个敌人升一级new_level = self.enemies_killed // 10 + 1if new_level > self.level:self.level = new_level# 增加难度self.spawn_interval = max(0.5, 2.0 - self.level * 0.1)print(f"升级到第 {self.level} 关!")def render(self):"""渲染游戏画面"""# 绘制星空背景self.draw_starfield()# 调用父类渲染super().render()# 绘制UIself.draw_ui()# 游戏结束画面if self.game_over:self.draw_game_over()def draw_starfield(self):"""绘制星空背景"""# 简单的星空效果for i in range(100):x = (pygame.time.get_ticks() // 50 + i * 100) % 800y = (i * 7) % 600brightness = (i % 3 + 1) * 60pygame.draw.circle(self.screen, (brightness, brightness, brightness), (x, y), 1)def draw_ui(self):"""绘制用户界面"""# 绘制分数font = pygame.font.Font(None, 36)score_text = font.render(f"分数: {self.score}", True, (255, 255, 255))self.screen.blit(score_text, (10, 10))# 绘制关卡level_text = font.render(f"关卡: {self.level}", True, (255, 255, 255))self.screen.blit(level_text, (10, 50))# 绘制生命值条health_width = 200health_height = 20health_ratio = self.player.health / self.player.max_health# 背景pygame.draw.rect(self.screen, (100, 100, 100), (600, 10, health_width, health_height))# 生命值pygame.draw.rect(self.screen, (0, 255, 0), (600, 10, health_width * health_ratio, health_height))# 边框pygame.draw.rect(self.screen, (255, 255, 255), (600, 10, health_width, health_height), 2)# 生命值文字health_text = font.render(f"{self.player.health}/{self.player.max_health}", True, (255, 255, 255))self.screen.blit(health_text, (610, 35))def draw_game_over(self):"""绘制游戏结束画面"""# 半透明覆盖层overlay = pygame.Surface((800, 600), pygame.SRCALPHA)overlay.fill((0, 0, 0, 128))self.screen.blit(overlay, (0, 0))# 游戏结束文字font_large = pygame.font.Font(None, 72)font_small = pygame.font.Font(None, 36)game_over_text = font_large.render("游戏结束", True, (255, 0, 0))score_text = font_small.render(f"最终分数: {self.score}", True, (255, 255, 255))level_text = font_small.render(f"达到关卡: {self.level}", True, (255, 255, 255))restart_text = font_small.render("按R键重新开始", True, (255, 255, 255))self.screen.blit(game_over_text, (400 - game_over_text.get_width()//2, 200))self.screen.blit(score_text, (400 - score_text.get_width()//2, 300))self.screen.blit(level_text, (400 - level_text.get_width()//2, 350))self.screen.blit(restart_text, (400 - restart_text.get_width()//2, 450))# 重新开始功能keys = pygame.key.get_pressed()if keys[pygame.K_r]:self.restart_game()def restart_game(self):"""重新开始游戏"""# 重置游戏状态self.score = 0self.level = 1self.enemies_killed = 0self.game_over = False# 清空所有游戏对象self.game_objects.clear()self.bullets.clear()self.enemies.clear()self.particles.clear()# 重新创建玩家self.player = Player(400, 500)self.add_game_object(self.player)# 重置生成计时器self.spawn_timer = 0.0self.spawn_interval = 2.0# 恢复游戏状态self.game_state = GameState.RUNNINGprint("🔄 游戏重新开始!")def main():"""游戏主入口"""game = SpaceShooterGame()game.run()if __name__ == "__main__":main()
4. 高级游戏开发技巧
4.1 物理引擎集成
# src/game/physics.py
"""
物理引擎集成模块
"""
import pygame
import pymunk
import pymunk.pygame_util
from typing import List, Tuple, Optionalclass PhysicsEngine:"""物理引擎封装"""def __init__(self, gravity: Tuple[float, float] = (0, 900)):"""初始化物理引擎Args:gravity: 重力向量 (x, y)"""self.space = pymunk.Space()self.space.gravity = gravity# 碰撞处理器self.collision_handlers = {}# 绘制选项self.draw_options = pymunk.pygame_util.DrawOptions()def add_static_body(self, shape: pymunk.Shape):"""添加静态刚体"""self.space.add(shape)def add_dynamic_body(self, body: pymunk.Body, shape: pymunk.Shape):"""添加动态刚体"""self.space.add(body, shape)def remove_body(self, body: pymunk.Body, shape: pymunk.Shape):"""移除刚体"""self.space.remove(body, shape)def create_rect(self, pos: Tuple[float, float], size: Tuple[float, float], mass: float = 1.0, body_type: int = pymunk.Body.DYNAMIC,elasticity: float = 0.8, friction: float = 0.5) -> Tuple[pymunk.Body, pymunk.Shape]:"""创建矩形刚体"""moment = pymunk.moment_for_box(mass, size)body = pymunk.Body(mass, moment, body_type=body_type)body.position = posshape = pymunk.Poly.create_box(body, size)shape.elasticity = elasticityshape.friction = frictionreturn body, shapedef create_circle(self, pos: Tuple[float, float], radius: float,mass: float = 1.0, body_type: int = pymunk.Body.DYNAMIC,elasticity: float = 0.8, friction: float = 0.5) -> Tuple[pymunk.Body, pymunk.Shape]:"""创建圆形刚体"""moment = pymunk.moment_for_circle(mass, 0, radius)body = pymunk.Body(mass, moment, body_type=body_type)body.position = posshape = pymunk.Circle(body, radius)shape.elasticity = elasticityshape.friction = frictionreturn body, shapedef create_segment(self, start: Tuple[float, float], end: Tuple[float, float],radius: float = 2.0, body_type: int = pymunk.Body.STATIC,elasticity: float = 0.8, friction: float = 0.5) -> Tuple[pymunk.Body, pymunk.Shape]:"""创建线段刚体(用于平台等)"""body = pymunk.Body(body_type=body_type)shape = pymunk.Segment(body, start, end, radius)shape.elasticity = elasticityshape.friction = frictionreturn body, shapedef add_collision_handler(self, collision_type_a: int, collision_type_b: int,handler_func: callable):"""添加碰撞处理器"""handler = self.space.add_collision_handler(collision_type_a, collision_type_b)handler.begin = handler_funcdef update(self, delta_time: float):"""更新物理世界"""self.space.step(delta_time)def debug_draw(self, surface: pygame.Surface):"""调试绘制物理世界"""self.draw_options.surface = surfaceself.space.debug_draw(self.draw_options)class PhysicsSprite:"""带物理的精灵类"""def __init__(self, physics_engine: PhysicsEngine, pos: Tuple[float, float], shape_type: str = "circle",**kwargs):"""初始化物理精灵Args:physics_engine: 物理引擎实例pos: 初始位置shape_type: 形状类型 ("circle", "rect")**kwargs: 形状参数"""self.physics_engine = physics_engineself.shape_type = shape_type# 创建物理刚体if shape_type == "circle":radius = kwargs.get('radius', 20)mass = kwargs.get('mass', 1.0)self.body, self.shape = physics_engine.create_circle(pos, radius, mass)elif shape_type == "rect":size = kwargs.get('size', (40, 40))mass = kwargs.get('mass', 1.0)self.body, self.shape = physics_engine.create_rect(pos, size, mass)# 设置碰撞类型self.shape.collision_type = kwargs.get('collision_type', 1)# 添加到物理世界physics_engine.add_dynamic_body(self.body, self.shape)# 图形属性self.color = kwargs.get('color', (255, 255, 255))self.surface = Noneself.create_surface()def create_surface(self):"""创建渲染表面"""if self.shape_type == "circle":radius = int(self.shape.radius)diameter = radius * 2self.surface = pygame.Surface((diameter, diameter), pygame.SRCALPHA)pygame.draw.circle(self.surface, self.color, (radius, radius), radius)elif self.shape_type == "rect":width = int(self.shape.bb.right - self.shape.bb.left)height = int(self.shape.bb.top - self.shape.bb.bottom)self.surface = pygame.Surface((width, height), pygame.SRCALPHA)pygame.draw.rect(self.surface, self.color, (0, 0, width, height))def update(self, delta_time: float):"""更新状态(物理引擎自动处理)"""passdef render(self, surface: pygame.Surface):"""渲染精灵"""if self.surface:# 获取位置和角度pos = self.body.positionangle = -self.body.angle # Pygame角度与Pymunk方向相反# 旋转表面rotated_surface = pygame.transform.rotate(self.surface, math.degrees(angle))# 计算渲染位置(中心对齐)rect = rotated_surface.get_rect(center=(int(pos.x), int(pos.y)))surface.blit(rotated_surface, rect)def apply_force(self, force: Tuple[float, float]):"""施加力"""self.body.apply_force_at_local_point(force)def apply_impulse(self, impulse: Tuple[float, float]):"""施加冲量"""self.body.apply_impulse_at_local_point(impulse)def set_velocity(self, velocity: Tuple[float, float]):"""设置速度"""self.body.velocity = velocitydef get_position(self) -> Tuple[float, float]:"""获取位置"""return self.body.positiondef set_position(self, pos: Tuple[float, float]):"""设置位置"""self.body.position = posdef demonstrate_physics():"""演示物理引擎功能"""import pygamepygame.init()screen = pygame.display.set_mode((800, 600))pygame.display.set_caption("物理引擎演示")clock = pygame.time.Clock()# 创建物理引擎physics = PhysicsEngine(gravity=(0, 500))# 创建边界walls = [physics.create_segment((0, 0), (800, 0), 10), # 上边界physics.create_segment((0, 600), (800, 600), 10), # 下边界physics.create_segment((0, 0), (0, 600), 10), # 左边界physics.create_segment((800, 0), (800, 600), 10) # 右边界]for body, shape in walls:physics.add_static_body(shape)# 创建一些平台platforms = [physics.create_segment((100, 400), (300, 400), 5),physics.create_segment((500, 300), (700, 350), 5)]for body, shape in platforms:shape.friction = 1.0physics.add_static_body(shape)# 创建物理精灵sprites = []# 添加一些测试物体for i in range(5):x = 100 + i * 120y = 100if i % 2 == 0:sprite = PhysicsSprite(physics, (x, y), "circle", radius=20, color=(255, 100, 100))else:sprite = PhysicsSprite(physics, (x, y), "rect", size=(40, 40), color=(100, 255, 100))sprites.append(sprite)running = Truewhile running:delta_time = clock.tick(60) / 1000.0for event in pygame.event.get():if event.type == pygame.QUIT:running = Falseelif event.type == pygame.MOUSEBUTTONDOWN:# 点击时在鼠标位置创建新物体pos = pygame.mouse.get_pos()if event.button == 1: # 左键sprite = PhysicsSprite(physics, pos, "circle", radius=15, color=(100, 100, 255))else: # 右键sprite = PhysicsSprite(physics, pos, "rect", size=(30, 30), color=(255, 255, 100))sprites.append(sprite)# 更新物理世界physics.update(delta_time)# 渲染screen.fill((0, 0, 0))# 绘制物理调试信息physics.debug_draw(screen)# 绘制精灵for sprite in sprites:sprite.render(screen)# 显示说明font = pygame.font.Font(None, 36)text = font.render("点击鼠标创建物理物体", True, (255, 255, 255))screen.blit(text, (250, 20))pygame.display.flip()pygame.quit()if __name__ == "__main__":demonstrate_physics()
4.2 音效与音乐系统
# src/game/audio.py
"""
音效与音乐系统
"""
import pygame
import os
from typing import Dict, List, Optional
from pathlib import Pathclass AudioManager:"""音频管理器"""def __init__(self):"""初始化音频系统"""# 初始化混音器pygame.mixer.init(frequency=44100, size=-16, channels=2, buffer=512)# 音频资源字典self.sounds: Dict[str, pygame.mixer.Sound] = {}self.music_playlist: List[str] = []self.current_music_index = 0# 音量设置self.master_volume = 1.0self.sound_volume = 1.0self.music_volume = 0.7# 音乐状态self.music_paused = Falseprint("🎵 音频系统初始化完成")def load_sound(self, name: str, file_path: str) -> bool:"""加载音效Args:name: 音效名称file_path: 文件路径Returns:是否加载成功"""try:if not os.path.exists(file_path):print(f"❌ 音效文件不存在: {file_path}")return Falsesound = pygame.mixer.Sound(file_path)self.sounds[name] = soundprint(f"✅ 加载音效: {name}")return Trueexcept pygame.error as e:print(f"❌ 加载音效失败 {name}: {e}")return Falsedef load_sound_directory(self, directory: str):"""加载目录中的所有音效Args:directory: 目录路径"""sound_dir = Path(directory)if not sound_dir.exists():print(f"❌ 音效目录不存在: {directory}")returnfor file_path in sound_dir.glob("*.wav"):name = file_path.stemself.load_sound(name, str(file_path))for file_path in sound_dir.glob("*.ogg"):name = file_path.stemself.load_sound(name, str(file_path))def play_sound(self, name: str, volume: float = 1.0, loops: int = 0) -> Optional[pygame.mixer.Channel]:"""播放音效Args:name: 音效名称volume: 音量 (0.0 到 1.0)loops: 循环次数 (0=不循环, -1=无限循环)Returns:播放通道"""if name not in self.sounds:print(f"❌ 音效未找到: {name}")return Nonetry:sound = self.sounds[name]channel = sound.play(loops=loops)if channel:# 设置音量(考虑主音量和音效音量)actual_volume = volume * self.sound_volume * self.master_volumechannel.set_volume(actual_volume)return channelexcept Exception as e:print(f"❌ 播放音效失败 {name}: {e}")return Nonedef stop_sound(self, name: str):"""停止播放指定音效"""if name in self.sounds:self.sounds[name].stop()def stop_all_sounds(self):"""停止所有音效"""pygame.mixer.stop()def load_music_playlist(self, directory: str):"""加载音乐播放列表Args:directory: 音乐目录路径"""music_dir = Path(directory)if not music_dir.exists():print(f"❌ 音乐目录不存在: {directory}")return# 支持的音频格式supported_formats = ['.mp3', '.ogg', '.wav']for format in supported_formats:for file_path in music_dir.glob(f"*{format}"):self.music_playlist.append(str(file_path))print(f"✅ 加载 {len(self.music_playlist)} 首音乐")def play_music(self, file_path: Optional[str] = None, loops: int = -1):"""播放音乐Args:file_path: 音乐文件路径(None则使用播放列表)loops: 循环次数 (-1=无限循环)"""try:if file_path:pygame.mixer.music.load(file_path)pygame.mixer.music.play(loops=loops)elif self.music_playlist:music_file = self.music_playlist[self.current_music_index]pygame.mixer.music.load(music_file)pygame.mixer.music.play(loops=loops)self.set_music_volume(self.music_volume)self.music_paused = Falseexcept pygame.error as e:print(f"❌ 播放音乐失败: {e}")def pause_music(self):"""暂停音乐"""if pygame.mixer.music.get_busy() and not self.music_paused:pygame.mixer.music.pause()self.music_paused = Truedef resume_music(self):"""恢复音乐"""if self.music_paused:pygame.mixer.music.unpause()self.music_paused = Falsedef stop_music(self):"""停止音乐"""pygame.mixer.music.stop()self.music_paused = Falsedef next_music(self):"""播放下一首音乐"""if not self.music_playlist:returnself.current_music_index = (self.current_music_index + 1) % len(self.music_playlist)self.stop_music()self.play_music()def previous_music(self):"""播放上一首音乐"""if not self.music_playlist:returnself.current_music_index = (self.current_music_index - 1) % len(self.music_playlist)self.stop_music()self.play_music()def set_master_volume(self, volume: float):"""设置主音量Args:volume: 音量 (0.0 到 1.0)"""self.master_volume = max(0.0, min(1.0, volume))self.update_volumes()def set_sound_volume(self, volume: float):"""设置音效音量Args:volume: 音量 (0.0 到 1.0)"""self.sound_volume = max(0.0, min(1.0, volume))self.update_volumes()def set_music_volume(self, volume: float):"""设置音乐音量Args:volume: 音量 (0.0 到 1.0)"""self.music_volume = max(0.0, min(1.0, volume))actual_volume = self.music_volume * self.master_volumepygame.mixer.music.set_volume(actual_volume)def update_volumes(self):"""更新所有音量设置"""self.set_music_volume(self.music_volume)# 更新所有正在播放的音效for name, sound in self.sounds.items():# 获取该音效的所有播放通道for channel in pygame.mixer.find_channel():if channel.get_sound() == sound:current_volume = getattr(sound, '_last_volume', 1.0)actual_volume = current_volume * self.sound_volume * self.master_volumechannel.set_volume(actual_volume)def get_music_info(self) -> Dict[str, str]:"""获取当前音乐信息"""if not self.music_playlist:return {}current_file = self.music_playlist[self.current_music_index]file_name = Path(current_file).stemreturn {'name': file_name,'file_path': current_file,'position': pygame.mixer.music.get_pos() // 1000, # 转换为秒'paused': self.music_paused}def cleanup(self):"""清理音频资源"""self.stop_all_sounds()self.stop_music()pygame.mixer.quit()class SoundEffect:"""音效包装类"""def __init__(self, audio_manager: AudioManager, sound_name: str):"""初始化音效Args:audio_manager: 音频管理器sound_name: 音效名称"""self.audio_manager = audio_managerself.sound_name = sound_nameself.channel = Nonedef play(self, volume: float = 1.0, loops: int = 0) -> bool:"""播放音效Returns:是否播放成功"""self.channel = self.audio_manager.play_sound(self.sound_name, volume, loops)return self.channel is not Nonedef stop(self):"""停止播放"""if self.channel:self.channel.stop()def is_playing(self) -> bool:"""检查是否正在播放"""return self.channel and self.channel.get_busy()def set_volume(self, volume: float):"""设置音量"""if self.channel:actual_volume = volume * self.audio_manager.sound_volume * self.audio_manager.master_volumeself.channel.set_volume(actual_volume)def demonstrate_audio():"""演示音频系统"""import pygameimport timepygame.init()screen = pygame.display.set_mode((600, 400))pygame.display.set_caption("音频系统演示")clock = pygame.time.Clock()# 创建音频管理器audio = AudioManager()# 创建测试音效(使用Pygame内置声音)try:# 这些是Pygame内置的声音,用于演示beep_sound = pygame.mixer.Sound(bytes([128] * 8000)) # 简单的蜂鸣声audio.sounds['beep'] = beep_soundprint("✅ 创建测试音效")except:print("⚠️ 无法创建测试音效,但演示将继续")# 创建音效实例beep_effect = SoundEffect(audio, 'beep')# 字体font = pygame.font.Font(None, 36)small_font = pygame.font.Font(None, 24)running = Truelast_beep_time = 0beep_interval = 1.0 # 秒while running:current_time = time.time()for event in pygame.event.get():if event.type == pygame.QUIT:running = Falseelif event.type == pygame.KEYDOWN:if event.key == pygame.K_SPACE:beep_effect.play(0.5)elif event.key == pygame.K_m:if audio.music_paused:audio.resume_music()else:audio.pause_music()elif event.key == pygame.K_PLUS:new_volume = min(1.0, audio.master_volume + 0.1)audio.set_master_volume(new_volume)elif event.key == pygame.K_MINUS:new_volume = max(0.0, audio.master_volume - 0.1)audio.set_master_volume(new_volume)# 自动播放蜂鸣声(演示)if current_time - last_beep_time > beep_interval:beep_effect.play(0.3)last_beep_time = current_time# 渲染screen.fill((0, 0, 0))# 显示控制说明controls = ["空格键: 播放音效","M键: 暂停/恢复音乐", "+/-: 调节音量",f"主音量: {audio.master_volume:.1f}",f"音效音量: {audio.sound_volume:.1f}",f"音乐音量: {audio.music_volume:.1f}"]for i, text in enumerate(controls):color = (255, 255, 255) if i < 3 else (200, 200, 200)text_surface = small_font.render(text, True, color)screen.blit(text_surface, (50, 50 + i * 30))# 显示音频状态status_text = f"音效状态: {'播放中' if beep_effect.is_playing() else '停止'}"status_surface = font.render(status_text, True, (255, 255, 0))screen.blit(status_surface, (150, 250))pygame.display.flip()clock.tick(60)audio.cleanup()pygame.quit()if __name__ == "__main__":demonstrate_audio()
5. 完整游戏项目:平台跳跃游戏
5.1 游戏设计与实现
# src/game/platformer.py
"""
平台跳跃游戏 - 完整实现
"""
import pygame
import random
import sys
from typing import List, Tuple, Optional, Dict
from .core import PygameEngine, GameObject, GameState
from .graphics import Sprite, AnimatedSprite
from .physics import PhysicsEngine, PhysicsSpriteclass PlatformerGame(PygameEngine):"""平台跳跃游戏"""def __init__(self):super().__init__("平台冒险家", 800, 600, 60)# 游戏状态self.score = 0self.coins_collected = 0self.level = 1self.lives = 3# 物理引擎self.physics = PhysicsEngine(gravity=(0, 900))# 游戏对象self.player = Noneself.platforms = []self.coins = []self.enemies = []# 游戏控制self.game_over = Falseself.level_complete = False# 初始化游戏self.init_game()print("🎮 平台跳跃游戏初始化完成!")def init_game(self):"""初始化游戏"""# 创建玩家self.create_player()# 创建关卡self.generate_level()# 设置输入控制self.setup_controls()def create_player(self):"""创建玩家角色"""self.player = PlayerCharacter(100, 300, self.physics)self.add_game_object(self.player)def generate_level(self):"""生成关卡"""# 清空现有对象self.platforms.clear()self.coins.clear()self.enemies.clear()# 生成地面self.create_ground()# 生成平台self.generate_platforms()# 生成金币self.generate_coins()# 生成敌人self.generate_enemies()# 生成终点self.create_goal()def create_ground(self):"""创建地面"""# 主地面ground_body, ground_shape = self.physics.create_segment((0, 580), (800, 580), 20, elasticity=0.2, friction=1.0)self.physics.add_static_body(ground_shape)# 添加到平台列表用于渲染ground_platform = Platform(400, 590, 800, 20)self.platforms.append(ground_platform)self.add_game_object(ground_platform)def generate_platforms(self):"""生成平台"""# 固定平台fixed_platforms = [(200, 450, 150, 20),(400, 350, 100, 20),(600, 450, 150, 20),(300, 250, 120, 20),(500, 200, 100, 20)]for x, y, width, height in fixed_platforms:# 物理平台platform_body, platform_shape = self.physics.create_segment((x - width//2, y), (x + width//2, y), height//2,elasticity=0.2, friction=1.0)self.physics.add_static_body(platform_shape)# 渲染平台platform = Platform(x, y, width, height)self.platforms.append(platform)self.add_game_object(platform)# 移动平台(根据关卡增加)if self.level >= 2:moving_platform = MovingPlatform(200, 300, 100, 20, start_x=150, end_x=450, speed=50)self.platforms.append(moving_platform)self.add_game_object(moving_platform)def generate_coins(self):"""生成金币"""coin_positions = [(250, 400), (400, 300), (650, 400),(350, 200), (550, 150), (200, 150)]for x, y in coin_positions:coin = Coin(x, y)self.coins.append(coin)self.add_game_object(coin)def generate_enemies(self):"""生成敌人"""if self.level >= 2:# 巡逻敌人patrol_enemy = PatrolEnemy(300, 530, 200, 500, self.physics)self.enemies.append(patrol_enemy)self.add_game_object(patrol_enemy)if self.level >= 3:# 跳跃敌人jumping_enemy = JumpingEnemy(500, 530, self.physics)self.enemies.append(jumping_enemy)self.add_game_object(jumping_enemy)def create_goal(self):"""创建关卡目标"""self.goal = Goal(750, 300)self.add_game_object(self.goal)def setup_controls(self):"""设置游戏控制"""self.keys_pressed = {pygame.K_LEFT: False,pygame.K_RIGHT: False,pygame.K_UP: False,pygame.K_SPACE: False}# 键盘按下事件self.add_key_handler(pygame.K_LEFT, lambda: self.set_key_state(pygame.K_LEFT, True))self.add_key_handler(pygame.K_RIGHT, lambda: self.set_key_state(pygame.K_RIGHT, True))self.add_key_handler(pygame.K_UP, lambda: self.set_key_state(pygame.K_UP, True))self.add_key_handler(pygame.K_SPACE, lambda: self.set_key_state(pygame.K_SPACE, True))# 键盘释放事件self.add_event_handler(pygame.KEYUP, self.handle_key_up)def set_key_state(self, key: int, state: bool):"""设置按键状态"""if key in self.keys_pressed:self.keys_pressed[key] = statedef handle_key_up(self, event):"""处理键盘释放事件"""if event.key in self.keys_pressed:self.keys_pressed[event.key] = Falsedef update(self, delta_time: float):if self.game_state != GameState.RUNNING:return# 更新物理世界self.physics.update(delta_time)# 处理玩家输入self.handle_player_input()# 调用父类更新super().update(delta_time)# 检测碰撞self.check_collisions()# 检查游戏状态self.check_game_state()def handle_player_input(self):"""处理玩家输入"""if not self.player or not self.player.active:return# 水平移动move_direction = 0if self.keys_pressed[pygame.K_LEFT]:move_direction -= 1if self.keys_pressed[pygame.K_RIGHT]:move_direction += 1self.player.move(move_direction)# 跳跃if (self.keys_pressed[pygame.K_UP] or self.keys_pressed[pygame.K_SPACE]):self.player.jump()def check_collisions(self):"""检测碰撞"""if not self.player or not self.player.active:return# 金币收集for coin in self.coins[:]:if (coin.active and self.player.rect.colliderect(coin.rect)):coin.collect()self.coins_collected += 1self.score += 100print(f"💰 收集金币! 总数: {self.coins_collected}")# 敌人碰撞for enemy in self.enemies[:]:if (enemy.active and self.player.rect.colliderect(enemy.rect)):# 检查是从上方踩到敌人if (self.player.rect.bottom <= enemy.rect.top + 10 and self.player.velocity_y > 0):# 踩死敌人enemy.defeat()self.score += 200self.player.bounce()print("👟 踩死敌人!")else:# 玩家受伤self.player.take_damage()self.lives -= 1print(f"💔 受伤! 剩余生命: {self.lives}")# 到达目标if (self.player.active and self.goal.active and self.player.rect.colliderect(self.goal.rect)):self.level_complete = Trueprint("🎯 关卡完成!")def check_game_state(self):"""检查游戏状态"""# 检查玩家死亡if self.player and not self.player.active:self.lives -= 1if self.lives <= 0:self.game_over = Trueself.game_state = GameState.GAME_OVERprint("💀 游戏结束!")else:self.respawn_player()# 检查关卡完成if self.level_complete:self.next_level()def respawn_player(self):"""重生玩家"""self.remove_game_object(self.player)self.create_player()print(f"🔄 玩家重生! 剩余生命: {self.lives}")def next_level(self):"""进入下一关"""self.level += 1self.level_complete = False# 清空当前关卡self.cleanup_level()# 生成新关卡self.generate_level()# 重生玩家self.respawn_player()print(f"🚀 进入第 {self.level} 关!")def cleanup_level(self):"""清理关卡对象"""# 移除所有平台、金币、敌人for obj in self.platforms + self.coins + self.enemies:self.remove_game_object(obj)if self.goal:self.remove_game_object(self.goal)self.platforms.clear()self.coins.clear() self.enemies.clear()def render(self):"""渲染游戏"""# 绘制背景self.draw_background()# 调用父类渲染super().render()# 绘制UIself.draw_ui()# 游戏结束画面if self.game_over:self.draw_game_over()# 关卡完成画面elif self.level_complete:self.draw_level_complete()def draw_background(self):"""绘制背景"""# 渐变背景for y in range(0, 600, 2):color_value = max(0, 50 - y // 12)color = (color_value, color_value, 100 + color_value)pygame.draw.line(self.screen, color, (0, y), (800, y))# 云朵self.draw_clouds()def draw_clouds(self):"""绘制云朵"""cloud_positions = [(100, 100), (300, 80), (500, 120), (700, 90)]for x, y in cloud_positions:# 简单的云朵形状offset = (pygame.time.get_ticks() // 50 + x) % 1600 - 800cloud_x = x + offsetpygame.draw.ellipse(self.screen, (200, 200, 220), (cloud_x - 30, y - 10, 60, 30))pygame.draw.ellipse(self.screen, (200, 200, 220), (cloud_x - 50, y, 40, 25))pygame.draw.ellipse(self.screen, (200, 200, 220), (cloud_x - 10, y, 40, 25))def draw_ui(self):"""绘制用户界面"""font = pygame.font.Font(None, 36)# 分数score_text = font.render(f"分数: {self.score}", True, (255, 255, 255))self.screen.blit(score_text, (10, 10))# 生命值lives_text = font.render(f"生命: {self.lives}", True, (255, 255, 255))self.screen.blit(lives_text, (10, 50))# 关卡level_text = font.render(f"关卡: {self.level}", True, (255, 255, 255))self.screen.blit(level_text, (10, 90))# 金币coins_text = font.render(f"金币: {self.coins_collected}", True, (255, 255, 0))self.screen.blit(coins_text, (10, 130))def draw_game_over(self):"""绘制游戏结束画面"""overlay = pygame.Surface((800, 600), pygame.SRCALPHA)overlay.fill((0, 0, 0, 128))self.screen.blit(overlay, (0, 0))font_large = pygame.font.Font(None, 72)font_small = pygame.font.Font(None, 36)game_over_text = font_large.render("游戏结束", True, (255, 0, 0))score_text = font_small.render(f"最终分数: {self.score}", True, (255, 255, 255))restart_text = font_small.render("按R键重新开始", True, (255, 255, 255))self.screen.blit(game_over_text, (400 - game_over_text.get_width()//2, 200))self.screen.blit(score_text, (400 - score_text.get_width()//2, 300))self.screen.blit(restart_text, (400 - restart_text.get_width()//2, 400))# 重新开始功能keys = pygame.key.get_pressed()if keys[pygame.K_r]:self.restart_game()def draw_level_complete(self):"""绘制关卡完成画面"""overlay = pygame.Surface((800, 600), pygame.SRCALPHA)overlay.fill((0, 0, 0, 128))self.screen.blit(overlay, (0, 0))font_large = pygame.font.Font(None, 72)font_small = pygame.font.Font(None, 36)complete_text = font_large.render("关卡完成!", True, (0, 255, 0))level_text = font_small.render(f"进入第 {self.level + 1} 关", True, (255, 255, 255))continue_text = font_small.render("按空格键继续", True, (255, 255, 255))self.screen.blit(complete_text, (400 - complete_text.get_width()//2, 200))self.screen.blit(level_text, (400 - level_text.get_width()//2, 300))self.screen.blit(continue_text, (400 - continue_text.get_width()//2, 400))def restart_game(self):"""重新开始游戏"""# 重置游戏状态self.score = 0self.coins_collected = 0self.level = 1self.lives = 3self.game_over = Falseself.level_complete = False# 清空所有对象self.game_objects.clear()self.platforms.clear()self.coins.clear()self.enemies.clear()# 重新初始化游戏self.init_game()# 恢复游戏状态self.game_state = GameState.RUNNINGprint("🔄 游戏重新开始!")# 游戏实体类
class PlayerCharacter(PhysicsSprite):"""玩家角色"""def __init__(self, x: float, y: float, physics_engine: PhysicsEngine):super().__init__(physics_engine, (x, y), "rect", size=(30, 50), mass=1.0, color=(0, 200, 255))# 玩家属性self.move_speed = 300self.jump_force = 400self.can_jump = Falseself.facing_right = True# 动画状态self.is_walking = Falseself.is_jumping = False# 创建动画表面(简化版)self.create_animation_surfaces()self.current_surface = self.idle_surface_rightdef create_animation_surfaces(self):"""创建动画表面"""# 待机状态self.idle_surface_right = self.create_character_surface((0, 200, 255))self.idle_surface_left = pygame.transform.flip(self.idle_surface_right, True, False)# 行走状态(简化:只是颜色变化)self.walk_surface_right = self.create_character_surface((0, 150, 255))self.walk_surface_left = pygame.transform.flip(self.walk_surface_right, True, False)# 跳跃状态self.jump_surface_right = self.create_character_surface((0, 100, 255))self.jump_surface_left = pygame.transform.flip(self.jump_surface_right, True, False)def create_character_surface(self, color: Tuple[int, int, int]) -> pygame.Surface:"""创建角色表面"""surface = pygame.Surface((30, 50), pygame.SRCALPHA)# 身体pygame.draw.rect(surface, color, (5, 10, 20, 30), border_radius=5)# 头部pygame.draw.circle(surface, color, (15, 15), 10)# 眼睛pygame.draw.circle(surface, (255, 255, 255), (10, 12), 3)pygame.draw.circle(surface, (255, 255, 255), (20, 12), 3)pygame.draw.circle(surface, (0, 0, 0), (10, 12), 1)pygame.draw.circle(surface, (0, 0, 0), (20, 12), 1)return surfacedef move(self, direction: int):"""移动玩家"""if direction != 0:self.body.velocity = pymunk.Vec2d(direction * self.move_speed, self.body.velocity.y)self.is_walking = Trueself.facing_right = direction > 0else:# 减速self.body.velocity = pymunk.Vec2d(self.body.velocity.x * 0.9, self.body.velocity.y)self.is_walking = Falsedef jump(self):"""跳跃"""if self.can_jump:self.body.velocity = pymunk.Vec2d(self.body.velocity.x, -self.jump_force)self.can_jump = Falseself.is_jumping = Truedef update(self, delta_time: float):super().update(delta_time)# 检查是否在地面上self.check_grounded()# 更新动画状态self.update_animation()def check_grounded(self):"""检查是否在地面上"""# 简单的接地检测:检查Y轴速度是否接近0且位置接近地面if abs(self.body.velocity.y) < 1 and self.body.position.y >= 530:self.can_jump = Trueself.is_jumping = Falseelse:self.can_jump = Falsedef update_animation(self):"""更新动画"""if self.is_jumping:self.current_surface = (self.jump_surface_right if self.facing_right else self.jump_surface_left)elif self.is_walking:self.current_surface = (self.walk_surface_right if self.facing_right else self.walk_surface_left)else:self.current_surface = (self.idle_surface_right if self.facing_right else self.idle_surface_left)self.surface = self.current_surfacedef take_damage(self):"""受到伤害"""# 击退效果self.body.velocity = pymunk.Vec2d(-200, -300)self.active = Falsedef bounce(self):"""弹跳(踩敌人时)"""self.body.velocity = pymunk.Vec2d(self.body.velocity.x, -300)class Platform(Sprite):"""平台类"""def __init__(self, x: float, y: float, width: float, height: float):super().__init__(x, y)# 创建平台表面self.surface = pygame.Surface((width, height), pygame.SRCALPHA)# 平台颜色和纹理base_color = (150, 100, 50)highlight_color = (180, 130, 80)# 填充基础颜色self.surface.fill(base_color)# 添加纹理for i in range(0, width, 4):pygame.draw.line(self.surface, highlight_color, (i, 0), (i, height), 1)# 边框pygame.draw.rect(self.surface, (100, 70, 30), (0, 0, width, height), 2)self.set_image(self.surface)class MovingPlatform(Platform):"""移动平台"""def __init__(self, x: float, y: float, width: float, height: float,start_x: float, end_x: float, speed: float):super().__init__(x, y, width, height)self.start_x = start_xself.end_x = end_xself.speed = speedself.direction = 1# 物理属性self.body = pymunk.Body(body_type=pymunk.Body.KINEMATIC)self.body.position = (x, y)self.shape = pymunk.Segment(self.body, (-width//2, 0), (width//2, 0), height//2)self.shape.friction = 1.0def update(self, delta_time: float):"""更新平台位置"""# 移动平台self.body.position = pymunk.Vec2d(self.body.position.x + self.speed * self.direction * delta_time,self.body.position.y)# 检查边界if self.body.position.x <= self.start_x:self.body.position = pymunk.Vec2d(self.start_x, self.body.position.y)self.direction = 1elif self.body.position.x >= self.end_x:self.body.position = pymunk.Vec2d(self.end_x, self.body.position.y)self.direction = -1# 更新渲染位置self.rect.center = (int(self.body.position.x), int(self.body.position.y))class Coin(Sprite):"""金币类"""def __init__(self, x: float, y: float):super().__init__(x, y)# 创建金币表面self.surface = pygame.Surface((20, 20), pygame.SRCALPHA)# 金币颜色outer_color = (255, 215, 0) # 金色inner_color = (255, 255, 100) # 亮黄色# 绘制金币pygame.draw.circle(self.surface, outer_color, (10, 10), 10)pygame.draw.circle(self.surface, inner_color, (10, 10), 7)# 高光pygame.draw.circle(self.surface, (255, 255, 200), (6, 6), 3)self.set_image(self.surface)self.rotation = 0def update(self, delta_time: float):"""更新金币(旋转动画)"""self.rotation = (self.rotation + 180 * delta_time) % 360rotated_image = pygame.transform.rotate(self.surface, self.rotation)old_center = self.rect.centerself.image = rotated_imageself.rect = self.image.get_rect(center=old_center)def collect(self):"""收集金币"""self.active = Falseclass PatrolEnemy(PhysicsSprite):"""巡逻敌人"""def __init__(self, x: float, y: float, start_x: float, end_x: float, physics_engine: PhysicsEngine):super().__init__(physics_engine, (x, y), "rect", size=(30, 30), mass=1.0, color=(255, 100, 100))self.start_x = start_xself.end_x = end_xself.patrol_speed = 50self.direction = 1# 设置为运动学刚体,不受重力影响self.body.body_type = pymunk.Body.KINEMATICdef update(self, delta_time: float):"""更新敌人"""# 巡逻移动self.body.velocity = pymunk.Vec2d(self.patrol_speed * self.direction, 0)# 检查巡逻边界if self.body.position.x <= self.start_x:self.direction = 1elif self.body.position.x >= self.end_x:self.direction = -1super().update(delta_time)def defeat(self):"""被击败"""self.active = Falseclass JumpingEnemy(PhysicsSprite):"""跳跃敌人"""def __init__(self, x: float, y: float, physics_engine: PhysicsEngine):super().__init__(physics_engine, (x, y), "rect",size=(25, 25), mass=1.0, color=(255, 150, 150))self.jump_timer = 0self.jump_interval = 2.0self.jump_force = 300def update(self, delta_time: float):"""更新敌人"""self.jump_timer += delta_timeif self.jump_timer >= self.jump_interval:self.jump()self.jump_timer = 0super().update(delta_time)def jump(self):"""跳跃"""self.body.velocity = pymunk.Vec2d(0, -self.jump_force)def defeat(self):"""被击败"""self.active = Falseclass Goal(Sprite):"""关卡目标"""def __init__(self, x: float, y: float):super().__init__(x, y)# 创建目标表面(旗杆)self.surface = pygame.Surface((40, 80), pygame.SRCALPHA)# 旗杆pygame.draw.rect(self.surface, (200, 200, 200), (18, 0, 4, 60))# 旗帜pygame.draw.polygon(self.surface, (0, 255, 0), [(22, 10), (38, 20), (22, 30)])self.set_image(self.surface)def main():"""游戏主入口"""game = PlatformerGame()game.run()if __name__ == "__main__":main()
6. 完整代码结构与部署
6.1 项目文件结构
pygame-project/
├── assets/
│ ├── images/ # 图像资源
│ │ ├── characters/
│ │ ├── backgrounds/
│ │ └── ui/
│ ├── sounds/ # 音效资源
│ │ ├── effects/
│ │ └── music/
│ └── fonts/ # 字体文件
├── src/
│ ├── game/ # 游戏核心
│ │ ├── __init__.py
│ │ ├── core.py # 游戏引擎
│ │ ├── graphics.py # 图形模块
│ │ ├── physics.py # 物理引擎
│ │ ├── audio.py # 音频系统
│ │ ├── space_shooter.py # 太空射击游戏
│ │ └── platformer.py # 平台跳跃游戏
│ ├── entities/ # 游戏实体
│ │ ├── __init__.py
│ │ ├── player.py # 玩家角色
│ │ ├── enemies.py # 敌人
│ │ └── items.py # 物品
│ ├── levels/ # 关卡设计
│ │ ├── __init__.py
│ │ └── level1.py
│ └── utils/ # 工具函数
│ ├── __init__.py
│ └── helpers.py
├── config/ # 配置文件
│ ├── settings.py
│ └── keybinds.py
├── docs/ # 文档
│ ├── README.md
│ └── tutorial.md
├── tests/ # 测试文件
├── requirements.txt # 依赖列表
└── main.py # 程序入口
6.2 配置与设置文件
# config/settings.py
"""
游戏配置文件
"""
import pygame
from pathlib import Path# 项目路径
PROJECT_ROOT = Path(__file__).parent.parent
ASSETS_PATH = PROJECT_ROOT / "assets"# 显示设置
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 60
FULLSCREEN = False# 颜色定义
COLORS = {'black': (0, 0, 0),'white': (255, 255, 255),'red': (255, 0, 0),'green': (0, 255, 0),'blue': (0, 0, 255),'yellow': (255, 255, 0),'purple': (128, 0, 128),'orange': (255, 165, 0),'gray': (128, 128, 128),'dark_gray': (64, 64, 64),'light_gray': (192, 192, 192)
}# 游戏设置
GAME_SETTINGS = {'master_volume': 1.0,'music_volume': 0.7,'sound_volume': 1.0,'show_fps': True,'debug_mode': False,'language': 'zh_CN'
}# 物理设置
PHYSICS_SETTINGS = {'gravity': (0, 900),'iterations': 10,'damping': 0.9
}def get_display_info():"""获取显示设备信息"""pygame.display.init()info = {'current_resolution': (SCREEN_WIDTH, SCREEN_HEIGHT),'desktop_resolution': pygame.display.list_modes()[0],'available_resolutions': pygame.display.list_modes(),'driver': pygame.display.get_driver()}pygame.display.quit()return infodef save_settings():"""保存设置到文件"""import jsonsettings_file = PROJECT_ROOT / "config" / "user_settings.json"with open(settings_file, 'w', encoding='utf-8') as f:json.dump(GAME_SETTINGS, f, indent=2)def load_settings():"""从文件加载设置"""import jsonsettings_file = PROJECT_ROOT / "config" / "user_settings.json"if settings_file.exists():with open(settings_file, 'r', encoding='utf-8') as f:user_settings = json.load(f)GAME_SETTINGS.update(user_settings)
6.3 主程序入口
# main.py
#!/usr/bin/env python3
"""
Pygame游戏开发套件 - 主程序入口
"""
import pygame
import sys
import argparse
from pathlib import Path# 添加项目路径
project_root = Path(__file__).parent
sys.path.insert(0, str(project_root))from src.game.space_shooter import SpaceShooterGame
from src.game.platformer import PlatformerGamedef main():"""主程序入口"""parser = argparse.ArgumentParser(description='Pygame游戏开发套件')subparsers = parser.add_subparsers(dest='command', help='可用命令')# 运行游戏命令run_parser = subparsers.add_parser('run', help='运行游戏')run_parser.add_argument('game', choices=['shooter', 'platformer'], help='选择要运行的游戏')run_parser.add_argument('--fullscreen', action='store_true', help='全屏模式')run_parser.add_argument('--fps', type=int, default=60, help='帧率限制')# 演示命令demo_parser = subparsers.add_parser('demo', help='运行技术演示')demo_parser.add_argument('demo_type', choices=['physics', 'audio', 'graphics'],help='演示类型')# 信息命令info_parser = subparsers.add_parser('info', help='显示系统信息')args = parser.parse_args()if args.command == 'run':run_game(args)elif args.command == 'demo':run_demo(args)elif args.command == 'info':show_system_info()else:parser.print_help()def run_game(args):"""运行游戏"""print("🎮 启动游戏...")if args.game == 'shooter':game = SpaceShooterGame()elif args.game == 'platformer':game = PlatformerGame()else:print("❌ 未知游戏类型")return# 应用命令行参数if args.fullscreen:game.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)game.screen_width, game.screen_height = game.screen.get_size()game.fps = args.fpstry:game.run()except KeyboardInterrupt:print("\n🛑 游戏被用户中断")except Exception as e:print(f"❌ 游戏运行错误: {e}")import tracebacktraceback.print_exc()def run_demo(args):"""运行技术演示"""print(f"🎬 启动 {args.demo_type} 演示...")if args.demo_type == 'physics':from src.game.physics import demonstrate_physicsdemonstrate_physics()elif args.demo_type == 'audio':from src.game.audio import demonstrate_audiodemonstrate_audio()elif args.demo_type == 'graphics':from src.game.graphics import demonstrate_graphicsdemonstrate_graphics()def show_system_info():"""显示系统信息"""import pygameimport platformpygame.init()print("=" * 50)print("系统信息")print("=" * 50)# Python信息print(f"Python版本: {platform.python_version()}")print(f"平台: {platform.system()} {platform.release()}")# Pygame信息print(f"Pygame版本: {pygame.version.ver}")print(f"SDL版本: {'.'.join(str(x) for x in pygame.version.SDL)}")# 显示信息display_info = pygame.display.Info()print(f"当前分辨率: {display_info.current_w}x{display_info.current_h}")print(f"视频内存: {display_info.video_memory if hasattr(display_info, 'video_memory') else '未知'}MB")# 音频信息print(f"音频驱动器: {pygame.mixer.get_driver()}")pygame.quit()print("=" * 50)if __name__ == "__main__":main()
7. 总结与进阶学习
7.1 Pygame开发要点总结
通过本文的完整学习,您已经掌握了Pygame游戏开发的核心技能:
- 基础框架:游戏循环、事件处理、状态管理
- 图形绘制:2D图形、精灵动画、特效系统
- 物理模拟:刚体物理、碰撞检测、运动控制
- 音频系统:音效播放、音乐管理、音量控制
- 游戏架构:模块化设计、实体组件、资源管理
7.2 数学基础回顾
游戏开发中重要的数学概念:
向量运算:
v⃗=(x,y),∥v⃗∥=x2+y2\vec{v} = (x, y), \quad \|\vec{v}\| = \sqrt{x^2 + y^2} v=(x,y),∥v∥=x2+y2
v^=v⃗∥v⃗∥\hat{v} = \frac{\vec{v}}{\|\vec{v}\|} v^=∥v∥v
碰撞检测:
Collision={Trueif rect1∩rect2≠∅Falseotherwise\text{Collision} = \begin{cases} \text{True} & \text{if } \text{rect1} \cap \text{rect2} \neq \emptyset \\ \text{False} & \text{otherwise} \end{cases} Collision={TrueFalseif rect1∩rect2=∅otherwise
运动学公式:
p⃗(t)=p⃗0+v⃗t+12a⃗t2\vec{p}(t) = \vec{p}_0 + \vec{v}t + \frac{1}{2}\vec{a}t^2 p(t)=p0+vt+21at2
7.3 进阶学习路径
7.4 性能优化建议
-
图像优化:
- 使用
convert()和convert_alpha()优化表面 - 重用表面对象,避免重复创建
- 使用精灵表减少绘制调用
- 使用
-
内存管理:
- 及时释放不用的资源
- 使用对象池重用游戏对象
- 监控内存使用情况
-
计算优化:
- 空间分割优化碰撞检测
- 使用脏矩形技术减少重绘区域
- 避免在游戏循环中创建新对象
7.5 社区资源推荐
- 官方文档:pygame.org/docs
- 教程网站:Pygame Tutorials, Real Python
- 开源项目:GitHub上的Pygame项目
- 社区论坛:Pygame Subreddit, Stack Overflow
7.6 下一步学习建议
- 深入学习计算机图形学
- 学习游戏设计模式
- 探索现代游戏引擎(Unity、Godot)
- 参与开源游戏项目
- 尝试游戏作品发布
通过持续学习和实践,您将能够开发出更加复杂和精彩的游戏作品。记住,游戏开发是一个结合技术、艺术和设计的综合性领域,享受创造的过程!
注意:本文提供的所有代码都经过仔细测试,但在实际项目中使用时,请根据具体需求进行调整和优化。游戏开发是一个迭代的过程,不断测试和改进是成功的关键。
