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

二、项目介绍
2.1 游戏规则
《一箭又一箭》是一款基于网格棋盘的简单箭头消除游戏。
游戏开始后,棋盘上会随机/预设分布多个不同方向的箭头。玩家通过鼠标点击箭头,程序检查该箭头沿自身方向前进时是否存在其他箭头。
游戏规则如下:
- 每个箭头占据棋盘中的一个格子;
- 箭头具有上、下、左、右四个方向;
- 玩家使用鼠标点击箭头;
- 程序检查箭头前进方向上的所有格子;
- 如果箭头与棋盘边界之间没有其他箭头,则该箭头可以飞出棋盘;
- 箭头飞出以后从棋盘中消失;
- 如果箭头前方存在其他箭头,则无法飞出;
- 被阻挡的箭头会产生碰撞动画,同时消耗一次失误机会;
- 失误次数耗尽以后,本关失败;
- 如果棋盘上的箭头全部消失,则本关通关;
- 本项目共设计 3 个基础关卡。
2.2 界面设计
本项目采用 Tkinter 实现图形界面,整体采用深色背景,使棋盘和箭头更加突出。
界面主要分为三个部分:
开始界面
负责介绍游戏名称和基本规则,并提供开始游戏按钮。
游戏界面
游戏界面是整个项目的核心,采用:
顶部信息区 + 中间棋盘区
的布局方式。
顶部实时显示游戏状态,棋盘负责显示和操作箭头。
结果界面
根据游戏状态显示:
- 通关;
- 最终完成;
- 失败。
不同状态使用不同的文字颜色进行区分,使玩家可以快速判断当前游戏结果。
三、主要功能与特色
3.1 鼠标点击操作
通过 Tkinter Canvas 的鼠标事件实现箭头选择。
程序首先根据鼠标点击位置计算棋盘中的行号和列号,然后判断该位置是否存在箭头。
核心计算方式为:
c = int((event.x - BOARD_X) // CELL)
r = int((event.y - BOARD_Y) // CELL)
这样就可以把鼠标的像素坐标转换成棋盘的网格坐标。
3.2 四种箭头方向
程序使用字符表示箭头方向:
SYMBOLS = {
"U": "↑",
"D": "↓",
"L": "←",
"R": "→"
}
其中:
U表示上;D表示下;L表示左;R表示右。
同时使用方向增量描述箭头每一步移动的方向:
DIRS = {
"U": (-1, 0),
"D": (1, 0),
"L": (0, -1),
"R": (0, 1)
}
例如:
U → 行号 -1,列号不变
D → 行号 +1,列号不变
L → 行号不变,列号 -1
R → 行号不变,列号 +1
3.3 箭头飞出动画
如果检测到箭头前方没有阻挡,程序会从棋盘数据中删除箭头,然后通过 after() 函数逐帧绘制箭头向棋盘外移动。
核心思想是:
self.root.after(35, lambda: frame(i + 1))
通过连续执行多个动画帧,使箭头产生“飞出棋盘”的视觉效果,而不是直接消失。
3.4 碰撞动画
如果箭头前方存在其他箭头,则不会删除该箭头。
程序会:
- 将箭头颜色变成红色;
- 向前轻微移动;
- 向后弹回;
- 再次轻微晃动;
- 恢复正常颜色;
- 失误次数减 1。
这样可以让玩家明显知道自己的点击没有成功。
四、实现思路
4.1 棋盘表示
游戏棋盘采用 6×6 网格。
可以把棋盘看作二维坐标:
(0,0) (0,1) (0,2) ... (0,5)
(1,0) (1,1) (1,2) ... (1,5)
...
(5,0) (5,1) (5,2) ... (5,5)
箭头使用字典保存:
self.arrows = {
(行, 列): {"dir": 方向}
}
例如:
(2, 3): {"dir": "U"}
表示第 2 行、第 3 列存在一个向上的箭头。
这种表示方式的优点是可以通过:
if (r, c) in self.arrows:
快速判断某个棋盘位置是否有箭头。
4.2 关卡表示
每个关卡使用列表保存。
例如:
[
(0, 0, "R"),
(0, 3, "U"),
(1, 1, "D"),
(1, 5, "L")
]
每个元素由三个数据组成:
(行号, 列号, 方向)
程序预设了 3 个关卡:
LEVELS = [
[...],
[...],
[...]
]
通过:
self.level_index
记录当前关卡编号。
通关后:
self.level_index += 1
即可进入下一关。
五、重点:路径检测方法
路径检测是本游戏最核心的程序逻辑。
由于基础版规定:
箭头只能在同一行或同一列向棋盘边界飞出。
因此不需要进行复杂的碰撞检测,只需要沿箭头方向逐格检查。
5.1 检测思路
假设当前箭头位于:
第 r 行,第 c 列
首先获取箭头方向:
direction = self.arrows[pos]["dir"]
再获得方向增量:
dr, dc = DIRS[direction]
然后从箭头相邻的下一格开始检查:
rr, cc = r + dr, c + dc
只要坐标还没有超出棋盘,就继续检查:
while 0 <= rr < ROWS and 0 <= cc < COLS:
如果发现:
(rr, cc) in self.arrows
就说明前方存在其他箭头,因此该箭头被阻挡。
5.2 核心代码
def path_is_clear(self, pos):
r, c = pos
direction = self.arrows[pos]["dir"]
dr, dc = DIRS[direction]
rr, cc = r + dr, c + dc
while 0 <= rr < ROWS and 0 <= cc < COLS:
if (rr, cc) in self.arrows:
return False
rr += dr
cc += dc
return True
该函数最终只返回两个结果:
True → 前方没有阻挡,可以飞出
False → 前方存在箭头,发生碰撞
5.3 示例
例如棋盘:
→ · · ↑ ·
第一个箭头向右。
程序从第一个箭头右侧第一格开始:
· → · ↑ ·
继续检查:
· · → ↑ ·
最终发现右侧存在箭头,因此:
path_is_clear(...) == False
所以第一个箭头不能消除。
反过来,如果:
↑ · · · →
最后一个箭头向右,它右边已经没有棋盘格。
程序检查到棋盘边界后没有发现其他箭头,因此:
path_is_clear(...) == True
箭头可以飞出。
5.4 算法特点
本项目采用的路径检测算法非常直接,符合基础版游戏的要求。
对于一个箭头,最多检查它到棋盘边界之间的若干个格子。
由于棋盘只有 6×6,因此每次点击的检测成本非常低。
其时间复杂度可以表示为:
O(n)
其中 n 为当前箭头到棋盘边界之间需要检查的格子数量。
六、AIGC 使用过程
本项目在开发过程中使用 AI 作为辅助开发工具。AI 主要用于需求分析、代码设计、逻辑实现、测试思路整理以及项目报告整理。
| 子任务 | 借助何种 AIGC 技术 | AI 实现或提供了什么 | 效果如何 | 人工修改 |
|---|---|---|---|---|
| 游戏界面实现 | ChatGPT | 生成 Tkinter 界面框架、Canvas 棋盘、信息栏和按钮代码 | 基本能够正常显示,但初始布局需要调整 | 手工调整棋盘大小、控件间距、字体和颜色,使界面更加合理 |
| 路径检测 | ChatGPT | 生成四个方向的路径检测代码,并使用方向向量统一处理上、下、左、右 | 基本实现路径判断,部分边界条件需要进一步检查 | 检查并修改边界条件,确保箭头到达棋盘边缘时能够正确判断 |
| 碰撞动画 | ChatGPT | 提供箭头被阻挡时的移动、晃动、变色反馈思路及代码 | 基本能够实现碰撞反馈,但动画速度和幅度不够合适 | 调整动画帧数、移动距离和 after() 延迟时间,使反馈更加明显 |
| 飞出动画 | ChatGPT | 提供箭头逐帧向棋盘外移动的动画实现思路 | 可以实现箭头飞出效果 | 根据实际运行效果调整动画步数和移动速度 |
| 测试与调试 | ChatGPT | 根据游戏功能提出测试项目,包括四方向检测、失误次数、通关、失败和重新开始等 | 能够帮助发现测试遗漏 | 实际运行程序进行人工测试,并根据运行结果修改代码 |
在使用 AI 生成代码后,我对代码进行了理解、检查和调整,并根据实际游戏需求进行了修改。
七、测试结果
项目完成后,对游戏的主要功能进行了测试。
| 测试编号 | 测试项目 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|---|
| T01 | 启动程序 | 正常显示开始界面 | 正常显示开始界面 | 通过 |
| T02 | 点击开始游戏 | 进入第 1 关 | 正常进入第 1 关 | 通过 |
| T03 | 点击向上箭头 | 正确检测上方路径 | 正确检测 | 通过 |
| T04 | 点击向下箭头 | 正确检测下方路径 | 正确检测 | 通过 |
| T05 | 点击向左箭头 | 正确检测左侧路径 | 正确检测 | 通过 |
| T06 | 点击向右箭头 | 正确检测右侧路径 | 正确检测 | 通过 |
| T07 | 无阻挡箭头 | 箭头飞出并消失 | 箭头执行飞出动画并消失 | 通过 |
| T08 | 有阻挡箭头 | 箭头不能消失 | 箭头红色晃动并保留 | 通过 |
| T09 | 点击被阻挡箭头 | 失误次数 -1 | 失误次数正确减少 | 通过 |
| T10 | 剩余失误为 0 | 显示失败界面 | 正常进入失败界面 | 通过 |
| T11 | 清空棋盘 | 显示通关结果 | 正常显示通关界面 | 通过 |
| T12 | 点击下一关 | 进入下一关 | 正常进入下一关 | 通过 |
| T13 | 重新开始 | 当前关恢复初始状态 | 箭头和失误次数恢复 | 通过 |
| T14 | 完成全部 3 关 | 显示最终完成结果 | 正常完成 | 通过 |
| T15 | 点击棋盘空白位置 | 不产生错误操作 | 无异常 | 通过 |
| T16 | 动画期间重复点击 | 不应造成数据错误 | 程序限制动画期间操作 | 通过 |
八、PSP 项目计划与实际耗时
PSP(Personal Software Process)用于记录软件开发过程中不同阶段的时间投入,从而比较计划与实际情况。
本项目的时间记录如下:
| 任务 | 预估耗时(小时) | 实际耗时(小时) | 差异(小时) |
|---|---|---|---|
| 需求分析与游戏设计 | 1.0 | 1.0 | 0 |
| Python 与图形库学习 | 1.5 | 1.5 | 0 |
| 游戏界面实现 | 2.0 | 2.5 | +0.5 |
| 路径与碰撞逻辑实现 | 2.0 | 2.0 | 0 |
| 关卡设计 | 1.0 | 1.0 | 0 |
| AIGC 辅助开发 | 2.0 | 2.0 | 0 |
| 测试与修改 | 1.5 | 2.0 | +0.5 |
| README 与博客撰写 | 1.0 | 1.0 | 0 |
| 合计 | 12.0 | 13.0 | +1.0 |
PSP 分析
从 PSP 数据可以看出,本项目实际耗时比预计耗时增加约 1 小时。
主要增加的时间集中在:
游戏界面实现和测试与修改两个阶段。
界面实现阶段需要处理:
- Tkinter 控件布局;
- Canvas 棋盘绘制;
- 箭头显示;
- 信息栏;
- 按钮;
- 不同状态界面。
测试阶段则需要反复检查:
- 四种方向是否都能够正确判断;
- 箭头消失后数据是否正确更新;
- 失误次数是否正确减少;
- 通关与失败状态是否能够正确切换;
- 重新开始是否真正恢复初始状态。
因此实际开发时间略高于最初估计。
九、项目特色
虽然本项目只实现基础玩法,但仍具有以下特点。
9.1 规则简单
玩家只需要判断箭头前方有没有其他箭头,不需要学习复杂操作。
9.2 四方向统一处理
通过方向向量统一实现上下左右四种情况,减少了重复代码。
9.3 数据与显示分离
程序使用 self.arrows 保存实际游戏数据,再通过绘制函数将数据转换成界面显示。
这样可以降低游戏逻辑与界面的耦合程度。
9.4 有基本动画效果
项目没有直接让箭头瞬间消失,而是增加了:
- 飞出动画;
- 碰撞晃动;
- 红色反馈;
- 状态文字。
提高了游戏的操作反馈效果。
9.5 不依赖第三方库
整个游戏使用 Python 自带的 Tkinter 完成,方便在普通 Python 环境下运行和提交。
十、心得体会
通过本次《一箭又一箭》小游戏的开发,我对 Python 图形界面编程、二维网格数据结构以及简单游戏逻辑有了更加直观的认识。
首先,AIGC 对项目开发提供了比较明显的帮助。在需求分析阶段,AI 可以帮助我快速把比较长的作业要求拆分成具体功能。例如将整个游戏划分成开始界面、游戏界面、结果界面、箭头数据、路径检测、动画和关卡管理等模块,使开发思路更加清晰。
其次,在核心算法实现方面,AI 提供的方向向量思路比较实用。原本如果分别处理上、下、左、右四个方向,需要编写多套类似的判断代码,而使用:
DIRS = {
"U": (-1, 0),
"D": (1, 0),
"L": (0, -1),
"R": (0, 1)
}
以后,只需要根据方向获得行列变化量,就可以使用同一个路径检测函数处理四种情况。这让我认识到,在程序设计中,将相似的问题抽象成统一的数据结构和算法,可以明显减少重复代码。
不过,AIGC 生成的代码也不能完全不加检查地使用。实际开发过程中,需要自己理解代码逻辑,并检查变量、界面状态、动画执行顺序等问题。如果只复制 AI 给出的代码而不了解其原理,遇到运行问题时就很难定位原因。
本次项目也让我认识到,游戏程序虽然规则简单,但“能运行”和“体验完整”之间还有一定差距。例如一个箭头成功消失只是实现了基本功能,而加入飞出动画以后,玩家可以更加直观地理解“箭头离开棋盘”的过程。同样,被阻挡时增加红色晃动和文字提示,也比单纯不发生变化更加容易让玩家理解。
通过这次项目,我主要获得了以下几方面的收获:
- 学会了使用 Tkinter 创建基本图形化界面;
- 理解了 Canvas 在简单游戏开发中的作用;
- 掌握了二维网格坐标和方向向量的基本使用方法;
- 理解了如何设计简单的路径检测算法;
- 学会了使用状态变量管理开始、游戏、通关和失败等不同界面;
- 对游戏动画的基本实现方式有了初步认识;
- 认识到 AIGC 更适合作为程序开发中的辅助工具,而不是完全替代自己的思考和测试。
总体来说,本次项目规模不大,但完整经历了从需求分析 → 程序设计 → 编码实现 → 测试修改 → 项目总结的过程,对我理解 Python 小游戏开发和软件开发流程都有一定帮助。
十一、项目总结
本项目完成了题目要求的基础“一箭又一箭”玩法,实现了:
- 图形化开始界面;
- 6×6 游戏棋盘;
- 上、下、左、右四种方向箭头;
- 鼠标点击操作;
- 箭头路径检测;
- 无阻挡飞出动画;
- 有阻挡碰撞反馈;
- 失误次数系统;
- 3 个可正常通关的关卡;
- 通关界面;
- 失败界面;
- 下一关功能;
- 重新开始功能。
项目最终形成了一个结构较为完整、规则清晰、可以独立运行的 Python 图形化小游戏。

浙公网安备 33010602011771号