Pygame大富翁毕设项目:从环境部署到状态机改造实战
简介这是一份基于 Pygame 的大富翁游戏毕业设计项目面向计算机相关专业在校学生、教师及游戏开发入门者适合用于毕业设计、课程设计、大作业或项目初期演示。项目以 Python 实现覆盖地图绘制、角色移动、骰子判定、事件触发等核心玩法逻辑完整、功能可用并配有说明文档简单部署即可运行。压缩包共 37 个文件整体约 16.3MB主体是 1 个 Python 启动脚本其余主要是 22 张 PNG 图片素材、6 个 WAV 与 1 个 OGG 音频、2 个 TTF 字体以及 README、游戏说明文档等文本图片、字体、音频分目录存放结构清晰。目前已有 47 人学习/下载。说明文档覆盖目录结构与玩法介绍启动即可上手源码保留合理的模块划分便于按需修改适合在此基础上扩展新玩法或移植到其他场景。1. 别小看 Pygame 大富翁从 zip 到能跑中间隔着三个坑从网上下载的《基于 Pygame 的大富翁游戏》毕业设计 zip解压后通常能看到 TheRich-master 目录、StartGame.py 和 resource 文件夹听上去很简单直接python StartGame.py就能跑。但实际在毕设机房或自己电脑上第一眼看到的往往是黑色窗口一闪而过或者一串ModuleNotFoundError。问题通常出在三处pygame 没装进当前环境、资源路径没有基于脚本目录解析、Windows 终端编码干扰了启动输出。这个项目本质上是一个回合制状态机加一套 Pygame 渲染循环逻辑不复杂可踩坑的地方全在工程细节上。这篇文章就按“拆结构、跑起来、改出亮点、答辩前验证”的顺序把从 zip 到能演示、能答辩的完整路径说清楚适合用 Pygame 做毕设的学生也适合想快速接手这类源码的人。2. 拆解 TheRich大富翁的代码结构不按 MVC 走也能很清晰很多大富翁源码没有用经典 MVC 分层而是把窗口初始化、资源加载、事件分发全部堆在 StartGame.py 里。TheRich 这个项目同样保留了这种课设风格但目录拆得还算规整至少能把图片、字体和声音独立出来。接手的第一件事不是读代码而是先把目录地图画出来。2.1 入口与 resource 目录藏着什么先看解压后的文件清单典型的资源结构大致如下路径作用StartGame.py游戏入口负责初始化 Pygame 并进入主循环TheRich-master/resource/pic地图图块、角色头像、骰子图片、菜单背景TheRich-master/resource/font中文字体文件用于渲染游戏内文本TheRich-master/resource/sound背景音乐与掷骰子、购买地产等音效游戏说明文档.txt操作方式、游戏规则、胜负条件README.md运行环境和启动方式的简要说明从这份清单能看出资源文件是否齐全直接决定了游戏能不能显示中文、能不能出声音。很多同学拿到的 zip 是别人压缩后再上传的resource 目录可能被部分丢失结果pygame.image.load在初始化阶段直接抛异常。拿到资源先检查一遍pic下是否有地图图片、font下是否有.ttf文件这个动作比读代码更快。再往代码层看这类课设的常见写法是让 StartGame.py 里的main()创建pygame.display.set_mode然后把screen对象传给各个绘制函数。这种写法在几百行的项目里是可维护的但一旦要加多级菜单、道具系统函数间互相传screen就会变得很乱。好在这套源码的功能点基本都在棋盘地图和回合结算上区域划分还算清楚。2.2 游戏循环和事件处理为什么骰子会连掷两次Pygame 游戏的骨架是while True加上pygame.event.get()大富翁也不例外。启动后主循环会一直轮询鼠标、键盘、退出事件再调用对应的绘制函数刷新窗口。一个基础但完整的循环如下import pygame import sys FPS 60 def main(): pygame.init() screen pygame.display.set_mode((1280, 720)) pygame.display.set_caption(TheRich 大富翁) clock pygame.time.Clock() while True: for event in pygame.event.get(): if event.type pygame.QUIT: pygame.quit() sys.exit() if event.type pygame.MOUSEBUTTONDOWN and event.button 1: handle_click(pygame.mouse.get_pos()) screen.fill((255, 255, 255)) draw_background(screen) draw_players(screen) pygame.display.flip() clock.tick(FPS)这里的clock.tick(FPS)控制帧率FPS 通常设在 60让动画平滑运行同时避免明显掉帧。MOUSEBUTTONDOWN对应鼠标左键点击handle_click内部要判断当前处于哪个游戏阶段。我在这类项目里见过最经典的 bug玩家点击一次掷骰子结果角色连续跳了两格。原因往往是点击事件没有消费完pygame.event.get()在下一帧又读到了同一个MOUSEBUTTONDOWN处理函数却把“掷骰子”和“确认移动”绑定在同一个鼠标事件上导致逻辑重复执行。模块级变量来保存当前游戏阶段是这套源码最基本的控制手段。比如用game_state PLAYER_ROLL表示等待掷骰子PLAYER_MOVE表示移动中BUY_PROPERTY表示地产结算。不同状态下对同一鼠标点击事件做不同分支能避免大量嵌套 if 把事件循环搅成一团。2.3 资源加载与中文字体resource/font 的存在意义Pygame 默认字体不支持中文直接pygame.font.Font(None, 30)渲染“开始游戏”会输出一串方块字符。TheRich 把字体文件放在 resource/font 下启动时需要用指定路径加载def load_font(size): return pygame.font.Font(resource/font/STKAITI.TTF, size)注意这里如果直接在终端里用相对路径启动工作目录可能在项目外导致字体找不到。更稳的做法是用基于文件的路径解析import os BASE_DIR os.path.dirname(os.path.abspath(__file__)) FONT_PATH os.path.join(BASE_DIR, resource, font, STKAITI.TTF)图片资源同理。地图、骰子、角色头像如果带透明背景要用convert_alpha()保持 alpha 通道直接convert()会把透明区域变成黑色方块一眼就能看出问题。声音加载则要在pygame.init()之后进行否则pygame.mixer可能还没初始化加载音频会报pygame.error: mixer not initialized。3. 本地部署与 pygame 安装让下载的源码直接跑起来拿到这份资源的下一步是把它在自己电脑上跑通。大富翁游戏的部署成本比 Web 项目低很多不需要数据库不需要 Redis难点几乎全部集中在 Python 环境和 Pygame 依赖上。不少人的项目失败在第一步用系统 Python 直接pip install把环境搞乱了再回头排查找不到是哪个包冲突。3.1 环境准备用虚拟环境隔离避免污染系统 Python不管你是用 Windows、Linux 还是 macOS我都建议先从虚拟环境开始。项目本身没有复杂的第三方依赖一个虚拟环境足够python -m venv .venv # Windows .venv\Scripts\activate # Linux / macOS source .venv/bin/activate pip install pygame激活后pip install pygame会把 Pygame 装到项目隔离环境里不会影响你有机器学习课设依赖的全局 Python。这里的逻辑是Python 环境多了python StartGame.py用的解释器很可能是系统默认的那个而不是激活的虚拟环境。如果执行python StartGame.py前没有在终端看到命令行前缀变成(.venv)说明环境没激活装的 pygame 自然失效。3.2 运行 StartGame.py 及常见报错环境准备完成后直接在项目根目录执行python StartGame.py如果窗口没有立即出现而在终端看到报错最常见的几种情况列在下面报错信息可能原因处理方式ModuleNotFoundError: No module named pygamepygame 没有安装或装到了别的环境确认虚拟环境已激活执行pip install pygamepygame.error: video system not initializedpygame.init()没有在最前面执行检查入口函数是否最先调用初始化UnicodeDecodeError或乱码Windows 控制台用 GBK 读取了 UTF-8 文本在.py首行加# -*- coding: utf-8 -*-或用系统终端打开再运行双击StartGame.py出现黑框闪退多半是异常信息还没看到进程就退出了。正确做法是先打开命令行再在终端里手工执行python StartGame.py这样 traceback 能完整留在屏幕上。如果你把代码放进 PyCharm 运行也要注意 PyCharm 的 Run Configuration 里默认工作目录是否在项目根目录否则相对路径resource/pic/...会找不到文件。3.3 解决 failed to build pygame 与 wheel 安装失败在 Windows 上pip install pygame通常会下载预编译的 wheel一行命令装完就能用。但在 Linux 或某些 Python 版本环境中pip 可能找不到对应 wheel转而尝试从源码编译于是出现error: failed to build pygame when getting requirements to build wheel这种情况通常是系统缺少 Pygame 编译所需的 SDL 开发库。标准处理流程是安装依赖后重新安装# Ubuntu / Debian 系 sudo apt-get install libsdl2-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-ttf2.0-dev pip install pygame如果是网络原因导致 wheel 下载失败或者镜像源访问慢可以换国内 PyPI 镜像pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pygame装完马上验证版本python -c import pygame; print(pygame.ver)只要这一行能输出版本号说明 pygame 已经可以正常导入。以后遇到failed to build第一反应不应该是怀疑源码而是看编译依赖是否齐全。很多 Pygame 老项目不带 requirements.txt因为只需要一个 pygame但如果你想留一个副本给答辩老师建议自己生成pip freeze requirements.txt这样下次换机器部署时直接pip install -r requirements.txt就能把依赖一次性装齐。4. 把毕业设计做出工程感状态机、存档与手写 UI大富翁这类项目如果只把原始代码跑通答辩时很难和别人的课设拉开差距。评审老师看代码时最关心的不是你写了多少行而是你能不能说明白“游戏流程如何控制”“数据怎么保存”“界面交互点在哪里”。下面三个改造方向都很适合在这套 Pygame 代码上叠加工作量可控但能明显提升工程感。4.1 场景状态机代替多层 if原始代码里最常见的问题是主循环中充满if state START、if state MENU之类的判断。与其继续堆积不如用一个简单状态类把流程切换集中起来class GameState: MAIN_MENU 0 PLAYING 1 PROPERTY_SETTLE 2 GAME_OVER 3 class Game: def __init__(self): self.state GameState.MAIN_MENU def switch_to(self, new_state): self.state new_state # 状态进入时可能需要重置骰子点数、锁定玩家操作这样在事件循环里只需要写if game.state GameState.PLAYING:不用再维护多个布尔变量。状态机的好处是每个状态之间的转换路径清晰比如掷骰子后从PLAYING切到PROPERTY_SETTLE结算完成再切回PLAYING或切到GAME_OVER。答辩时你把状态转换图画在黑板上比贴一堆 if 代码好解释得多。4.2 JSON 存档与读档让答辩演示从中间流程开始大富翁一局动辄半小时答辩现场从头打到尾不现实。给项目加一个最简单 JSON 存档演示时可以加载第 10 回合的进度直接展示破产、房产交易等关键节点。存档逻辑可以独立成模块import json import os SAVE_PATH save.json def save_game(data): with open(SAVE_PATH, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2) def load_game(): if not os.path.exists(SAVE_PATH): return None with open(SAVE_PATH, r, encodingutf-8) as f: return json.load(f)玩家和地产数据必须是纯 Python 类型才能被 JSON 序列化。如果你代码里有Player类需要先转换成字典比如{name: 玩家1, cash: 1500, position: 3}。加载时再根据字典恢复Player对象。这个细节经常被忽略答辩时如果现场跑读档最容易在这里报TypeError: Object of type Player is not JSON serializable。另外存档路径最好不要写死相对路径和资源加载一样用os.path.join(os.path.dirname(__file__), save.json)否则在不同的启动目录下会读不到同一个文件。4.3 手写按钮组件不引入 Pygame GUI 也能做交互“Pygame GUI”在很多人的认知里是pygame_gui这样的第三方库功能丰富但引入后学习成本反而高。对于大富翁这种菜单按钮不过四五个的项目直接用矩形碰撞检测就够了。封装一个按钮类非常快import pygame class Button: def __init__(self, rect, text, font): self.rect pygame.Rect(rect) self.text text self.font font def draw(self, screen): pygame.draw.rect(screen, (200, 200, 200), self.rect) label self.font.render(self.text, True, (0, 0, 0)) screen.blit(label, (self.rect.x 10, self.rect.y 5)) def hit(self, pos): return self.rect.collidepoint(pos)在主循环里检测到鼠标点击时遍历按钮列表调用hit(pos)判断是否按下。这个做法的优点在于代码量少而且所有细节都能在答辩时讲清楚矩形坐标、文字渲染、碰撞检测。相比引入 GUI 库评委反而更容易确认这是你自己写的。下面给出三个改造方向的工作量评估方便你规划时间改造点关键代码位置预计工作量状态机重构游戏主循环的事件分发小半天JSON 存档读档独立工具模块加玩家序列化半天手写按钮组件菜单场景和交互响应小半天改造时注意不要一次性把所有代码都动完。先跑通原始版本再单独改一个模块验证没破坏原有功能再继续下一个。我的习惯是先加存档因为它不动 UI 逻辑风险最小状态机重构放在最后因为需要动主循环。5. 答辩前验证用冒烟测试和日志锁定运行时问题最后这一步很多人会跳过但恰恰是它决定演示现场是流畅还是翻车。资源类项目的验证不需要做单元测试全覆盖但至少要有两个基础手段资源完整性检查和日志输出。5.1 快速检查资源完整性的脚本把关键图片、字体、音效路径写成一个断言脚本放在项目根目录随时运行import os REQUIRED [ StartGame.py, resource/pic/map.png, resource/font/STKAITI.TTF, resource/sound/bgm.mp3, ] for path in REQUIRED: assert os.path.exists(path), f缺少资源: {path} print(资源完整可以启动)在python StartGame.py之前先跑一遍能在五秒内定位是缺文件还是代码问题而不是进入游戏后报一堆找不到文件的异常。5.2 让日志说话logging 记录玩家动作很多 Pygame 项目只靠print调试窗口打开后 print 的内容在控制台滚动但现场演示时没人看。用 logging 把关键动作写进文件出问题时回看更直接import logging logging.basicConfig( levellogging.INFO, filenamegame.log, format%(asctime)s %(levelname)s %(message)s ) logging.info(玩家1 掷骰子得到 %d, dice_value) logging.info(玩家2 购买地产 %s, property_name)答辩前自己玩一局然后看game.log里每个回合的日志是否连贯。如果某一步没有日志输出说明事件处理分支没有被执行问题范围一下子就从整盘游戏缩小到了对应按钮的回调函数。5.3 最后的运行参数建议演示时尽量用窗口模式不要全屏防止不同分辨率下 UI 布局错乱。如果有条件把FPS从 60 改到 30能降低集成显卡或虚拟机中的卡顿概率。如果你的代码支持启动参数可以固定一个演示配置比如python StartGame.py --demo让玩家初始资金更多、地图更小方便在五分钟内展示完整流程。没有这个参数也没关系改代码里的默认玩家现金值即可。演示前最后跑一遍冒烟脚本再确认日志目录可写剩下的事情就是打开窗口按节奏操作让大富翁自己把故事讲完。本文还有配套的精品资源点击获取