简介:这是一份基于 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 'pygame' | pygame 没有安装或装到了别的环境 | 确认虚拟环境已激活,执行pip install pygame |
pygame.error: video system not initialized | pygame.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", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) def load_game(): if not os.path.exists(SAVE_PATH): return None with open(SAVE_PATH, "r", encoding="utf-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( level=logging.INFO, filename="game.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,让玩家初始资金更多、地图更小,方便在五分钟内展示完整流程。没有这个参数也没关系,改代码里的默认玩家现金值即可。演示前最后跑一遍冒烟脚本,再确认日志目录可写,剩下的事情就是打开窗口,按节奏操作,让大富翁自己把故事讲完。
本文还有配套的精品资源,点击获取