软件工程第二次个人作业——《元素之箭 · 一箭又一箭》
《元素之箭 · 一箭又一箭》—— AIGC 完成箭头解谜小游戏
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | 软件工程与软件工程实践(福州大学) |
| 这个作业要求在哪里 | 作业链接 |
| 这个作业的目标 | 使用 Python 和 AIGC 完成“一箭又一箭”小游戏 |
| 学号 | 102402138 |
| GitHub 仓库 | https://github.com/Xun1342/Software-Engineering-Assignment-2-elemental-arrows |
一、项目展示
GIF展示

开始界面:七元素彩带云海背景、玩法说明、入口按钮,右上角是原创精灵向导「灵灵」。

游戏过程:竖版 540×860,网格点阵棋盘,七元素配色的宝石箭头,HUD 实时显示时间 / 步数 / 剩余 / 失误心形;点“提示”后当前可飞出的箭头会带金色脉动描边。

通关 / 失败界面:通关时精灵庆祝、彩屑飘落,三颗星依次弹出并按失误次数评星;失败时精灵安慰并允许重开。

选关与随机关卡:六个手工关卡卡片(星级、最佳成绩、锁定状态),另有“随机试炼”每次生成全新可通关棋盘。

二、项目介绍
这是一个用 Python + pygame-ce 实现的点击解谜小游戏:观察每支箭头的方向,按正确顺序点击,让所有箭头依次飞出棋盘。全部素材均为原创生成,未使用任何商业游戏的代码、美术、音效或关卡。
规则:
- 点击一支箭头,程序沿它的朝向扫描,检查同一行(左右)或同一列(上下)上、箭头到棋盘边界之间是否还有其他箭头;
- 前方没有箭头 → 加速飞出棋盘并消除,播放星光爆发与音效;
- 前方有箭头 → 原地晃动警示、红屏闪烁、扣一次失误;
- 清空全部箭头通关,按失误次数评星;失误耗尽则失败。
主要功能与特色:
- 六个手工设计关卡(5×5 ~ 8×8,6~22 支箭头),难度递进;
- 提示按钮:金色脉动高亮当前所有可飞出的箭头(快捷键 H);
- 计时、步数统计、撤销(Z)、重开(R);
- 选关界面 + 三星评价 + 本地存档(
save.json记录星级、最佳成绩、解锁进度); - 随机关卡:逆向摆放算法即时生成 7×7、15 箭、保证可通关的新棋盘;
- 自动演示:DFS 求解器求出通关顺序,由精灵逐支自动飞出,可随时停止;
- 奇幻表现:原创白色精灵向导「灵灵」(圆身体、小翅膀、四角星天线,有开心/惊讶/沮丧/庆祝表情和对话气泡)、七色环境光点、飞出星光、碰撞火星、通关彩屑。
三、实现思路
3.1 数据表示
棋盘用二维网格,每支箭头就是一个 (行, 列, 方向) 三元组,方向是 U/D/L/R 四个字母之一;元素色按七元素循环编号。关卡就是一个字典,直接写在 game/levels.py 里,以后扩关只需追加一个字典:
{
"name": "风起 · 试炼",
"rows": 5, "cols": 5,
"mistakes": 3, # 允许失误次数
"accent": 2, # 本关主色(七元素之一)
"arrows": [(0, 1, "L"), (0, 3, "R"), (2, 0, "U"),
(2, 2, "U"), (2, 4, "D"), (4, 2, "U")],
}
3.2 核心:路径检测
这是整个游戏最关键的一段。方向先换算成行列增量 (dr, dc),然后从箭头的相邻格开始一步一步走向边界;一路上只要碰到任何还没飞出的箭头,就说明“被挡住”,返回不可飞;走到边界都没碰到,就是可以飞出。
def is_clear_to_edge(self, arrow):
"""判断沿箭头朝向到边界之间是否还有其他箭头。"""
dc, dr = S.DIRS[arrow.direction] # 方向 -> (列增量, 行增量)
r, c = arrow.row + dr, arrow.col + dc
while 0 <= r < self.rows and 0 <= c < self.cols:
other = self.arrows.get((r, c))
if other is not None and other.state != "gone":
return False # 被挡住
r += dr
c += dc
return True # 一路畅通,可以飞出
“提示”和“自动演示”都复用它:把所有箭头过一遍筛出 idle 且 is_clear_to_edge() 的集合即可。
3.3 撤销:快照/恢复
撤销没有去做复杂的命令栈,而是在每次成功飞出之前,把当前所有待机箭头的 (行, 列, 方向, 元素色) 存进一个栈;撤销时出栈一份重建棋盘,同时恢复当时的失误数和步数:
def snapshot(self):
return [(a.row, a.col, a.direction, a.element_idx)
for a in self.arrows.values() if a.state == "idle"]
def restore(self, data):
self.arrows = {(r, c): Arrow(r, c, d, e) for r, c, d, e in data}
3.4 随机关卡为什么“保证有解”
如果随机乱摆箭头,很容易生成无解死局。这里用逆向摆放:从空棋盘开始,每次随机放一支箭头,只放在“沿它的方向到边界之间还没有任何已放箭头”的位置。这样放完之后,按摆放顺序倒过来点,就一定能通关。生成后再用一个独立的 DFS 求解器验证一遍,双重保险:
def solve(arrows, rows, cols):
"""DFS 求一个通关顺序;无解返回 None。"""
state = {(r, c): d for r, c, d in arrows}
def dfs(st, path):
if not st:
return path
for (r, c), d in list(st.items()):
if clear(st, r, c, d, rows, cols): # 该箭头当前无阻挡
nxt = dict(st); nxt.pop((r, c))
found = dfs(nxt, path + [(r, c, d)])
if found is not None:
return found
return None
return dfs(state, [])
四、AIGC 使用过程
| 子任务 | 借助何种 AIGC 技术 | AI 实现或提供了什么 | 效果如何 | 人工修改 |
|---|---|---|---|---|
| 路径检测与游戏骨架 | 豆包2.1Pro | 生成四方向行列扫描、飞出/碰撞动画、主循环与界面切换初版和其他游戏主体框架 | 不能跑 | 查找报错,是本地编译器问题,切换编译器和下载相关软件,成功运行 |
| 自动化测试 | 豆包2.1Pro | 按 T01–T06 写 unittest 验收用例,并补了 T07–T13 | 13 个用例一次基本通过 | 无 |
| 美术背景与音效 | 豆包2.1Pro | 文生图生成七元素云海背景;用标准库波形合成 5 个音效 | 无版权风险、体积小 | 无 |
| 使用记录如下: | ||||
| 生成整体游戏框架 | ||||
![]() |
||||
![]() |
||||
| 处理报错 | ||||
![]() |
||||
| 增加功能 | ||||
![]() |
五、测试结果
自动化测试


用例文件 tests/test_acceptance.py,在无头环境(SDL_VIDEODRIVER=dummy)下驱动真实游戏帧循环运行,共 13 个用例,全部通过:
$env:SDL_VIDEODRIVER="dummy"; $env:SDL_AUDIODRIVER="dummy"
python -m unittest tests.test_acceptance -v
# Ran 13 tests in 0.7s —— OK
对应作业要求的 T01–T06:
| 编号 | 测试内容 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|---|
| T01 | 点击前方无阻挡的箭头 | 箭头飞出棋盘并消失 | 进入 flying,动画结束后移除,剩余 6→5,失误不变 | 通过 |
| T02 | 点击前方有阻挡的箭头 | 箭头不消失,失误次数减 1 | 箭头晃动后仍在原地,失误 3→2,红闪提示 | 通过 |
| T03 | 点击边缘、朝向棋盘外的箭头 | 正常消失,不越界 | 六关逐帧校验待机箭头坐标均在棋盘内,飞出无索引错误 | 通过 |
| T04 | 消除本关全部箭头 | 显示通关并进入下一关 | 弹出结算面板,点“下一关”后装载第二关 | 通过 |
| T05 | 失误次数耗尽 | 显示失败并允许重开 | 连续 3 次被挡后进入失败界面,重开后布局/失误复位 | 通过 |
| T06 | 游戏进行中重新开始 | 箭头布局与失误数恢复 | 飞出+失误后重开,布局与初始完全一致,失误恢复 3 | 通过 |
扩展功能用例 T07–T13:提示高亮集合与可飞箭头一致、撤销可恢复、计时只在游戏中累计、随机关卡必有解、自动演示可清空棋盘、零失误记三星并持久化、未解锁关卡点击无效——全部通过。早期逻辑测试 tests/test_levels.py(路径检测、六关可通关交叉验证)也保持通过,无回归。
测试中发现并修复的问题:提示描边初版不醒目(改双层金色脉动)、结算按钮压住弹窗边框(重新排布)、选关锁定图标与文字重叠(调整卡片布局),均已复测通过。
六、PSP 表格
| 任务 | 预估耗时(小时) | 实际耗时(小时) | 差异(小时) |
|---|---|---|---|
| 需求分析与游戏设计 | 1 | 1 | 0 |
| Python 与图形库学习 | 1 | 0.1 | 0.9 |
| 游戏界面实现 | 0.5 | 0.5 | 0 |
| 路径与碰撞逻辑实现 | 0.5 | 0.5 | 0 |
| 关卡设计 | 0.2 | 0.1 | 0.1 |
| AIGC 辅助开发 | 3 | 2.5 | 0.5 |
| 测试与修改 | 3 | 1 | 2 |
| README 与博客撰写 | 3 | 1 | 2 |
| 合计 | *12.2 | 6.7 | 5.5 |
七、心得体会
这次作业最大的体会是:AIGC 很适合“把骨架快速搭起来”,但“对不对、好不好玩”还得靠自己把关。路径检测初版能跑,可一旦没把“飞行中的箭头”从遮挡判定里排除,整个游戏规则就错了;随机关卡算法虽然巧妙,但如果不做求解器二次验证,很可能把无解死局交给玩家。这些坑 AI 不会主动告诉你,都是在写测试、实际试玩时才暴露出来的。
另一方面,小步提交 + 自动化测试帮了大忙:每加一个功能就跑一遍 T01–T13,哪里改坏了一眼就能定位;撤销、星级存档这些容易出回归的功能,也是靠无头测试把边界场景都覆盖到。再就是学会了重视版权意识——背景用文生图、音效用程序合成、精灵和界面全用代码画,全程没有抄任何商业素材,交作业心里踏实。
最后,“灵灵”这个原创小精灵和粒子特效虽然不影响玩法,但让游戏一下子有了灵气。以后再做类似项目,我会更早地把测试用例和存档结构设计好,少做返工。
仓库地址:https://github.com/Xun1342/Software-Engineering-Assignment-2-elemental-arrows





浙公网安备 33010602011771号