软工第二次作业:用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.开始界面

image
开始界面提供一个游戏进入入口和简洁的游戏介绍

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 负责提供方案、生成草稿和加速迭代。 这种协作模式将是我未来学习和职业生涯中不可或缺的能力。

posted @ 2026-09-22 16:44  102402147郑宇嘉  阅读(8)  评论(0)    收藏  举报