软件工程第二次作业
软件工程第二次个人作业:利用 AIGC 开发“一箭又一箭”小游戏
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | 202601软件工程 |
| 这个作业要求在哪里 | 2026秋软件工程个人作业(第二次) |
| 这个作业的目标 | 使用 Python 和 AIGC 完成“一箭又一箭”小游戏 |
| 学号 | 102401434 |
| 姓名 | 包学丰 |
| GitHub 仓库 | OneArrowAgain |
一、项目展示
1. 开始界面
开始界面展示游戏标题、规则说明和“开始游戏”按钮。
2. 游戏界面
游戏界面显示当前关卡、剩余箭头数量、剩余失误次数和重新开始按钮。
第一关
第三关
3. 全关卡通关界面
完成全部三个关卡后,显示独立设计的最终通关界面,使用深蓝背景、金色奖杯和庆祝装饰,并提供重新挑战按钮。
4. 失败界面
失误次数耗尽后显示失败结果,玩家可以重新挑战当前关卡。
5.游戏演示动图

二、项目介绍
1. 游戏规则
“一箭又一箭”是一款点击式箭头解谜游戏,使用Python和Pygame实现。
- 棋盘中的箭头分为上、下、左、右四种方向。
- 玩家使用鼠标左键点击箭头。
- 如果箭头前进方向没有其他箭头阻挡,箭头沿方向飞出棋盘并消失。
- 如果前方存在其他箭头,当前箭头不会消失,并短暂变红。
- 每次点击被阻挡的箭头,剩余失误次数减1。
- 清空当前关卡后显示通关结果,可以进入下一关。
- 失误次数耗尽后显示失败,可以重新开始当前关卡。
2. 开发环境
| 项目 | 环境或工具 |
|---|---|
| 操作系统 | Windows |
| 开发工具 | Visual Studio |
| 编程语言 | Python 3.9.13 |
| 图形库 | Pygame 2.6.1 |
| 版本管理 | Git、GitHub |
| AIGC工具 | Codex |
3. 主要功能
项目实现了以下基础功能:
- 开始、游戏、通关和失败界面;
- 四种方向的单格箭头;
- 鼠标点击和路径阻挡判断;
- 箭头飞出动画;
- 碰撞变红与失误扣除;
- 三个可以通关的关卡;
- 重新开始、下一关和全部通关后重玩;
- 动画期间限制其他箭头点击。
4. 界面特色与素材说明
界面优化主要围绕信息清楚、状态明确和视觉层次展开:
- 顶部状态卡片将关卡、箭头数量和失误次数分组;
- 圆角棋盘和悬停反馈帮助定位鼠标;
- 绿色与红色区分成功和失败;
- 独立的金色奖杯页面突出全部通关;
- 渐变、阴影和装饰图形丰富界面,但不改变游戏规则。
箭头、奖杯、徽章和背景装饰均通过Pygame代码绘制,没有使用原商业游戏的美术素材或音效。
5. 安装与运行
在项目目录中执行:
python -m pip install -r requirements.txt
运行游戏:
python OneArrowAgain.py
也可以在Visual Studio中打开项目,选择主程序后运行。
三、实现思路
1. 文件结构与职责
核心项目结构如下:
OneArrowAgain/
├── OneArrowAgain.py # 界面、动画、事件处理和游戏流程
├── game_logic.py # 四方向路径阻挡判断
├── levels.py # 三个正式关卡的数据
├── TESTING.md # 手工测试记录
├── requirements.txt # Python依赖
├── README.md # 项目说明
└── assets/
└── screenshot/ # 游戏截图
开发初期,我先将窗口、界面和事件处理放在一个主文件中,便于逐步运行和排错。
开始实现路径检测时,将规则判断拆分到game_logic.py;制作正式关卡时,再建立levels.py。主程序负责显示和交互,其他模块分别负责规则和数据。
2. 箭头与关卡的数据表示
每个箭头使用字典保存三个信息:
{"row": 3, "col": 4, "direction": "LEFT"}
row:箭头所在行;col:箭头所在列;direction:箭头朝向。
行列从0开始,方向使用UP、DOWN、LEFT和RIGHT四个字符串。
关卡数据使用列表保存。以下是第一关的数据:
{
"mistakes": 3,
"arrows": [
{"row": 0, "col": 1, "direction": "UP"},
{"row": 1, "col": 4, "direction": "RIGHT"},
{"row": 3, "col": 2, "direction": "DOWN"},
{"row": 4, "col": 0, "direction": "LEFT"},
{"row": 5, "col": 5, "direction": "RIGHT"},
{"row": 3, "col": 4, "direction": "LEFT"},
],
}
三个关卡分别包含6、10和14个箭头,使用6×6棋盘,每关有3次失误机会。
界面显示的关卡编号从1开始,而列表索引从0开始,因此读取关卡时需要减1:
level = LEVELS[current_level - 1]
3. 鼠标点击检测
鼠标事件提供窗口坐标,需要先判断鼠标是否在棋盘内,再换算成格子的行列。
def get_clicked_arrow(mouse_pos):
"""根据鼠标位置查找被点击的箭头"""
mouse_x, mouse_y = mouse_pos
# 鼠标在棋盘外时直接返回
if not (
BOARD_X <= mouse_x < BOARD_X + BOARD_WIDTH
and BOARD_Y <= mouse_y < BOARD_Y + BOARD_HEIGHT
):
return None
# 窗口坐标转换成棋盘行列
col = (mouse_x - BOARD_X) // CELL_SIZE
row = (mouse_y - BOARD_Y) // CELL_SIZE
# 查找该格子中的箭头
for arrow in arrows:
if arrow["row"] == row and arrow["col"] == col:
return arrow
return None
例如棋盘起始横坐标为240、格子宽度为70,鼠标横坐标为415,那么:
(415 - 240) // 70 = 2
对应第2列,即从左到右的第三列。
边界判断采用左闭右开的范围,避免鼠标恰好位于棋盘右侧边界时被换算成第6列。
界面优化后,格子之间增加了视觉间距,但点击检测仍然使用原来的完整网格坐标。点击空白格或棋盘外时返回None,不消除箭头,也不扣除失误。
4. 四方向路径检测
基础版中,一个箭头只占一个格子。因此不需要复杂的物理碰撞系统,只需要检查同一行或同一列中的前方箭头。
| 方向 | 前方阻挡的判断条件 |
|---|---|
| UP | 同一列,其他箭头的行号更小 |
| DOWN | 同一列,其他箭头的行号更大 |
| LEFT | 同一行,其他箭头的列号更小 |
| RIGHT | 同一行,其他箭头的列号更大 |
完整检测函数如下:
def is_blocked(selected_arrow, all_arrows):
"""判断选中箭头的前进方向上是否存在其他箭头"""
selected_row = selected_arrow["row"]
selected_col = selected_arrow["col"]
direction = selected_arrow["direction"]
for other_arrow in all_arrows:
# 不检查箭头自身
if other_arrow is selected_arrow:
continue
other_row = other_arrow["row"]
other_col = other_arrow["col"]
if direction == "UP":
if other_col == selected_col and other_row < selected_row:
return True
elif direction == "DOWN":
if other_col == selected_col and other_row > selected_row:
return True
elif direction == "LEFT":
if other_row == selected_row and other_col < selected_col:
return True
elif direction == "RIGHT":
if other_row == selected_row and other_col > selected_col:
return True
return False
函数发现任意一个符合条件的箭头时,就返回True;遍历结束仍没有阻挡,则返回False。
这里检测的是整条前进路径,不只是相邻格子。即使前方隔着几个空白格,只要仍然存在其他箭头,也不能飞出。
阻挡判断只与其他箭头的位置有关,与其他箭头的朝向无关。例如一个朝右箭头前方存在朝上箭头,仍然会被阻挡。
检测函数遍历一次箭头列表,时间复杂度为O(n),其中n是当前剩余箭头数。对于本项目的小型棋盘,这种方法简单且足够使用。
5. 点击后的消除与碰撞处理
主程序获得被点击的箭头后,调用路径检测函数。以下是鼠标事件处理中的关键代码节选:
blocked = is_blocked(selected_arrow, arrows)
if blocked:
mistakes_left = max(0, mistakes_left - 1)
collision_arrow = selected_arrow
collision_until = pygame.time.get_ticks() + 500
if mistakes_left == 0:
result_kind = "failed"
game_state = RESULT
else:
# 成功时先播放动画,不立即删除
flying_arrow = selected_arrow
flight_offset_x = 0.0
flight_offset_y = 0.0
selected_arrow = None
有阻挡时,箭头保留,失误减1,并记录500毫秒的红色反馈时间。使用max(0, ...)保证失误次数不会变成负数。
无阻挡时,程序保存正在飞出的箭头,并将偏移量归零,交给后续动画更新函数处理。
绘制时根据当前时间选择颜色:
current_time = pygame.time.get_ticks()
if arrow is collision_arrow and current_time < collision_until:
arrow_color = (220, 70, 70)
elif arrow is selected_arrow:
arrow_color = (240, 140, 50)
else:
arrow_color = (45, 95, 150)
6. 界面优化方法
界面装饰主要使用Pygame绘图,不依赖额外商业素材:
- 渐变背景通过逐条绘制色带实现;
- 圆角卡片使用
pygame.draw.rect()和border_radius实现; - 阴影通过在原矩形下方绘制偏移矩形实现;
- 成功与失败徽章使用圆形和线段组合;
- 奖杯使用圆环、矩形和多边形组合;
- 彩纸位置随时间变化,形成飘落效果。
这些视觉效果与核心规则分开,优化绘制函数时不需要改动路径检测。
四、AIGC使用过程
本项目使用Codex辅助开发。以下记录根据真实开发对话整理,需求描述为概括。
| 子任务 | 借助何种AIGC技术 | 我的要求 | AI实现或提供了什么 | 效果如何 | 人工操作与修改 |
|---|---|---|---|---|---|
| 鼠标点击 | Codex | 如何识别被点击的箭头并解决报错 | 提供坐标转换和选中反馈代码,分析缩进及变量作用域问题 | 录入时出现缩进错误和arrow未定义错误,修复后正常 | 根据AI指出的位置调整缩进,将颜色判断放回函数内部 |
| 路径检测 | Codex | 如何判断四方向阻挡并拆分文件 | 提供同行、同列坐标比较逻辑,建议创建game_logic.py | 有阻挡和无阻挡情况得到不同结果 | 创建模块、导入函数并增加测试箭头验证 |
| 关卡与流程 | Codex | 如何实现三个关卡、失败和下一关 | 提供关卡数据、消除顺序检查和流程代码 | 三个关卡能够通关,整合时出现旧变量引用和缩进问题 | 替换旧函数、修复分支缩进,并本人试玩三个关卡 |
| 飞出动画 | Codex | 如何让箭头沿方向飞出 | 提供位移更新、动画交互限制和结束后删除方案 | 箭头能沿正确方向移动,飞出后更新数量和通关状态 | 修改绘制函数与主循环,并进行手工测试 |
开发中的典型问题
鼠标事件录入后曾出现:
IndentationError: expected an indented block
箭头颜色判断放到函数外后又出现:
NameError: name 'arrow' is not defined
我向Codex提供截图和实际代码,AI读取后指出了具体行号。我按照建议修改并重新运行。
拆分关卡数据后,重新开始函数仍引用已经删除的initial_arrows,也导致运行失败。修改为读取当前关卡数据后,重新开始恢复正常。
这些问题让我认识到,局部代码正确不代表整合后一定正确。复制代码后,还需要检查缩进、变量作用域和旧代码是否已经替换。
五、测试结果
1. 测试方式与范围
本项目采用本人手工操作测试。根据最终运行检查和既有测试记录,基础玩法、三个关卡及新版界面均运行正常。
详细基础测试记录见仓库中的TESTING.md。
本次没有编写自动化测试,以下结论仅针对实际检查的手工测试场景,不代表覆盖了所有可能情况。
2. 基础功能测试
| 编号 | 测试操作 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|---|
| T01 | 点击前方无阻挡的箭头 | 箭头飞出并消失,剩余数量减1,失误不变 | 箭头沿朝向飞出棋盘后消失,剩余数量减1,失误次数不变 | 通过 |
| T02 | 点击前方有阻挡的箭头 | 箭头保留并产生反馈,失误减1 | 箭头短暂变红且未消失,剩余失误次数减1 | 通过 |
| T03 | 点击边缘且朝向棋盘外的箭头 | 正常飞出,不发生越界错误 | 边缘箭头正常飞出并消失,程序没有出现越界错误 | 通过 |
| T04 | 清除当前关卡全部箭头并点击下一关 | 显示通关并进入下一关 | 最后一个箭头飞出后显示通关,按钮正常切换关卡 | 通过 |
| T05 | 连续碰撞直到失误耗尽 | 显示失败并允许重新开始 | 失误次数减至0后显示失败,重新开始能恢复当前关卡 | 通过 |
| T06 | 消除一个箭头、碰撞一次后重新开始 | 恢复初始布局、箭头数量和失误次数 | 当前关卡恢复初始布局与数量,失误次数恢复为3 | 通过 |
3. 关卡试玩
| 关卡 | 初始箭头数 | 是否本人完整通关 | 实际结果 |
|---|---|---|---|
| 第一关 | 6 | 是 | 可以清空棋盘,显示过关结果并进入第二关 |
| 第二关 | 10 | 是 | 可以清空棋盘,显示过关结果并进入第三关 |
| 第三关 | 14 | 是 | 可以清空棋盘,并显示独立的全部通关庆祝页面 |
三个关卡均存在可行的消除顺序,已由本人实际试玩通关。
4. 动画与边界交互测试
| 测试内容 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|
| 动画期间点击其他箭头 | 不启动其他箭头操作 | 其他箭头不响应,当前动画正常继续 | 通过 |
| 动画期间重新开始 | 清除动画并恢复当前关卡 | 动画清除,棋盘和失误次数恢复 | 通过 |
| 点击空白格 | 不消除箭头、不扣失误 | 游戏数据没有变化 | 通过 |
| 点击棋盘外 | 不触发箭头操作,不报错 | 程序正常运行,游戏数据没有变化 | 通过 |
| 全部通关后重玩 | 返回第一关并恢复初始数据 | 恢复第一关的6个箭头和3次失误机会 | 通过 |
5. 测试总结
本次测试覆盖了基本规则、边界处理、动画交互、重新开始、关卡切换和结果页面分流。
在上述手工测试覆盖的场景中,各项功能符合预期,未发现异常。开发中曾出现的缩进、变量作用域和旧变量引用问题已修复。
后续可以增加自动化测试,系统覆盖四个方向的有阻挡、无阻挡、后方箭头及不同行列等组合。
六、Git与项目管理
我在开发初期建立GitHub仓库,随后分阶段更新程序、依赖、README、测试记录和截图,而不是最后一次性上传整个项目。
开发中遇到过远程分支比本地更新而无法推送,以及文件修改后没有创建Commit就直接推送的问题。
通过实际操作,我理解了三个步骤:
git add:选择本次要提交的文件
git commit:创建本地版本记录
git push:将提交上传到GitHub
项目通过.gitignore排除Visual Studio缓存、Python缓存和虚拟环境。
截图统一保存在assets/screenshot中。界面优化后替换同名图片,README和博客继续引用这些图片地址。
七、PSP表格
本项目实际总投入约10小时。由于开发过程中没有逐项计时,实际耗时采用开发结束后的回顾估算,阶段分配并非精确计时结果。
预计耗时列为整理阶段的参考估算,并非开发前记录的原始预计值。差异按“实际耗时-预计耗时”计算。
| 任务 | 预计耗时(小时,参考估算) | 实际耗时(小时,回顾估算) | 差异(小时) |
|---|---|---|---|
| 需求分析与游戏设计 | 1.0 | 0.5 | -0.5 |
| Python与图形库学习 | 2.0 | 1.0 | -1.0 |
| 游戏界面实现与优化 | 3.5 | 2.5 | -1.0 |
| 路径与碰撞逻辑实现 | 2.0 | 1.0 | -1.0 |
| 关卡设计 | 1.0 | 0.5 | -0.5 |
| AIGC辅助开发 | 1.5 | 2.0 | +0.5 |
| 测试与修改 | 2.0 | 1.5 | -0.5 |
| README与博客撰写 | 2.5 | 2.0 | -0.5 |
| 合计 | 15.5 | 11.0 | -4.5 |
耗时分析
按本次回顾估算,界面实现与优化、测试修改和文档整理占用了较多时间。界面不仅需要完成初版,还进行了状态卡片、渐变背景和全部通关页面等调整。
Codex提供了实现思路、代码示例和错误定位帮助,但代码录入、运行验证、关卡试玩和Git操作仍需要本人完成。
上述差异仅用于回顾分析,不能据此认定AI准确节省了4.5小时。后续开发中,我会在开始前记录预计耗时,并在每个阶段结束后及时记录实际耗时,提高PSP数据的准确性。
八、心得体会
1. AI带来的帮助
Codex帮助我将作业拆分成较小的开发步骤,从环境配置逐步推进到界面、鼠标点击、路径检测、关卡和动画。
在不熟悉Pygame写法时,AI提供的示例可以作为学习起点。发生错误后,结合截图和实际文件分析,也能更快找到问题位置。
2. 我遇到的问题
本次开发多次出现缩进问题。我开始时更关注代码内容,没有充分注意代码属于哪个函数或条件分支。
后来我认识到,Python缩进决定执行逻辑。录入代码后,需要检查缩进、变量作用域,以及新旧代码是否已经正确衔接。
Git操作也是一个学习过程。保存文件、创建Commit和推送是不同步骤,直接推送不会自动上传尚未提交的修改。
3. 界面优化的体会
初版界面以纯色背景和文字为主,能够完成操作,但视觉层次比较简单。
优化时,我没有先增加新的玩法,而是通过卡片、图标、配色和反馈突出已有功能。普通过关与失败使用不同颜色,全部通关再使用独立的奖杯庆祝画面,使不同游戏状态更容易区分。
界面改动也提醒我:按钮位置变化后,需要重新检查点击区域;结果页面分流后,需要验证第三关失败不会被误判为全部通关。
4. 我的收获
通过本次开发,我逐步了解了Pygame主循环、鼠标事件、二维坐标转换、列表和字典,以及基本游戏状态管理。
使用AIGC并不意味着省略理解和测试。AI提供的代码进入项目后,仍然需要由我运行、验证,并对最终提交负责。
5. 后续改进方向
如果继续完善项目,我希望增加路径检测的自动化测试、更多关卡和提示功能,并进一步整理主程序结构。
相比增加大量附加功能,我更希望先保证基础规则正确、交互稳定和代码容易理解。
九、总结
本项目实现了“一箭又一箭”的基础玩法,包括四方向阻挡判断、箭头飞出、碰撞失误、三个关卡,以及通关、失败和重新开始流程。
在基础功能完成后,进一步优化了游戏信息卡片、棋盘、开始与结果界面,并为全部通关设计了独立的庆祝页面。
整个开发过程采用Codex辅助与本人本地操作、运行测试相结合的方式,源码、运行说明和截图已通过GitHub进行管理。

浙公网安备 33010602011771号