软工第二次作业:用AIGC编写“一箭又一箭”小游戏
简单好玩的游戏:一箭又一箭
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | H2026软件工程与软件工程实践 |
| 这个作业要求在哪里 | 2026 秋软件工程个人作业(第二次) |
| 这个作业的目标 | 使用 Python 和 AIGC 完成“一箭又一箭”小游戏 |
| 学号 | 102402147 |
| GitHub 仓库 | One-Arrow-After-Another |
一、1.项目基本信息
- 项目名称: One-Arrow-After-Another(一箭又一箭)
- 开发语言: Python
- GUI 框架: Pygame
- 开发工具: Visual Studio Code
- 辅助AIGC:DeepSeek
- 代码仓库: GitHub
二、项目目标和基本流程
本次作业要求使用 Python 完成一个“一箭又一箭”类型的箭头解谜小游戏。 游戏的基本玩法是:棋盘中会放置不同方向的箭头,玩家点击箭头后,程序需要判断箭头前进的方向上是否存在其他箭头。如果没有障碍物,箭头就可以飞出棋盘并消失;如果前方存在其他箭头,则本次点击失败,同时消耗一次失误机会。 在完成基本玩法的基础上,游戏还需要实现多个关卡、胜利和失败状态,并使用 AIGC 工具参与实际开发过程。 我的实现目标并不是一开始就加入很多复杂功能,而是先保证游戏最基本的逻辑正确,尤其是四个方向的路径检测。之后再逐步增加动画、界面和关卡等功能。
三、项目展示和项目介绍
1.开始界面

开始界面提供一个游戏进入入口和简洁的游戏介绍
2.演示gif
- 通关演示
![demo]()
*设置了至少3关
![demo2]()
- 碰撞演示:碰撞次数超过3次就失败,重新开始,恢复这局开始状态
![demo3]()
四、项目介绍
(1)游戏规则
-
游戏盘面由网格组成,网格中放置着若干带方向(上、下、左、右)的箭头。
-
玩家通过鼠标左键点击箭头,程序会检测该箭头前进方向到棋盘边界之间是否存在其他箭头阻挡:
-
前方无阻挡:箭头沿着所指方向飞出棋盘并消失。
-
前方有阻挡:箭头无法飞出,会左右晃动并变色提示碰撞,同时消耗一次失误机会。
-
清空当前关卡的全部箭头即可通关并进入下一关;失误次数耗尽则本关失败,可随时点击重新开始本关。
-
游戏内置了 3 个手工设计且验证可通关的关卡,之后的关卡会通过算法自动生成,保证每一个关卡都有解。
(2)界面设计
-
游戏包含三个主要界面,布局简洁,交互直观:
-
开始界面:展示游戏标题、四个方向的彩色箭头图标、玩法说明,以及“开始游戏”按钮。
-
游戏界面:顶部显示当前关卡、剩余箭头数量、剩余失误次数(用圆点表示)以及“重新开始”按钮;中部为带圆角的网格棋盘;底部显示快捷键提示。
-
结算界面:通关时显示“过关!”及“下一关”按钮;失败时显示“失误用尽”及“重新开始”按钮。两者均在游戏画面上叠加半透明遮罩。
(3)主要功能
-
四方向路径检测:基于行与列的几何检测算法,判断箭头与边界之间是否有其他存活箭头。
-
失误惩罚机制:限制失误次数,并在 UI 中实时扣减,失误达到上限触发失败。
-
关卡动态切换:手工预设与自动生成相结合,算法会检测关卡是否可解,避免出现死局。
-
动画视觉反馈:箭头被阻挡时向左右晃动、颜色向红色渐变;飞出时带有平滑的淡出和缩小的飞出轨迹。
-
一键重开:支持按钮或快捷键(R 键)随时把当前关卡恢复到初始状态。
(4)游戏特色
-
纯代码绘制:不依赖任何外部图片或音频资源,所有箭头图形、背景渐变、按钮和动画均由 Pygame 代码实时计算生成,项目体积极小且易于运行。
-
兼容性设计:内置中文字体回退机制,如系统无中文字体则自动切换为英文界面,保证游戏在任何环境下都能正常显示。
-
智能关卡生成:基于贪心算法对随机生成的布局进行可解性校验,无效布局自动丢弃并重试,确保玩家永远有通关路径。
-
AIGC 辅助开发:遵循要求,开发全程借助了 AIGC / Coding Agent 工具辅助完成代码编写、环境配置排错与 Bug 修复。
五、设计思路
本项目基于 Python 与 Pygame 开发,采用面向对象与网格化设计。整体实现思路分为四个核心部分:数据结构定义、路径检测算法、点击交互逻辑、以及关卡可解性校验。
1.数据结构与游戏元素表示
(1)方向的表示
为了简化计算,将上、右、下、左四个方向分别映射为整数 0、1、2、3,并利用字典绑定方向与行列坐标的增量:
UP, RIGHT, DOWN, LEFT = 0, 1, 2, 3
# 方向 -> (行增量, 列增量)
DIRS = {UP: (-1, 0), RIGHT: (0, 1), DOWN: (1, 0), LEFT: (0, -1)}
(2)箭头的表示
用 Arrow 类来管理每一个箭头。核心属性包括:网格坐标 r, c,当前朝向 d,以及动画状态 state(idle静止、shake晃动、fly飞出)。为了支持流畅动画,类中还预留了计时器 t、位置偏移 dx, dy 和缩放值 scale。
(3)关卡的表示
棋盘的所有箭头存放在一个字典 grid 中,键为元组 (r, c) 网格坐标,值为对应的 Arrow 对象。这样可以通过坐标在 O(1) 时间内快速判断某个位置是否被占用。
2.路径检测算法(核心重点)
当玩家点击某个箭头时,需要通过 path_clear 函数判断它能否飞出棋盘。该算法沿箭头方向逐步向边界推进,检查路径上是否存在其他存活箭头:
def path_clear(occupied, r, c, d, rows, cols):
dr, dc = DIRS[d]
nr, nc = r + dr, c + dc
# 逐步向边界推进
while 0 <= nr < rows and 0 <= nc < cols:
if (nr, nc) in occupied: # 发现阻挡
return False
nr += dr
nc += dc
return True # 顺利到达边界,无阻挡
-
occupied 传入的是当前棋盘所有箭头的坐标集合(即 grid 的 keys)。
-
该算法只用了一个简单的 while 循环,时间复杂度仅为 O(rows + cols),性能极高。
-
如果返回 True,则将箭头从 grid 中删除,并放入 flying 列表播放飞出动画;如果返回 False,则触发碰撞反馈并消耗失误次数。
3.点击交互与游戏状态机
点击事件的处理逻辑集中在一个函数中,实现了“检测-消除/惩罚”的闭环:
def click_board(self, pos):
cell = self.cell_at(pos)
if cell is None: return
arrow = self.grid.get(cell)
if arrow is None or arrow.state != "idle": return
if path_clear(self.grid, arrow.r, arrow.c, arrow.d, self.rows, self.cols):
# 路径畅通:飞出棋盘
del self.grid[cell]
arrow.state = "fly"
self.flying.append(arrow)
else:
# 路径被挡:晃动提示 + 记一次失误
arrow.state = "shake"
arrow.t = 0.0
self.mistakes += 1
if self.mistakes >= self.max_mistakes:
self.scene = "lose"
self.overlay_t = 0.0
4.关卡动态生成与可解性校验
为了保证玩家永远有通关路径(避免出现“所有箭头都被互相堵死”的死局),我写了一个 is_solvable 贪心校验算法:
def is_solvable(grid, rows, cols):
remain = dict(grid)
while remain:
for key, d in list(remain.items()):
r, c = key
if path_clear(remain, r, c, d, rows, cols):
del remain[key]
break
else:
return False # 还剩箭头,但都动不了 -> 死局
return True
-
逻辑:反复扫描当前棋盘,只要发现某个箭头路径畅通,就把它移除。因为移除箭头只会让路径更空,所以贪心顺序不影响结论。
-
应用:在程序生成随机关卡时,会反复随机布局并用这个函数校验,直到找到一个能完全清空的棋盘为止,确保每一个关卡都必定可解。
5.动画与视觉反馈
游戏每一帧都会更新箭头的状态:
-
碰撞晃动:当箭头处于 shake 状态时,利用 math.sin(a.t * 62.0) * 9.0 * k 计算左右偏移量 dx,并且让颜色随时间向红色渐变(通过颜色混合函数 blend),给予明显的视觉反馈。
-
飞出动画:当箭头处于 fly 状态时,根据飞行时间 t,计算平滑缓动值 p,然后让箭头沿方向平移 dist * p 并逐渐缩小 scale,实现平滑飞出的效果。
六、AIGC使用过程
1.协作一:核心关卡生成与可解性校验算法设计
-
开发背景:为了让游戏在后续关卡能够随机生成,我最初写了一个随机放置箭头的逻辑。但测试时发现,随机布局经常出现“死局”(所有箭头互相堵死,无法消除),导致玩家无法通关。
-
AI 协作过程:我将“如何保证随机生成的关卡必定有解”的问题抛给 AI。AI 给出了一个非常巧妙的贪心算法思路:反复扫描当前棋盘,只要找到某个箭头的前方没有阻挡,就将其移除;移除后继续扫描,直到清空或卡死。如果卡死,说明是死局。AI 帮我写出了核心的 is_solvable() 函数。
-
最终效果:在关卡生成时调用该函数进行校验,如果校验失败则重新生成。这成功解决了随机死局的问题,确保了游戏永远有通关路径。
2.协作二:动画反馈与视觉交互实现
-
开发背景:基础功能完成后,游戏手感比较生硬。箭头消除时是瞬间消失的,被阻挡时也没有任何提示,玩家很难判断操作是否成功。
-
AI 协作过程:我向 AI 提出了“希望增加箭头飞出动画和碰撞反馈”的需求。AI 建议将箭头状态分为 idle(静止)、shake(晃动)、fly(飞出)三种,并给出了具体的数学公式:飞出动画使用 smoothstep 缓动函数 p * p * (3 - 2 * p) 来实现平滑移动和缩小;碰撞晃动则使用正弦函数 math.sin(t * 62.0) * 9.0 * k 来产生衰减的左右偏移。
-
最终效果:配合颜色混合函数 blend,箭头被阻挡时会左右晃动并逐渐变红,飞出时会平滑缩小飞出。这套动画反馈大幅提升了游戏的操作手感和视觉表现。
3.协作三:Anaconda 环境静默闪退排查与修复
-
开发背景:游戏代码编写完成后,在本地运行遇到了最棘手的工程问题——使用 Anaconda 的 Python 3.13 环境运行 Pygame,终端能打印出欢迎信息,但游戏窗口无法弹出,也没有任何报错,直接静默闪退。
-
AI 协作过程:我将终端输出发给 AI 分析。AI 判断这是 Anaconda 自带的底层 SDL 库与 pip 安装的 Pygame 2.6.1 存在兼容性冲突。AI 指导我使用 python -m venv venv 创建一个纯净的虚拟环境来隔离冲突。在安装依赖时又遭遇了清华镜像源 403 错误,AI 立即建议换用阿里云镜像源并加上 --trusted-host 参数。
-
最终效果:在纯净的虚拟环境下,游戏窗口终于成功弹出,环境问题被彻底解决,为后续的测试和截图扫清了障碍。
七、测试结果
| 编号 | 测试内容 | 预期结果 |
|---|---|---|
| T01 | 点击前方无阻挡的箭头 | 箭头沿当前方向平滑飞出棋盘,随后被消除,剩余箭头数量减 1。 |
| T02 | 点击前方有阻挡的箭头 | 箭头无法飞出,产生左右晃动并逐渐变红的碰撞动画,同时扣除 1 次失误机会(失误圆点减少)。 |
| T03 | 点击位于边缘且朝向棋盘外的箭头 | 箭头前方即为边界,无任何阻挡,箭头直接飞出棋盘并被消除。 |
| T04 | 消除本关全部箭头 | 触发胜利判定,弹出“过关!”半透明遮罩,并显示“下一关”按钮。 |
| T05 | 失误次数耗尽 | 触发失败判定,弹出“失误用尽”半透明遮罩,并显示“重新开始”按钮。 |
| T06 | 游戏进行中重新开始 | 点击“重新开始”按钮或按 R 键,当前关卡棋盘恢复到初始状态,失误次数清零,所有动画状态重置。 |
| 测试结果全部通过 |
八、PSP 表格
| 任务 | 预估耗时(小时) | 实际耗时(小时) | 差异(小时) |
|---|---|---|---|
| 需求分析与游戏设计 | 1.5 | 2.0 | +0.5 |
| Python 与图形库学习 | 2.5 | 4.0 | +1.5 |
| 游戏界面实现 | 3.0 | 4.5 | +1.5 |
| 路径与碰撞逻辑实现 | 2.5 | 3.0 | +0.5 |
| 关卡设计 | 1.5 | 2.0 | +0.5 |
| AIGC 辅助开发 | 3.0 | 4.5 | +1.5 |
| 测试与修改 | 2.0 | 3.5 | +1.5 |
| README 与博客撰写 | 2.0 | 4.0 | +2.0 |
| 合计 | 18.0 | 27.5 | +9.5 |
九、心得体会
本次“一箭又一箭”小游戏的开发,是我第一次在软件工程作业中如此深度地依赖 AIGC 工具。从最初的环境配置,到核心算法设计,再到最终的项目交付,AI 既是我的“良师益友”,也像一面镜子,让我看清了自己的不足。以下是我对此次 AI 协作开发的总结与感悟。
1.AI 带来的帮助:效率的飞跃与思路的启发
快速构建代码骨架:作为 Python 和 Pygame 的初学者,如果从零开始查阅文档,搭建图形界面和箭头绘制将耗费大量时间。AI 迅速给出了 Arrow 类、方向映射字典以及平滑动画的数学公式(如 smoothstep 缓动函数),让我能迅速进入核心逻辑的编写。
攻克算法难题:在关卡生成时,我一度对“如何保证随机生成的关卡必定可解”毫无头绪。AI 给出的贪心算法思路(is_solvable 函数)非常惊艳,它通过反复扫描并移除路径畅通的箭头来判断死局。这种思路让我深刻体会到了算法设计在工程实践中的优雅。
全天候的排错助手:从 Anaconda 环境与 Pygame 的冲突导致静默闪退,到 pip 安装遭遇 403 错误,再到 GitHub 的 404 页面和 Markdown 排版错乱,AI 都耐心地根据报错截图给出了行之有效的解决方案。
2.出现的问题:AI 的局限与“盲区”
-
环境建议的滞后性:在早期解决 Pygame 闪退时,AI 最初没有直接建议我创建虚拟环境,而是尝试通过更换镜像源和重装库来解决,直到问题反复出现才定位到 Anaconda 底层 SDL 库冲突。这说明 AI 对本地真实环境的理解存在盲区,需要我提供极其精确的上下文。
-
格式与内容混淆:在撰写 README 时,AI 常常给出带有外层 Markdown 代码块标记的文本,我由于不熟悉语法直接全量复制,导致页面排版崩塌。这让我意识到,AI 生成的内容往往需要自己具备基础甄别能力,不能盲目“拿来主义”。
-
代码理解成本:AI 生成的 300 多行代码里包含了很多精细的动画逻辑和状态机设计,如果不逐行读懂,一旦出现 bug 我根本无法调试。这倒逼我必须去弄懂每一行代码的含义。
3.自己的收获:从“复制粘贴”到“自主掌控”
-
工程能力的提升:我彻底掌握了使用 python -m venv 隔离项目环境的方法,明白了镜像源(清华源、阿里云源)切换的必要性,学会了 Git 的基本操作和 GitHub 的使用规范(如多次 Commit、Markdown 语法、图片上传)。
-
调试与排错思维:面对闪退、报错和 404,我不再慌乱,而是学会了从终端输出中寻找线索,学会在 AI 无法解决时主动切换思路(例如最后放弃用 Python 转 GIF 准备换在线工具)。
-
辩证看待 AI:AI 是极其强大的效率工具,但它无法替代人的思考。它给了我“鱼”(代码),但我需要自己去掌握“渔”(逻辑)。只有理解了算法的原理,我才能在 AI 生成的代码基础上进行修改、优化和排错。
总结:这次作业不仅让我完成了一个可以正常通关的小游戏,更让我体验到了 AI 时代软件开发的真实工作流——人类负责定义问题、拆分任务和审核结果,AI 负责提供方案、生成草稿和加速迭代。 这种协作模式将是我未来学习和职业生涯中不可或缺的能力。




浙公网安备 33010602011771号