ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Pygame实战:从零开发抓老鼠小游戏,掌握游戏循环与碰撞检测

Pygame实战:从零开发抓老鼠小游戏,掌握游戏循环与碰撞检测 之前做小游戏练习时经常卡在两件事上一是游戏主循环的逻辑组织容易写乱二是坐标碰撞检测总差半格对不上。网上很多教程只给片段跑起来又缺东少西。这次我用一个叫“鼠来”的抓老鼠小游戏把从环境搭建到完整可玩、再到功能扩展、排错清单的全过程整理成文新手能照着做有基础的人也可以直接抄代码改玩法。本文使用的技术栈是 Python pygame代码都在本地可运行不依赖任何第三方账号或网络服务。读完你至少能掌握游戏循环、事件处理、碰撞检测、得分统计和游戏状态控制这几块核心能力后续想改成贪吃蛇、接水果、打地鼠思路都是一样的。1. 背景与核心概念1.1 “鼠来”是什么“鼠来”是我给这个小游戏起的项目代号简单说就是“老鼠来了快去抓住它”。玩家通过键盘方向键控制一只“猫”在地图里移动抓随机出现的老鼠。每抓到一只得分加一同时老鼠会刷新到另一个随机位置。游戏过程中老鼠会随着时间或得分增加而移动得更快整体难度逐渐上升。它本质上是一个基于网格坐标的即时反应类小游戏核心模型并不复杂有一个二维坐标系窗口被划分为等大小的单元格。玩家角色和老鼠都占用一个格子。玩家通过键盘事件控制移动方向。每次移动后判断玩家坐标与老鼠坐标是否重合。重合则计分并刷新目标位置。这类游戏在游戏开发学习里的地位类似编程语言里的“Hello World”规模小但把最核心的事件循环、输入处理、状态更新和渲染刷新都串起来了。1.2 为什么用 pygame 实现pygame 是 Python 最常用的 2D 游戏开发库之一它基于 SDL 开发封装了窗口创建、图像绘制、键盘鼠标事件、音效播放等底层能力。选择它的原因有三个上手成本低。只需要安装一个依赖包就能创建窗口并绘制图形。逻辑直观。游戏循环就是“处理事件 - 更新状态 - 重新绘制”这三步非常适合学习。生态成熟。网上关于 python 游戏、pygame 小游戏、方向键控制、碰撞检测的资料非常多出错时容易排查。不过要提前说明pygame 不是一个高性能游戏引擎它适合教学、原型验证和轻量小游戏不适合做大型 3D 项目。学习它的意义在于理解游戏开发的基本原理而不是追求商业级画面。1.3 常见应用场景除了写小游戏娱乐这类项目还能用在很多地方教学演示用 pygame 讲清“事件驱动编程”和“循环状态更新”模型。算法可视化把网格坐标、碰撞检测、随机生成等概念做成可视化的交互小工具。自动化测试练习通过模拟按键事件练习程序健壮性。体感交互原型如果需要验证某个交互逻辑先用 pygame 快速搭原型比直接上游戏引擎成本低。对初学者来说最重要的收获是理解“程序不是从头到尾跑一遍就结束而是不断循环、不断响应外部输入”。这个思维方式对后续学习 GUI 开发、游戏引擎、甚至后端服务的事件处理都有帮助。2. 环境准备与版本说明2.1 运行环境本文示例以常见开发环境为准版本需要根据你本机实际情况调整重点演示配置思路。我本地的环境是操作系统Windows 10 / 11macOS 和主流 Linux 发行版也可以运行。Python3.9 及以上建议使用 3.10 或 3.11。pygame2.5 及以上版本。编辑器VS Code 或 PyCharm 均可。如果你还没有安装 Python建议先到 Python 官网下载对应系统版本安装时勾选“Add Python to PATH”避免后续在命令行中找不到 python 命令。2.2 安装 pygame打开终端或命令行窗口执行下面的命令pip install pygame如果你的机器上同时安装了多个 Python 版本可能需要用 pip3 或 python -m pippython -m pip install pygame安装完成后可以进入 Python 交互环境验证版本import pygame print(pygame.version.ver)正常会输出类似 2.5.2 的版本号。如果这里报错 ModuleNotFoundError说明安装没有成功或者当前命令行指向的 Python 环境不对。2.3 示例项目结构为了后续扩展方便项目一开始就按下面的结构组织mouse_come/ ├── main.py # 游戏入口 ├── config.py # 配置常量 └── README.md # 项目说明第一版为了跑通核心逻辑只保留 main.py 就够了config.py 在扩展阶段使用。下面我会按照“先跑通再优化”的顺序讲解。3. 核心原理拆解很多初学者一写游戏就容易把代码堆在一起然后发现改一个功能牵一发动全身。所以要先把几个核心概念拆开讲清楚。3.1 游戏循环pygame 程序不会像普通脚本那样从头执行到尾就退出而是进入一个持续运行的循环。这个循环可以做下面四件事监听事件用户按了哪个键、是否点击了关闭按钮。根据事件更新游戏状态方向变化、位置移动、分数增加。清空画布并重新绘制所有对象。控制帧率避免程序跑得太快。循环终止的条件通常是用户关闭窗口或主动退出程序。核心骨架如下running True while running: for event in pygame.event.get(): if event.type pygame.QUIT: running False # 更新状态 # 绘制画面 # 刷新显示这里的关键点是每一帧的画面都基于当前状态重新生成而不是在旧图上涂抹。所以你放心清空画布然后全部重画即可。3.2 事件与方向控制pygame 把用户操作封装为事件常见的有QUIT用户点击窗口关闭按钮。KEYDOWN键盘按键被按下。KEYUP键盘按键被松开。MOUSEBUTTONDOWN鼠标按键按下。在“鼠来”中最重要的是 KEYDOWN。按下方向键时修改猫的移动方向。这里有一个设计选择每次按键只移动一格还是按住方向键持续移动按键移动一格逻辑简单不容易穿墙适合网格类游戏。持续移动需要维护一个方向向量每帧都按方向移动。本文第一版采用“按键一次移动一格”这样玩家操作节奏更可控碰撞检测也更精确。3.3 坐标与碰撞检测玩过游戏的人都知道碰撞检测最怕“看着明明重叠了代码却判断没碰到”。这是因为对象模型和显示模型不一致。在“鼠来”里我把窗口划分成 20 像素 x 20 像素的网格玩家和老鼠的坐标永远是网格的整数倍。因为两者占据的都是一个完整网格所以碰撞判断就可以简化为坐标相等if player_pos mouse_pos: # 抓到老鼠如果不用网格而是像素级坐标你可能需要判断两个矩形是否相交那就要处理四个边界的大小比较。网格模型把这个过程大大简化了。3.4 绘制与刷新pygame 的绘制逻辑也很有规律。先用屏幕填充背景色然后按顺序绘制各个对象最后调用 pygame.display.flip() 把绘制结果显示出来。一个常见误区是“把绘制写在更新状态之前”导致画面表现滞后一帧。虽然偶尔一帧看不出问题但如果后续做粒子效果、动画平滑时顺序错乱就会出现闪烁或错位。规范顺序是处理输入更新状态绘制刷新4. 完整实战案例这一节会给出从零到可运行的完整代码。所有代码都写在 main.py 中设计上尽量少魔法数字常量和逻辑分离方便修改。4.1 初始化 pygame首先导入依赖并初始化窗口# 文件路径mouse_come/main.py import pygame import random import sys # 初始化 pygame pygame.init() # 窗口尺寸 WIDTH 600 HEIGHT 400 CELL_SIZE 20 FPS 10 # 颜色定义 WHITE (255, 255, 255) BLACK (0, 0, 0) RED (200, 0, 0) BLUE (0, 100, 255) # 创建窗口 screen pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption(鼠来 - 抓老鼠小游戏) # 游戏时钟用于控制帧率 clock pygame.time.Clock()WIDTH 和 HEIGHT 是窗口逻辑尺寸CELL_SIZE 是网格大小。窗口宽度 600、高度 400 意味着横向有 30 个格子纵向有 20 个格子。4.2 创建玩家和老鼠玩家用一个列表记录坐标鼠标同理。生成老鼠坐标时必须保证它落在网格点上# 玩家初始位置 player_pos [100, 100] # 老鼠初始位置 def random_mouse_pos(): max_x (WIDTH - CELL_SIZE) // CELL_SIZE max_y (HEIGHT - CELL_SIZE) // CELL_SIZE x random.randint(0, max_x) * CELL_SIZE y random.randint(0, max_y) * CELL_SIZE return [x, y] mouse_pos random_mouse_pos() # 得分 score 0 font pygame.font.SysFont(Arial, 28)这里需要注意random.randint(0, max_x) 的上限不能写成 WIDTH // CELL_SIZE否则有可能把老鼠生成在窗口右边界之外。因为一个格子本身占用 CELL_SIZE 大小的像素坐标必须满足 pos CELL_SIZE WIDTH。4.3 实现移动逻辑接下来是事件处理。当用户按下方向键时猫移动一个格子def move_player(direction): global player_pos dx, dy direction new_x player_pos[0] dx * CELL_SIZE new_y player_pos[1] dy * CELL_SIZE # 边界限制防止走出窗口 new_x max(0, min(new_x, WIDTH - CELL_SIZE)) new_y max(0, min(new_y, HEIGHT - CELL_SIZE)) player_pos [new_x, new_y]这是把移动逻辑单独抽成函数避免在事件循环里堆太多代码。4.4 编写游戏主循环主循环整合事件、移动、碰撞、绘制# 游戏主循环 running True while running: for event in pygame.event.get(): if event.type pygame.QUIT: running False elif event.type pygame.KEYDOWN: if event.key pygame.K_UP: move_player((0, -1)) elif event.key pygame.K_DOWN: move_player((0, 1)) elif event.key pygame.K_LEFT: move_player((-1, 0)) elif event.key pygame.K_RIGHT: move_player((1, 0)) elif event.key pygame.K_ESCAPE: running False # 碰撞检测 if player_pos mouse_pos: score 1 mouse_pos random_mouse_pos() # 绘制 screen.fill(WHITE) # 绘制玩家 pygame.draw.rect(screen, BLUE, (player_pos[0], player_pos[1], CELL_SIZE, CELL_SIZE)) # 绘制老鼠 pygame.draw.rect(screen, RED, (mouse_pos[0], mouse_pos[1], CELL_SIZE, CELL_SIZE)) # 绘制得分 score_text font.render(f得分: {score}, True, BLACK) screen.blit(score_text, (10, 10)) # 刷新显示 pygame.display.flip() # 控制帧率 clock.tick(FPS) # 退出游戏 pygame.quit() sys.exit()如果你复制以上所有代码到一个 main.py运行后就能开始游戏了。4.5 运行与验证在项目目录下运行python main.py预期效果是弹出 600x400 的窗口窗口标题为“鼠来 - 抓老鼠小游戏”。窗口左上角显示蓝色的方块地图中有一个红色的方块。按下方向键蓝色方块朝对应方向移动一格。当蓝色方块移动到红色方块所在格子时得分加一红色方块刷新到新的随机位置。按 ESC 键退出程序。如果运行后方向键控制不灵敏先确认当前窗口是否获得焦点。很多新手在运行游戏后又点击了 IDE 的某个面板导致键盘事件被其他窗口截获。5. 功能扩展计时、难度和音效第一版能跑通后可以继续加入更多功能让游戏更像一个完整的作品。5.1 加入倒计时增加一个游戏总时长限制例如 60 秒。倒计时逻辑放在每帧刷新时计算import time start_time time.time() game_duration 60 # 在主循环内 elapsed time.time() - start_time remaining int(game_duration - elapsed) if remaining 0: running False time_text font.render(f剩余时间: {remaining}s, True, BLACK) screen.blit(time_text, (WIDTH - 220, 10))倒计时结束后可以显示“游戏结束”和最终得分按任意键重新开始。5.2 难度递增为了让游戏有挑战性可以让老鼠在每次被抓到后随机出现在更远的位置或者减少老鼠静止等待时间。最简单的做法是根据得分提高 FPS# 动态调整速度 dynamic_fps min(FPS score * 2, 30) clock.tick(dynamic_fps)但需要注意FPS 提高也会让猫的移动刷新频率增加如果使用的是“按键一格”的移动方式FPS 对操作节奏影响不明显如果是持续移动效果就非常明显。所以难度递增要和你选择的移动模式匹配。5.3 音效与背景音乐pygame.mixer 可以加载 wav 或 mp3 文件。建议只在游戏开始时初始化一次pygame.mixer.init() catch_sound pygame.mixer.Sound(catch.wav) catch_sound.play()资源文件需要放在项目目录下比如新建 resources/sound 文件夹。如果没有现成音效文件也可以先用提示音 API 生成简单音效或者在后期再补。5.4 使用图片素材替换方块方块虽然直观但不够好看。可以使用 pygame.image.load 加载图片player_img pygame.image.load(resources/player.png) # 缩放图片到格子大小 player_img pygame.transform.scale(player_img, (CELL_SIZE, CELL_SIZE)) screen.blit(player_img, (player_pos[0], player_pos[1]))需要注意图片格式建议使用带透明通道的 PNG否则显示时会带底色。6. 常见问题与排查思路下面是做 pygame 小游戏时最常遇到的问题我按“现象 - 原因 - 解决”的顺序整理成表格和排查步骤。问题现象常见原因解决思路运行报 ModuleNotFoundErrorpygame 未安装或环境不对使用 python -m pip install pygame 重新安装确认 import 成功窗口一闪就消失主循环没有正确维护或代码缩进错误检查 while running 是否包含事件处理确认缩进层级方向键按了没反应窗口失焦事件类型判断错误点击游戏窗口让窗口获得焦点检查 KEYDOWN 分支角色移到窗口外消失缺少边界限制在移动函数中用 max/min 限制坐标范围老鼠刷不出新位置随机坐标超出边界检查随机数上限是否扣除了 CELL_SIZE抓到了老鼠但分数不变碰撞判断的坐标和绘制坐标不一致统一使用同一个 player_pos 和 mouse_pos 列表游戏运行太快 / 太慢缺少时钟控制或 FPS 设置不合适创建 clock 并调用 clock.tick(FPS)补充一个初学者容易踩的坑如果在事件循环里写 player_pos [player_pos[0] 1, player_pos[1]]然后又在循环底部对同一坐标做边界处理顺序颠倒就会导致边界失效。我的建议是移动和边界约束放在同一个函数 move_player 里处理调用的时候不会漏。还有一个排查技巧在主循环中加一个临时 print(player_pos, mouse_pos)输出坐标再运行能快速判断碰撞检测是不是坐标差半格。排查清楚后把这个 print 删掉即可。7. 最佳实践与工程建议7.1 代码分层第一版把所有代码写在 main.py 里没问题但当你加入计时、音效、难度、菜单后文件会膨胀到上千行。建议早点拆分mouse_come/ ├── main.py # 主循环 ├── config.py # 常量配置 ├── game.py # 游戏状态和逻辑 └── player.py # 玩家类把配置单独放到 config.py 后修改窗口尺寸、颜色、速度时就不用在代码里到处搜索常量。7.2 使用类封装游戏对象玩家和老鼠都可以抽象成类class Player: def __init__(self, x, y, color): self.x x self.y y self.color color def move(self, dx, dy): self.x dx * CELL_SIZE self.y dy * CELL_SIZE def draw(self, screen): pygame.draw.rect(screen, self.color, (self.x, self.y, CELL_SIZE, CELL_SIZE))类封装的好处是后续如果玩家要增加血量、动画、技能不需要在外面写一堆散落的函数。7.3 异常与边界处理Python 游戏虽然不像后端服务那样强调异常处理但边界情况必须考虑窗口尺寸变化后随机坐标逻辑要同步变化。游戏结束后重置分数、位置、时间。资源文件缺失时给出友好提示而不是直接崩溃。一个简单的防御写法try: player_img pygame.image.load(resources/player.png) except FileNotFoundError: print(警告未找到 player.png使用默认方块替代) player_img None7.4 日志与调试建议在关键节点输出调试日志比如“老鼠重新生成”“游戏结束”。可以使用 Python 的 logging 模块也可以先用 print 简单记录。重点是调试用的 print 要能方便地关闭。7.5 打包与版本管理完成游戏后可以用 PyInstaller 打包成可执行文件pip install pyinstaller pyinstaller --onefile --windowed main.py这个命令会生成一个不带控制台窗口的单独 exe 文件。注意打包前要把 resources 目录放到对应位置否则图片和音效加载不到。代码建议从一开始就用 Git 管理。每完成一个功能提交一次比如 feat: 完成基础移动、feat: 加入倒计时、fix: 修复边界问题。这样以后改坏了也能方便回退。8. 总结与学习路线“鼠来”这个小游戏虽然简单但覆盖了游戏开发的完整链路环境准备、窗口初始化、事件循环、对象移动、碰撞检测、绘制刷新、扩展功能、排错思路。把这些基础打牢之后你其实已经可以做出很多变体游戏。如果你想继续往下深入建议按这个顺序先把这个项目改成持续移动模式感受两种移动方式的差异。加入菜单界面和结束界面学习游戏状态管理。尝试用精灵类和动画替代矩形绘制。做一个小型“接水果”游戏理解多对象碰撞检测。学习 pygame 的 sprite 组和碰撞组函数代码会更高效。尝试做双人对战模式进一步理解多个输入设备事件。在整个过程中遇到问题不要急着搜答案先看报错信息再定位到具体代码行这比“对着报错抄解决方案”更有价值。如果这个项目的代码你有看不懂的地方也可以把问题点发在评论区实战项目里遇到的问题往往比背语法更容易让人进步。
返回列表