2026秋软件工程第二次个人作业—— 一箭又一箭
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering |
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering/homework/16717 |
| 这个作业的目标 | 使用 Python 和 AIGC 完成“一箭又一箭”小游戏 |
| 学号 | 102401403 |
| GitHub 仓库 | https://github.com/pyyy123/arrow-game |
一、项目展示
1. 开始界面

2. 游戏过程

演示 GIF:demo.gif
3. 失败界面

4. 通关界面

二、项目介绍
游戏规则
棋盘上分布着朝向上、下、左、右四个方向的箭头。玩家点击某个箭头后:
- 如果箭头前进方向上没有其他箭头阻挡,它会飞出棋盘并被消除;
- 如果路径上有其他箭头,它不会消失,并会晃动并变色作为碰撞反馈;
- 每次点击被阻挡的箭头消耗一次失误机会;
- 清空本关全部箭头即可通关,失误耗尽则失败。
界面设计
游戏包含三种界面状态:
- 开始界面:标题、玩法说明、开始按钮;
- 游戏界面:顶部 HUD(关卡、剩余箭头、剩余失误)、棋盘、重新开始按钮;
- 通关/失败界面:半透明遮罩 + 结果文字 + 操作按钮。
主题为“梦境”,背景为淡紫色,配有云朵和梦境精灵装饰,箭头带有柔光效果。
主要功能
- 四种方向箭头;
- 鼠标点击交互;
- 路径阻挡检测;
- 飞出动画与碰撞晃动反馈;
- 失误次数机制;
- 3 个可通关关卡;
- 通关、失败、重新开始、下一关。
三、实现思路
1. 箭头、方向的表示
用一个二维列表 grid 表示棋盘,每个格子的值是整数:
0表示空;1/2/3/4分别表示上、下、左、右。
方向到行列增量的映射:
DIR_VEC = {
UP: (-1, 0),
DOWN: ( 1, 0),
LEFT: ( 0, -1),
RIGHT: ( 0, 1),
}
这样在路径检测时只需要按方向累加即可,不需要为四个方向分别写四套循环。
2. 关卡表示
每个关卡是一个字典,包含 name、mistakes 和 grid:
"level_1": {
"name": "第1关:5x5 简单",
"mistakes": 5,
"grid": [
[0, 0, 1, 0, 0],
...
],
}
LEVEL_ORDER 控制关卡顺序,方便后续扩展。
3. 路径检测(重点)
核心函数 is_blocked(row, col):
def is_blocked(self, row, col):
direction = self.grid[row][col]
dr, dc = DIR_VEC[direction]
r, c = row + dr, col + dc
while 0 <= r < self.rows and 0 <= c < self.cols:
if self.grid[r][c] != 0:
return True
r += dr
c += dc
return False
从当前格子出发,沿方向逐步前进,只要在棋盘内且遇到非空格子就返回 True;走出棋盘则返回 False。这样同时处理了“被挡住”和“边缘飞出”两种情况,不需要额外判断边界。
4. 飞出动画与碰撞反馈
飞出:点击无阻挡箭头后,把 (row, col) 加入 animating_cells,并创建 FlyAnimation。动画期间 grid 仍保留该箭头,动画结束后再置 0。这样 count_arrows() 在动画期间不会误判通关。
碰撞:点击被阻挡箭头时,把 (row, col) 加入 bad_effects,绘制时根据剩余时间做水平晃动并变色。
四、AIGC 使用过程
开发过程中我主要使用 ChatGPT 和 DeepSeek 辅助开发,下面记录 3 次有代表性的过程。
记录 1:飞出动画优化(ChatGPT)
| 子任务 | 借助何种 AIGC 技术 | AI 实现或提供了什么 | 效果如何 | 人工修改 |
|---|---|---|---|---|
| 飞出动画 | ChatGPT | 分析缓动与 alpha 曲线,给出优化建议 | 指出 alpha 线性衰减过早、距离与 alpha 缓动不一致 | 未采纳 alpha 曲线建议,保留线性 alpha,仅参考了写法建议 |
我向 ChatGPT 展示了 FlyAnimation.draw 的代码,并问了三个问题:
- 飞出动画能否更自然?
- 渐隐 alpha 会不会在飞出距离长时提前变透明?
- 有没有更简洁或更流畅的写法?
ChatGPT 指出:距离用了 ease-out cubic,但 alpha 是线性的,视觉上会出现“先冲后飘”的割裂感;而且 alpha = 255 * (1 - p) 在 p=0.5 时已经降到 128,对 560px 的飞行距离来说太早淡出。
它建议:
- 距离用 ease-out quad
1 - (1-p)**2; - alpha 也加缓动,例如
alpha = 255 * (1 - p**2)或alpha = 255 * clamp((1-p)/0.4, 0, 1); - 可选加缩放增强纵深;
- 用
convert_alpha()、Vector2位移、rect.move等优化写法。
我评估后没有采纳 alpha 曲线的建议,因为本游戏的飞出动画时长只有 0.28 秒,线性 alpha 在实际观感中已经足够自然,换成曲线反而会让前段过亮、后段过暗。我最终保留了原有的 alpha = int(255 * (1 - progress)) 写法,没有改动这段代码。


记录 2:路径检测边界分析与飞出逻辑(ChatGPT)
| 子任务 | 借助何种 AIGC 技术 | AI 实现或提供了什么 | 效果如何 | 人工修改 |
|---|---|---|---|---|
| 路径检测 | ChatGPT | 补充边界测试用例,分析飞出期间 grid 未清空的问题 | 指出动画期间其他箭头仍会把它算作阻挡物 | 评估后保留原方案,并加注释说明 |
我让 ChatGPT 为 is_blocked 补充边界测试用例,包括:
- (0,0) 位置朝上(越界判断);
- 两个箭头背对背,互不阻挡;
- 空 grid 上的行为;
- 同一行/列多个箭头。
ChatGPT 给出了详细分析:
- 边界安全:
is_blocked不会数组越界,因为r一旦越界,while 条件立刻不成立; - 不会把自己算作阻挡:检测起点是
row + dr, col + dc,不是(row, col); - 关键发现:代码无法区分「静止箭头」和「正在飞出的箭头」。如果飞出动画开始后
grid里还保留着 1–4,其他箭头会把它算作阻挡物。
它建议:如果游戏规则是“箭头一旦开始飞出就不再阻挡其他箭头”,应该在启动飞行动画时立刻把原格清空:
self.grid[row][col] = 0
我评估后认为,本游戏的设计是“飞出动画期间箭头仍占位”——这样视觉上箭头还没飞出棋盘,其他箭头就仍应把它当阻挡物。如果按 AI 建议立刻清空 grid,会导致动画期间路径检测结果和视觉不一致。
因此我保留了自己原有的方案:用animating_cells标记正在飞出的格子,动画结束才在update里把 grid置 0。这段逻辑最终没有改动。



记录 3:README 模板与撰写(DeepSeek)
| 子任务 | 借助何种 AIGC 技术 | AI 实现或提供了什么 | 效果如何 | 人工修改 |
|---|---|---|---|---|
| README 撰写 | DeepSeek | 生成完整的 README 结构模板 | 框架清晰,但内容偏通用,与项目实际不符 | 逐节替换为实际内容,修正 6 处不符 |
我向 DeepSeek 提问:“帮我写一份 Pygame 项目的 README 模板,项目是一个点击式箭头解谜小游戏,名叫‘一箭又一箭’。”DeepSeek 给出了一份结构清晰的 Markdown 模板,包含项目名称、游戏简介、开发环境、安装运行、操作说明、截图、项目结构、关卡说明、素材来源等章节。
模板的框架是可用的,但内容偏通用,我逐节对照实际项目进行了修改:
- 游戏简介:DeepSeek 写的是“改变箭头方向或触发连锁反应”,与我的实际玩法(点击后路径无阻挡则飞出)不符,我改写为真实规则;
- 开发环境:DeepSeek 写的是
Python 3.8+、Pygame 2.1+,我按实际写了Python 3.10+、pygame; - 安装运行:DeepSeek 写的是
git clone https://github.com/yourname/one-arrow-after-another.git,我替换为实际仓库地址,运行命令改成python main_fixed.py; - 操作说明:DeepSeek 额外写了
ESC退出、R重新开始,但我的项目只支持鼠标点击,没有键盘快捷键,我删掉了这两条; - 项目结构:DeepSeek 假设有
scenes/、assets/、levels/level1.py等目录,与实际不符,我按实际文件列表重写; - 关卡说明:DeepSeek 写的是“连锁反应”“终极考验”等玩法,与实际关卡机制不符,我改为按尺寸和失误次数说明;
- 素材来源:DeepSeek 写了
Kenney、OpenGameArt、Freesound、Google Fonts,但我的项目所有图形均由 Pygame 绘制,未使用外部素材,我改为真实来源。
最终 README 在 GitHub 上显示正常,助教可按说明直接运行。


五、测试结果
5.1 路径检测逻辑测试(tests/test_logic.py)
| 编号 | 测试内容 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|---|
| T01 | 前方无阻挡 | is_blocked 返回 False |
与预期一致 | ✅ |
| T02 | 前方有阻挡 | is_blocked 返回 True |
与预期一致 | ✅ |
| T03 | 边缘箭头朝外 | 正常飞出,不越界 | 与预期一致 | ✅ |
| T04 | 四个方向检测 | 全部返回 False | 与预期一致 | ✅ |
| T05 | 左边缘朝左 | 不越界 | 与预期一致 | ✅ |
| T06 | 背对背箭头 | 互不阻挡 | 与预期一致 | ✅ |
| T07 | 空格子 | 不可点 | 与预期一致 | ✅ |
| T08 | 同一行多箭头 | 最近阻挡生效 | 与预期一致 | ✅ |
| T09 | 顶部朝上 | 不越界 | 与预期一致 | ✅ |
5.2 游戏功能手工测试
| 编号 | 测试内容 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|---|
| T01 | 点击前方无阻挡的箭头 | 箭头飞出棋盘并消失 | 与预期一致 | ✅ |
| T02 | 点击前方有阻挡的箭头 | 箭头不消失,失误次数减 1 | 与预期一致 | ✅ |
| T03 | 点击位于边缘且朝向棋盘外的箭头 | 箭头正常消失,不发生越界错误 | 与预期一致 | ✅ |
| T04 | 消除本关全部箭头 | 显示通关并进入下一关 | 与预期一致 | ✅ |
| T05 | 失误次数耗尽 | 显示失败并允许重新开始 | 与预期一致 | ✅ |
| T06 | 游戏进行中重新开始 | 箭头布局和失误次数恢复 | 与预期一致 | ✅ |
5.3 关卡可通关性验证
运行 python verify_levels.py,三关均输出“有解”,分别需要 8、13、19 步。


六、PSP 表格
| 任务 | 预估耗时(小时) | 实际耗时(小时) | 差异(小时) |
|---|---|---|---|
| 需求分析与游戏设计 | 1.0 | 1.5 | +0.5 |
| Python 与图形库学习 | 2.0 | 2.5 | +0.5 |
| 游戏界面实现 | 2.0 | 2.0 | 0 |
| 路径与碰撞逻辑实现 | 2.0 | 3.0 | +1.0 |
| 关卡设计 | 1.0 | 1.5 | +0.5 |
| AIGC 辅助开发 | 2.0 | 2.0 | 0 |
| 测试与修改 | 1.5 | 2.0 | +0.5 |
| README 与博客撰写 | 2.0 | 2.5 | +0.5 |
| 合计 | 13.5 | 17.0 | +3.5 |
七、心得体会
这次作业让我第一次比较系统地用 AIGC 工具参与一个小型项目的完整开发。最大的感受是:AI 能大幅提高起手速度,但不能替代自己理解代码。
具体来说:
1.AI 擅长给结构和模板。 比如界面布局、按钮类、状态机这些“套路化”的部分,ChatGPT 给得很快,我只需要按自己的主题调整颜色和文字;README 模板 DeepSeek 也给了很好的框架。
2.AI 在边界条件上容易出错。 路径检测的越界、关卡死锁、动画频率过高,都是 AI 生成后我实际运行才发现的。这让我意识到“能跑”和“跑对”是两回事,必须自己测试。
3.关卡设计不能完全交给 AI。 AI 生成的布局看起来合理,但用 verify_levels.py 一跑就发现死锁。手工调整并试玩是必须的。
4.AIGC 记录要真实。 我在开发过程中确实和 ChatGPT、DeepSeek 来回修改了很多次,博客里记录的这几次是比较有代表性的。
5.AI 给的模板不能照抄。 DeepSeek 的 README 模板看起来齐全,但里面写的素材来源、项目结构、操作说明和我的项目完全不符,必须逐条对照修改。
6.不采纳 AI 的建议也是一种判断。 比如飞出动画的 alpha 曲线、路径检测的 grid 清空方案,我评估后都保留了原写法,因为原来的设计更符合本游戏的实际需求。
总的来说,这次作业让我对“人机协作开发”有了更具体的认识:AI 是加速器,但方向盘和刹车必须自己握。

浙公网安备 33010602011771号