软件工程第二次作业-AIGC辅助下的“一箭又一箭”解谜游戏开发
镖飞哪算哪:AIGC辅助下的“一箭又一箭”解谜游戏开发
一、 作业基本信息
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/fzu/2026-01SoftwareEngineeringandSoftwareEngineeringPractice |
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/fzu/2026-01SoftwareEngineeringandSoftwareEngineeringPractice/homework/16718 |
| 这个作业的目标 | 使用 Python 和 AIGC 完成“一箭又一箭”小游戏 |
| 学号 | 052403108 |
| GitHub 仓库 | https://github.com/wang-wang06/one-arrow-game |
一、 项目展示
1.1 开始界面

图1:极光主题的开始界面,包含游戏规则说明和进入游戏按钮。
1.2 游戏过程

图2:游戏主界面,左侧显示得分、时间、飞镖数量与失误次数,右侧为棋盘。
1.3 通关与失败界面


图3:通关界面(显示星级评价与得分)和失败界面(失误次数耗尽)。
二、 项目介绍
2.1 游戏规则
“镖飞哪算哪”是一款点击式解谜游戏。棋盘上随机分布着上、下、左、右四个方向的飞镖。
-
🎯玩家点击飞镖,系统会检测飞镖前进方向到棋盘边界之间是否有其他飞镖阻挡。
-
💯若前方畅通,飞镖飞出棋盘并消失,得 100 分。
-
🚨若前方有阻挡,飞镖无法消失,产生碰撞晃动反馈,扣除 50 分并消耗一次失误机会。
-
🚩 撤销机制:游戏过程中,玩家每执行一次操作(成功消除或碰撞),系统都会自动记录状态。点击“撤销上一步”按钮,可以撤回上次操作,最多支持连续撤销10步。
-
👉 重新开始:玩家可随时点击“重新开始”按钮,将当前关卡的棋盘布局和失误次数重置为初始状态,方便快速重新挑战。
-
🏆清空全部飞镖即可通关;失误次数耗尽则游戏失败。
2.2 界面设计与功能特色
- 视觉风格:采用了“极光(Aurora)”主题,深空靛蓝背景搭配霓虹青、星云紫、极光绿等色彩,棋盘为冰蓝玻璃质感,带有透视网格和光晕效果。
- 核心功能:包含开始界面、关卡选择、游戏界面、通关界面和失败界面。支持进度自动解锁(最高 6 关)。
- 拓展功能:实现了提示功能(高亮可消除的飞镖)、撤销上一步(最多可撤销 10 步)、动态计分与计时、星级评价系统(根据通关时间和失误次数评定 1-3 星),以及程序化音效(通过代码合成正弦波生成飞镖射出、碰撞和胜利音效,无需外部音频文件)。
- 动态与反馈:飞镖消除时有流畅的飞出动画、消除涟漪效果和得分飘字;发生碰撞时飞镖会左右晃动,屏幕下方会弹出“前方有阻挡!”的提示,并产生涟漪反馈。
2.3 开发环境
- 操作系统: Windows 11
- 编程语言:Python 3.10 及以上
- 图形库:Pygame 2.5.x
- 开发工具:PyCharm
- 第三方依赖:
pygame(可通过pip install pygame安装) - 运行依赖资源:
assets/dart.png(飞镖素材)、assets/shiweiyongchunheicuti.ttf(中文字体) - 跨平台兼容性:项目使用相对路径加载资源,并内置了系统字体备用链,支持在 Windows、macOS 和 Linux 环境下运行。
环境配置步骤:
- 安装 Python 3.x(安装时建议勾选“Add Python to PATH”)。
- 打开终端(CMD 或 PowerShell),执行
pip install pygame安装图形库。 - 确保项目根目录包含
assets文件夹及内部资源文件。 - 在项目根目录执行
python main.py即可启动游戏。
三、 实现思路
3.1 箭头、方向与关卡表示
- 箭头表示:使用字典
{"row": r, "col": c, "direction": d, "index": index}
表示每个飞镖。direction 为字符串 "up", "down", "left", "right"。
- 方向映射:通过
get_arrow_angle函数将方向映射为图像的旋转角度(例如"right": 135,"up": -135),用于加载和旋转dart.png。 - 关卡表示:关卡由元组列表
(row, col, direction)组成。为了保证关卡一定可通关,我编写了is_generated_level_solvable函数,利用“逆推法”验证:每次尝试从当前剩余飞镖中找出一个“前方无阻挡”的飞镖并将其移除,如果最后能全部移除,则该关卡可解。
3.2 核心路径检测方法
路径检测是游戏最核心的逻辑。实现思路是:根据方向设定行/列的步进(dr, dc),从当前飞镖位置向该方向逐步遍历,直到走出棋盘边界。如果途中遇到任何其他飞镖,则判定为有阻挡。
# 核心路径检测代码
def is_path_clear_for_arrows(arrow_list, row, col, direction):
dr = dc = 0
if direction == "up": dr = -1
elif direction == "down": dr = 1
elif direction == "left": dc = -1
elif direction == "right": dc = 1
cr, cc = row + dr, col + dc
# 逐步向边界遍历
while 0 <= cr < ROWS and 0 <= cc < COLS:
for a in arrow_list:
if a["row"] == cr and a["col"] == cc:
return False # 途中遇到其他飞镖,阻挡
cr += dr
cc += dc
return True # 安全到达边界,无阻挡
3.3 关卡生成与可解性验证
为了让游戏更有趣,我没有写死关卡,而是实现了随机生成可解关卡的算法。每次开局随机分配飞镖位置和方向后,会调用 is_generated_level_solvable 进行逆向推演:不断尝试从现有飞镖中找出一个“前方无阻挡”的飞镖并将其移除,如果最终能全部移除,说明该关卡一定有解。
# 逆向推演验证关卡是否可解
def is_generated_level_solvable(level_data):
remaining = [{"row": r, "col": c, "direction": d} for r, c, d in level_data]
while remaining:
removable = None
for a in remaining:
if is_path_clear_for_arrows(remaining, a["row"], a["col"], a["direction"]):
removable = a
break
if removable is None:
return False # 没有可消除的,说明死局
remaining.remove(removable)
return True
3.4 飞出与碰撞动画(交互反馈)
作业要求“箭头成功飞出和发生碰撞时,应有基本的动画或视觉反馈”。我在 update_flying_arrows 中引入了缓动函数(Easing Function),让飞镖先加速后减速地飞出,而不是生硬地线性移动。碰撞反馈则利用 math.sin 函数生成左右震动的偏移量。
# 飞行动画缓动函数(先加速后减速)
if t < 0.5:
eased = 4 * t * t * t
else:
eased = 1 - pow(-2 * t + 2, 3) / 2
a["x"] = a["start_x"] + (a["target_x"] - a["start_x"]) * eased
a["y"] = a["start_y"] + (a["target_y"] - a["start_y"]) * eased
3.5 状态管理与撤销机制(工程完整性)
游戏涉及得分、失误、布局等多个状态。每次点击操作前,通过 save_undo_state 利用 copy.deepcopy 保存当前快照,存入历史栈(最多 10 步)。当玩家点击“撤销上一步”时,undo_last_move 弹出最近一次快照并恢复状态。
# 保存状态快照,用于撤销
def save_undo_state():
undo_history.append({
"arrows": copy.deepcopy(arrows),
"score": score,
"mistakes": mistakes,
"moving_arrows": copy.deepcopy(moving_arrows),
})
if len(undo_history) > MAX_UNDO: # 最多保留10步
undo_history.pop(0)
3.6 点击处理与胜负判定
handle_arrow_click 函数是玩家交互的入口。它负责坐标转换、路径判断、得分扣除、触发动画和音效,并在每次操作后检查是否清空棋盘(通关)或失误归零(失败)。
# 点击处理(核心逻辑汇总)
def handle_arrow_click(mouse_pos):
# ... 坐标检测 ...
if is_path_clear(r, c, d):
save_undo_state()
score += SCORE_PER_ARROW
create_flying_arrow(r, c, d, clicked["index"]) # 触发飞出动画
arrows.remove(clicked)
if not arrows:
finish_level() # 通关判定
else:
save_undo_state()
trigger_collision(r, c) # 触发失误与碰撞反馈
3.7 星级评价与时间管理
为了增加游戏的复玩性,我设计了基于“通关时间”和“失误次数”的星级评价系统。calculate_stars 函数根据玩家的表现动态计算1至3星,这促使玩家不仅要通关,还要追求更完美的解法。配合 format_time 函数,时间可以直观地以 MM:SS 形式显示在界面上。
# 星级评价算法
def calculate_stars(elapsed_time, mistakes_used):
if elapsed_time <= 20 and mistakes_used <= 1:
return 3
if elapsed_time <= 35 and mistakes_used <= 3:
return 2
return 1
# 时间格式化(MM:SS)
def format_time(seconds):
seconds = max(0, int(seconds))
return f"{seconds // 60:02d}:{seconds % 60:02d}"
四、测试结果
4.1 测试环境与方法
- 测试环境:Windows 10/11,Python 3.x,Pygame 库。
- 测试方法:手工测试为主,针对游戏的核心规则和边界情况进行反复试玩验证,并观察控制台是否有报错。
4.2 测试用例与结果
| 编号 | 测试内容 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|---|
| T01 | 点击前方无阻挡的箭头 | 箭头飞出棋盘并消失 | 飞镖播放平滑的减速飞出动画,随后消失,得分增加 100 分,伴有射出音效 | 通过 |
| T02 | 点击前方有阻挡的箭头 | 箭头不消失,失误次数减 1 | 飞镖原地左右晃动,屏幕下方弹出“前方有阻挡!”提示,得分扣除 50 分,失误次数减 1,伴有碰撞音效 | 通过 |
| T03 | 点击位于边缘且朝向棋盘外的箭头 | 箭头正常消失,不发生越界错误 | 箭头顺利飞出棋盘,路径检测中的 while 循环在边界处安全结束,程序无报错或卡顿 |
通过 |
| T04 | 消除本关全部箭头 | 显示通关并进入下一关 | 触发通关界面,播放胜利音效,显示星级评价,点击“进入下一关”按钮后正常加载新关卡 | 通过 |
| T05 | 失误次数耗尽 | 显示失败并允许重新开始 | 触发失败界面并播放失败音效,点击“重新挑战”后,当前关卡重置,失误次数恢复为初始值 | 通过 |
| T06 | 游戏进行中重新开始 | 箭头布局和失误次数恢复 | 点击“重新开始”按钮,棋盘重新随机生成布局,失误次数重置,得分清零,撤销历史清空 | 通过 |
4.3 额外边界测试(附加验证)
除了作业要求的 6 项必测内容,我还在测试过程中额外验证了以下场景:
- 连续撤销:连续点击“撤销上一步”按钮,最多可回退 10 步,超过 10 步后按钮变为“暂无可撤销”状态,逻辑正常。
- 提示功能:点击“提示”按钮,正确的可消除飞镖高亮显示,提示音效播放,高亮闪烁约 2 秒后自动消失,不影响正常游戏。
- 跨平台字体:将项目复制到不同分辨率的环境下运行,中文字体通过相对路径加载成功,未出现乱码或报错。
- 快速连点:在极短时间内连续点击同一个飞镖,程序未出现状态冲突、越界或卡死崩溃。
五、 AIGC 使用过程
在本次开发中,我主要使用了 ChatGPT 和 DeepSeek 辅助开发。以下是 3 次具有代表性的 AIGC 使用过程,涵盖核心算法、动画反馈和跨平台兼容性:
5.1 AIGC 使用记录表
| 子任务 | 借助何种 AIGC 技术 | AI 实现或提供了什么 | 效果如何 | 人工修改 |
|---|---|---|---|---|
| 路径检测 | ChatGPT | 生成四个方向的网格路径检测代码,使用 while 循环逐步遍历 |
向上判断时出现数组越界错误(IndexError) |
修改边界条件,确保先判断行列是否越界再访问数组,修复后正常 |
| 关卡设计 | DeepSeek | 建议使用随机生成关卡数组,并给出了正向生成逻辑 | 生成的布局存在无法通关的死局,玩家可能无解可走 | 手工实现“逆向推演”算法(is_generated_level_solvable),从满盘反推消除,确保关卡一定可解 |
| 碰撞动画 | ChatGPT | 补全了箭头碰撞后的晃动代码,使用 math.sin 配合计时器生成偏移量 |
基本可用,但晃动幅度和节奏偏僵硬 | 调整动画速度与衰减系数,修正晃动帧数,使其反馈更自然 |
5.2 详细协作过程
过程一:路径检测逻辑与边界处理
- 我的要求:“请帮我用 Python 写一个函数,判断二维网格中某一行列的箭头,沿指定方向到边界是否被其他箭头阻挡。”
- AI 实现或提供了什么:AI 快速给出了基于
while循环的代码框架,并使用了方向步进dr和dc。 - 效果如何:初始代码在向上检测时出现了数组越界错误(
IndexError)。 - 人工修改:我分析了 AI 的代码,发现边界条件
while 0 <= cr < ROWS写成了先访问数组再判断。我修改了循环条件,确保先进行边界判断,再进行同行同列的坐标比对,修复了越界问题。
过程二:关卡生成与可解性验证
- 我的要求:“如何随机生成一箭又一箭的关卡,并且保证每个关卡都是绝对可以通关的?”
- AI 实现或提供了什么:AI 提供了随机排列箭头位置和方向的代码,并建议通过正向模拟来验证。
- 效果如何:正向生成非常容易出现“死局”(所有箭头都被互相阻挡),导致玩家无法通关。
- 人工修改:我放弃了 AI 的正向思路,自己编写了
is_generated_level_solvable函数,利用“逆向推演”来验证——每次从现有箭头中找出一个“前方无阻挡”的移除,直到全部移除,从而确保生成的关卡百分百可解。
过程三:中文字体跨平台兼容与 UI 设计
- 我的要求:“写一个 Pygame 加载中文字体的代码,要求 Windows 下能正常显示。”
- AI 实现或提供了什么:AI 给出了
C:/Windows/Fonts/msyh.ttc这种写死绝对路径的写法,并使用了大量硬编码的Rect坐标绘制面板。 - 效果如何:能在本地运行,但我意识到如果助教用 Mac 或 Linux 拉取代码会直接报错或乱码。
- 人工修改:我进行了重构,将字体文件放进
assets文件夹,改为相对路径os.path.join("assets", "shihu.ttf")加载,并增加了系统字体作为备用容错。同时,我将 AI 写死的坐标进行了参数化封装(如draw_button、draw_text_centered_with_glow),使 UI 更具可维护性。
六、 PSP 表格
| 任务 | 预估耗时(小时) | 实际耗时(小时) | 差异(小时) |
|---|---|---|---|
| 需求分析与游戏设计 | 1.5 | 2.0 | +0.5 |
| Python 与图形库学习 | 2.0 | 3.0 | +1.0 |
| 游戏界面实现 | 3.0 | 4.5 | +1.5 |
| 路径与碰撞逻辑实现 | 2.0 | 2.5 | +0.5 |
| 关卡设计 | 2.0 | 2.5 | +0.5 |
| AIGC 辅助开发 | 1.5 | 1.5 | 0 |
| 测试与修改 | 1.5 | 2.0 | +0.5 |
| README 与博客撰写 | 1.5 | 2.0 | +0.5 |
| 合计 | 15.0 | 20.0 | +5.0 |
6.1 耗时偏差分析
- Python 与图形库学习:实际耗时比预估多出 1 小时,主要因为 Pygame 的缓动动画和程序化音效生成需要额外的学习和调试时间。
- 游戏界面实现:实际耗时超出较多(+1.5 小时),因为“极光主题”的渐变背景、光晕面板和霓虹按钮需要手写大量的自定义绘制代码,比预想的复杂。
- 路径与关卡逻辑:基本符合预估,AI 在算法思路上提供了帮助,但人工修改逆向推演算法多花了一些时间。
- 整体偏差:总耗时超出预估约 33%,主要投入在界面美化和动画调试上。这让我认识到,UI/UX 的打磨往往比核心逻辑更耗费时间。
七、心得体会
本次“一箭又一箭”小游戏开发,是我第一次深度结合 AIGC 工具完成一个完整的软件项目。在这个过程中,我既体会到了 AI 带来的效率飞跃,也踩了不少 AI 生成代码的“坑”,更在实践中加深了对软件开发的理解。
7.1 AI 带来的帮助
- 快速构建代码框架:在项目初期,AI 很快帮我理清了 Pygame 的事件循环、坐标系统与绘制逻辑,生成了基础的游戏框架,省去了大量查文档的时间。
- 提供算法思路:在核心的“路径检测”和“缓动动画”上,AI 提供了清晰的实现思路(如使用
dr、dc步进配合while循环进行遍历,以及利用三次贝塞尔公式实现平滑动画),极大降低了学习成本。 - 拓展功能边界:在实现撤销、提示和星级评价等拓展功能时,AI 提供了状态快照和数学评价模型的代码片段,让我可以把更多精力放在游戏机制的设计上,而不是纠结于语法细节。
7.2 出现的问题
- 边界处理不严谨:AI 生成的路径检测代码在向上(
up)方向判断时,出现了数组越界(IndexError)。AI 写出的while循环是先访问数组再判断边界,这在边界情况下非常危险。 - 算法存在逻辑漏洞:在关卡生成上,AI 最初给出的“正向随机生成”方案极易生成死局(所有箭头互相阻挡,玩家无解可走),如果直接使用,会导致关卡实际上无法通关。
- 代码可维护性低:AI 生成的 UI 代码存在大量硬编码的
Rect坐标和魔法数字,导致后期调整界面和优化布局时非常费力。
7.3 个人的收获与成长
- 学会判断与审查 AI 代码:这次经历让我明白,AI 是强大的助手,但开发者必须是代码的最终负责人。不能盲目复制粘贴 AI 生成的代码,必须逐行理解,尤其是边界条件和路径加载这类容易出错的逻辑。
- 算法思维与工程能力的提升:为了解决 AI 的死局问题,我自研了“逆向推演”算法来验证关卡可解性;为了解决跨平台问题,我学会了使用相对路径和容错机制;为了保证项目完整性,我封装了统一的按钮绘制和文字渲染函数。
- 理解了“以 AI 为伴”的开发模式:AI 可以迅速提供 60 分的代码,但剩下 40 分的优化、边界处理、业务逻辑和用户体验,必须由开发者来完成。会提问、会 Debug、会重构,才是 AIGC 时代开发者的核心竞争力。
- 体会到完整软件开发的成就感:从零开始设计界面、编写逻辑、调试动画、测试边界,直到最后看到游戏流畅运行、成功上传 GitHub,这个过程让我对软件工程有了更深刻的体会,也更有信心迎接未来的开发挑战。
浙公网安备 33010602011771号