AIGC标识 2026秋软件工程个人作业(第二次)——一箭又一箭(完善版)

作业信息

项目 内容
这个作业属于哪个课程 2026 秋软件工程与软件工程实践
这个作业要求在哪里 2026 秋软件工程个人作业(第二次)
这个作业的目标 使用 Python 和 AIGC 完成“一箭又一箭”小游戏,并完成测试、文档与发布
学号 032401339
GitHub 仓库 2074599796-cpu/arrow-escape-game

前言

刚看到作业时,我先想的是把基本玩法做出来。做到能正常运行以后,我又觉得普通箭头有点单调,正好我比较喜欢《剑来》,于是就把箭头换成了飞剑,把关卡叫作“境界”,生命值也改成了“剑心”,最后做成了现在这个《一箭又一箭·飞剑问道》。

这个游戏用 Python 自带的 Tkinter 完成,不需要再安装 Pygame。我前后断断续续做了五天,顺序大概是:先把路径判断、失败和过关做对,再慢慢加飞出动画、碰撞、五个关卡、提示、撤销、星级和存档。AI 确实帮我写了不少代码,但第一版通常不会刚好符合我的想法,后面很多时间其实都花在看效果、提修改和重新测试上。


一、项目展示

1. 开始界面

开始界面用了水墨风的人物海报,下面有“执剑入局”和“境界选择”两个按钮。原图是竖版的,直接放进窗口里会很别扭,所以我让 AI 把人物保留在中间,再把两边暗化延展,底部也压暗一点,避免文字和按钮看不清。

开始界面

图片来源:小红书(原链接未保存),仅用于课程作业展示。飞剑透明素材由 AIGC 根据本人提供的参考图重新制作,项目没有直接使用商业游戏的代码、关卡或音效。

我最初使用的飞剑又短又胖,缩小后很难看出剑身细节,所以又在网上寻找了自己喜欢的飞剑造型作为参考,并交给 AI 处理成透明素材。之后我继续要求去掉飞剑周围圆形的一圈,保留细长剑身和清楚的剑尖方向,才形成现在的效果。

2. 境界选择

游戏一共有五关。开始时只能玩第一关,通关以后才会解锁下一关;已经解锁的关卡可以重新挑战,拿到的最好星级会保存在电脑里,下次打开游戏还能看到。

关卡选择与星级

3. 游戏过程

游戏页面上面会显示当前是第几关、还剩多少飞剑、剑心、积分和时间。右上角有“提示”“撤销”和“重整”,也可以直接按 HCtrl+ZR

游戏界面

4. 提示和撤销

点“提示”后,棋盘会标出一把现在就能飞出去的剑,同时在下面显示它的位置。它只告诉我该点哪一把,不会自动替我操作。

提示功能

“撤销”会把上一步的飞剑、剑心和积分一起恢复。我特地把提示文字也截了下来,这样能看出按钮确实有反应,不只是摆在界面上。

撤销功能

5. 飞出与碰撞

飞剑前面没有阻挡时,会沿着剑尖方向加速飞出去,后面还能看到剑气和光点。动画播完以后,这把剑才会真正从棋盘里删除。

飞剑破阵

如果前面有别的飞剑挡着,当前飞剑会先往前冲一下,撞出火花和冲击环,然后再弹回原来的位置,同时扣掉一点剑心。这个效果也是我觉得原版反馈不够明显以后继续加的。

碰撞反馈

6. 后期关卡和失败界面

越到后面棋盘越大,飞剑数量也越多。下面是第五关“十四境”,它是 9×9 棋盘,一共有 24 把飞剑。

第五关十四境

连续点错三次以后剑心耗尽,会进入“问剑未成”页面,可以直接重新挑战当前关卡,也可以返回开始界面。

失败界面

7. 过关结算

清空棋盘以后会显示星级、积分和用时,并保存这一关拿到的最好星级。普通关卡可以继续进入下一境,五关全部完成后会出现“五境皆破”的结算页。

过关界面

五境皆破结算界面


二、项目介绍

1. 游戏规则

规则其实不复杂。每把飞剑只有上、下、左、右四个方向,剑尖指向哪里,它就往哪里飞。点击以后,程序会顺着这个方向检查同一行或同一列:

  1. 前方没有其他飞剑:播放飞出动画,动画结束后消除该飞剑,积分增加 100;
  2. 前方存在其他飞剑:飞剑碰撞回弹,不会消失,剑心减少 1,积分扣除 25(最低为 0);
  3. 清空本关所有飞剑:显示过关结果并解锁下一境;
  4. 三点剑心全部耗尽:显示失败页面,可以重新挑战当前境界;
  5. 点击空格、动画期间重复点击或在非游戏状态点击,不会错误删除飞剑。

这里有一点比较容易看错:挡路的那把剑朝哪个方向并不重要,而且两把剑中间就算隔着空格也还是会挡住。只要它在当前飞剑正前方的同一行或同一列上,就不能直接飞出去。

2. 五个境界

境界 棋盘大小 飞剑数量 设计特点
骊珠洞天 5×5 6 用较少飞剑熟悉四方向判断
山水问剑 6×6 11 增加隔空阻挡和交叉路线
剑气长城 7×7 16 多行多列相互限制,消除顺序更重要
倒悬山 8×8 18 棋盘扩大,边缘与中心路线同时出现
十四境 9×9 24 综合考察四方向、远距离阻挡和消除顺序

这些关卡不是随便把剑摆上去就算完成。每次改完布局,我都会让测试程序从当前能飞出去的剑开始,一步步尝试把棋盘清空。能找到完整顺序以后,我才会保留这个关卡。

3. 已实现功能

类型 功能
基础玩法 四方向飞剑、路径检测、三次失误机会、过关与失败、重新开始
界面反馈 飞出动画、剑气拖尾、碰撞冲击环、回弹、文字提示、提示音
关卡系统 五个固定可解关卡、逐关解锁、境界选择
辅助操作 安全飞剑提示、撤销上一步、快捷键操作
成绩系统 积分、计时、星级评价、最佳星级记录
本地保存 保存最高解锁关卡与各关最佳星级,损坏存档可安全恢复
测试检查 规则和界面分开测试、12 项自动化测试、GUI 动画检查

4. 运行方法

运行环境是 Python 3.10 或更高版本。下载仓库以后,在项目目录运行:

python main.py

如果是 Windows,也可以直接双击 run_game.bat。游戏不需要联网,也不用再装第三方库。


三、实现思路

1. 项目结构

为了后面修改起来方便,我把规则、界面、关卡和存档分成了几个文件,没有把所有东西都塞进 main.py

arrow_escape_game/
├─ arrow_game/
│  ├─ model.py       # 飞剑、方向、游戏状态与路径规则
│  ├─ levels.py      # 五个固定关卡
│  ├─ progress.py    # 解锁进度与最佳星级存档
│  └─ app.py         # Tkinter 界面、动画、计时与交互
├─ assets/           # 四方向飞剑和开始背景
├─ screenshots/      # 博客展示截图
├─ tests/            # 自动化测试
├─ tools/            # 素材构建、截图和 GUI 冒烟测试工具
├─ main.py
└─ run_game.bat

其中 GameEngine 不负责画图,只管关卡、飞剑、剑心、积分和游戏状态;ArrowGameApp 负责画面、按钮和动画。这样我改界面时,不容易把原来的路径规则一起改坏。

2. 飞剑与方向表示

每把飞剑只需要记录行、列和方向。四个方向对应的行列变化写在同一个地方:

class Direction(str, Enum):
    UP = "U"
    DOWN = "D"
    LEFT = "L"
    RIGHT = "R"

    @property
    def delta(self):
        return {
            Direction.UP: (-1, 0),
            Direction.DOWN: (1, 0),
            Direction.LEFT: (0, -1),
            Direction.RIGHT: (0, 1),
        }[self]

例如向上就是行号减 1,向右就是列号加 1。关卡文件只管飞剑放在哪一格,真正显示时,再由界面把行列换算成画布坐标,并选择对应方向的飞剑图片。

3. 路径与最近阻挡物检测

我一开始以为要像动作游戏一样做碰撞框,后来发现这个游戏只需要看格子位置:有没有别的飞剑和它同行或同列,而且刚好在它前面。

def find_blocker(self, arrow: Arrow) -> Arrow | None:
    dr, dc = arrow.direction.delta
    blockers = []
    for candidate in self._arrows.values():
        if candidate == arrow:
            continue
        row_gap = candidate.row - arrow.row
        col_gap = candidate.col - arrow.col
        if dr and col_gap == 0 and row_gap * dr > 0:
            blockers.append((abs(row_gap), candidate))
        elif dc and row_gap == 0 and col_gap * dc > 0:
            blockers.append((abs(col_gap), candidate))
    return min(blockers, key=lambda item: item[0])[1] if blockers else None

代码里的乘法判断用来排除飞剑后面的对象,同行、同列判断则排除斜对角位置。如果前面有不止一把剑,就取最近的那一把,这样碰撞火花才会画在正确的位置。

4. 一次点击如何完成状态变化

一次有效点击按以下顺序处理:

点击飞剑
   ↓
保存操作前快照(用于撤销)
   ↓
查找前方最近阻挡物
   ├─ 无阻挡:播放飞出动画 → 删除飞剑 → 加分 → 判断是否过关
   └─ 有阻挡:扣剑心与积分 → 播放冲刺/火花/回弹 → 判断是否失败

这一步最容易出问题。如果动画还没结束就马上删数据,或者允许连续点击,就可能一次删掉两把剑。所以飞出动画结束后才刷新棋盘,碰撞时则一直保留原来的飞剑;动画播放期间也暂时不接收新的棋盘点击。

5. 提示、撤销与存档

提示功能会找出一把当前没有被挡住的飞剑,只把它标出来,不会替玩家点击。撤销则是在每次有效操作前先保存一份 GameSnapshot,里面有飞剑、剑心、积分和当前状态;点撤销时把这份记录恢复回来。

进度用 JSON 文件保存在 Windows 用户目录,只记住解锁到第几关和每关最好星级,不保存玩到一半的棋盘。我还加了一个损坏存档测试:如果文件内容不是合法 JSON,游戏会回到只解锁第一关的状态,不会因为一个坏文件直接打不开。

6. 关卡可解性验证

五关的可解性用广度优先搜索检查。简单来说,就是每次找出所有能飞走的剑,分别尝试删除,再继续往下找,直到棋盘为空。这个办法能说明关卡至少有一种正确顺序,不过它只能证明“能通关”,不能判断这一关玩起来是不是太难或者太无聊。


四、从普通箭头到飞剑主题的迭代

这个游戏并不是一次就变成现在这样。下面几次修改是我自己感觉变化最明显的地方。

1. 第一版:先完成基础规则

最开始我只要求先把棋盘、四方向箭头、路径判断、过关和失败做出来。那一版虽然能玩,但是界面比较简单,看起来很像一个测试程序。

2. 第二版:替换为白玉金纹飞剑

后来我提出把箭头换成《剑来》风格的飞剑,并发了一张我喜欢的飞剑图片。第一次做出来的剑周围还有一圈圆形装饰,缩小后又短又胖,我觉得实在不好看,所以继续要求去掉圆圈,保留细长剑身和明显的剑尖,最后再转成上、下、左、右四张图片。

3. 第三版:补足“飞出去”和“撞回来”

只把箭头换成图片以后,点下去还是没有“飞剑”的感觉。我又要求它飞出去时越来越快,并加上剑气和光点;撞到别的剑时则往前冲、出现火花,再弹回原位。改完以后重新测试,能飞的剑会消失,被挡的剑还在,剑心也会正常减少。

4. 第四版:从完成作业到增强可玩性

基础玩法稳定以后,我把三关增加到五关,又加入境界选择、计时、积分、星级、提示、撤销和存档。每加完一项都会重新跑原来的测试,避免新功能把已经正常的路径判断弄坏。


五、AIGC 使用过程

记录一:需求拆分和结构设计

子任务 借助何种 AIGC 技术 AI 实现或提供了什么 效果如何 人工修改
需求分析 Codex 将题目拆成规则、界面、关卡、测试和文档模块 得到清晰开发顺序 本人逐项对照作业要求复核

我的要求:先阅读完整作业要求,再一步一步确认对“一箭又一箭”玩法、基础要求和博客结构的理解,最后给出适合小型 Python 图形项目的实现方案,完成后交给我检查。

AI 给出的方案是把规则、界面和关卡分开写。开始时我不太清楚这样做有什么用,后面加动画和测试时才发现确实方便,所以把这个结构保留了下来,界面文字和主题则按我的想法改成飞剑风格。

记录二:路径检测实现

子任务 借助何种 AIGC 技术 AI 实现或提供了什么 效果如何 人工修改
路径检测 Codex 生成四方向阻挡判断和最近阻挡物查找逻辑 自动测试通过 本人检查边界、对角线和后方误判

我的要求:实现同行、同列的飞剑路径检测,不能让斜对角飞剑造成误判,并找出距离最近的阻挡者。

路径检测与碰撞效果的 AIGC 协作记录

AI 根据方向比较行列差,再筛选飞剑前方候选项。我通过 T01~T03 检查无阻挡飞出、有阻挡保留和边缘位置,并将最近阻挡物接入碰撞效果。

记录三:测试与关卡检查

子任务 借助何种 AIGC 技术 AI 实现或提供了什么 效果如何 人工修改
测试设计 Codex 生成核心功能测试和关卡可解性搜索思路 12 项测试可重复执行 本人复跑测试并核对输出

我的要求:按照作业给出的 T01~T06 场景编写自动化测试,并避免设计出无法通关的关卡。

AI 按照作业要求先写了 T01~T06,后来我又让它增加关卡可解性、提示、撤销、选关和存档测试,最后一共 12 项。这样不只是看到游戏能打开,还能检查每个功能有没有真的改变正确的数据。

记录四:飞剑素材与动画

子任务 借助何种 AIGC 技术 AI 实现或提供了什么 效果如何 人工修改
主题美术 图像生成与 Codex 根据参考图生成透明白玉金纹飞剑,并制作四方向素材和动画 剑尖清晰、无圆形底座 对比两版后保留细长比例并调整动画

我的要求:把普通箭头改为参考图中的白玉金纹飞剑,去掉飞剑周围的圆圈,并体现飞剑飞出棋盘的速度感。

飞剑素材与飞出动画的 AIGC 协作记录

第一版素材缩小以后还是显得太圆,我又要求继续去掉外面的装饰。飞出距离、速度、拖尾数量和碰撞回弹也改了几次。最后看起来比较像一把剑真正飞出去,而不是图片突然消失。

记录五:扩展功能与状态恢复

子任务 借助何种 AIGC 技术 AI 实现或提供了什么 效果如何 人工修改
扩展功能 Codex 增加选关、积分计时、星级、提示、撤销和本机存档 操作更完整,测试保持通过 保留简单按钮和 JSON 存档,便于理解

我的要求:在基础功能正确的前提下,按照附加分示例增加实用功能,但不要让代码和操作变得难以解释。

扩展功能与状态恢复的 AIGC 协作记录

最后做出来的效果是:提示只标出一把安全的剑,撤销会恢复刚才的操作,通关以后能解锁下一关并保存星级。加完第四、第五关后又重新检查了一遍可解性,存档损坏的情况也单独测试了。

AIGC 使用总结

AIGC 最大的帮助就是能很快把我的想法做成一个可以看的版本,但“能运行”和“我满意”还是两回事。比如第一版飞剑有圆圈、飞出去不够快、碰撞也不明显,这些都是我实际看到以后再继续要求修改的。至于关卡能不能通,也不能只听 AI 说,需要测试程序真的找到清空顺序。

这次项目里 AI 写了比较多代码,我主要负责定规则、选主题、找参考图、看运行效果,再把不满意的地方说清楚让它继续改。我重点看懂了方向变化、同行同列判断、最近阻挡物、撤销快照和 JSON 存档,因为这些都是老师问到时必须能说明白的部分。


六、测试过程与结果

1. 测试环境

  • 操作系统:Windows
  • Python:3.13.0
  • 图形库:Tkinter(Python 标准库)
  • 单元测试命令:python -m unittest discover -s tests -v
  • 最近复测:2026-09-21,12 项全部通过,用时 24.102 秒

2. 作业要求与扩展测试

编号 测试内容 预期结果 实际结果 是否通过
T01 点击前方无阻挡的飞剑 飞剑飞出棋盘并消失 删除对应飞剑,剩余数量减 1 通过
T02 点击前方有阻挡的飞剑 飞剑不消失,失误次数减 1 返回最近阻挡者,飞剑保留,剑心减 1 通过
T03 点击边缘且朝向棋盘外的飞剑 正常消失,不发生越界错误 正确完成最后一步并进入结算 通过
T04 清除本关全部飞剑 显示通关并可进入下一关 状态变为本关完成,下一关可载入 通过
T05 失误次数耗尽 显示失败并允许重开 剑心归零后进入失败状态 通过
T06 游戏进行中重新开始 布局和失误次数恢复 飞剑、剑心和状态恢复初始值 通过
T07 验证全部固定关卡 五关均存在合法清空顺序 状态搜索找到空棋盘 通过
T08 请求提示 返回一把当前可安全飞出的飞剑 返回结果前方无阻挡 通过
T09 撤销上一步 恢复飞剑、剑心和积分 快照内容完整恢复 通过
T10 从选关页进入指定境界 正确加载对应关卡 关卡编号和状态正确 通过
T11 保存并重新读取进度 解锁关卡和最佳星级一致 JSON 写入、读取结果一致 通过
T12 读取损坏的存档文件 程序不崩溃并恢复初始进度 返回第一关和空星级记录 通过

3. GUI 动画专项检查

单元测试只能检查数据,看不到动画,所以我又运行了 python tools/smoke_gui.py。它会先点击一把被挡住的飞剑,确认回弹后飞剑还在、剑心从 3 变成 2;再点一把能飞出的剑,确认动画结束后它确实消失;最后继续检查积分、撤销和提示。输出为:

GUI smoke test OK: collision, flight, score, undo and hint all finished

博客里的游戏截图都是当前程序实际打开以后截下来的,不是单独画出来的效果图。为了让博客展示得更完整,我又补截了提示、撤销、第五关、失败和五境通关页面。

自动测试可以反复检查规则,但它不知道按钮好不好点、飞剑方向清不清楚,这些还是要打开游戏以后自己看。所以我把自动测试结果和界面截图分开记录,没有把“程序能找到解”直接写成“我已经手动通关”。


七、PSP 表格

这个作业是我分五天断断续续做完的。下面的时间是按开发过程回忆整理的,实际大约用了 18.5 小时。

任务 预估耗时(小时) 实际耗时(小时) 差异(小时)
需求分析与游戏设计 0.8 1.2 +0.4
Python 与图形库学习 1.0 1.5 +0.5
游戏界面实现 2.0 4.0 +2.0
路径与碰撞逻辑实现 1.5 2.5 +1.0
关卡设计 1.0 2.0 +1.0
AIGC 辅助开发 1.0 1.5 +0.5
测试与修改 1.5 2.7 +1.2
README 与博客撰写 1.5 3.1 +1.6
合计 10.3 18.5 +8.2

最后比我一开始估计的多了 8.2 小时,主要都花在界面、动画和测试上。路径判断本身没有特别长,但飞剑缩小以后好不好看、动画快不快、撞击够不够明显,都要反复打开游戏才能知道。开始背景和飞剑素材也改了几版;加了提示、撤销和存档以后,还要重新跑原来的测试并补截图,所以实际时间比一开始想的长不少。


八、心得体会

这是我第一次把一个小游戏从规则、界面、素材一直做到测试和博客展示。刚开始我最在意的是“画面像不像一个游戏”,做到后面才发现,什么时候能点击、怎样算被挡住、动画结束前要不要删掉飞剑、重新开始要恢复什么,这些规则如果没想清楚,界面做得越多越容易出错。

GameEngine 和 Tkinter 界面分开,是我这次觉得最有用的一点。这样不用每次都手动点游戏,也能检查路径、失败、过关、提示和撤销。如果所有判断都写在鼠标点击里面,后面改动画时我可能根本不知道是画面错了,还是游戏数据错了。

AIGC 确实让我少走了很多弯路,不过它不是输入一句话就直接给我成品。飞剑外观、背景、碰撞回弹、五关布局和博客都改了很多次。它可以很快给出一版,我还是要自己判断好不好看、符不符合题目、能不能运行,以及我自己能不能解释清楚。

如果以后继续做,我想增加可以选难度的随机关卡,再加一个能开关的背景音乐(一开始没想到)保证五关可解、点错以后有明显反馈、测试能够重复运行,比盲目加更多功能更重要。最后看到普通箭头真的变成飞剑飞出去时,还是挺有成就感的。

感谢阅读。

posted @ 2026-09-21 17:27  天地玄黄人  阅读(15)  评论(0)    收藏  举报