软件工程第二次作业

一、基本信息

这个作业属于哪个课程软件工程
这个作业要求在哪里https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering/homework/16717
这个作业的目标使用 Python 和 AIGC 完成"一箭又一箭"小游戏
学号102401111
GitHub 仓库https://github.com/Kerical/arrow

二、项目展示

开始界面

游戏进行中

通关界面

三、项目介绍

"一箭又一箭"是一款基于 Pygame 开发的网格逻辑小游戏。玩家在 6×6 棋盘上点击箭头,判断箭头朝向的边界路径上是否有阻挡。若路径畅通,箭头沿方向飞出棋盘消失;若被其他箭头挡住,则扣除一次失误次数。清除当前关卡所有箭头即可过关,失误次数耗尽则失败。

游戏规则:

    每个箭头只能沿自身朝向直线移动

    路径上不能有其他存活箭头(允许同行/同列,但方向不能互挡)

    目标:清空当前关卡所有箭头

    失误次数限制:3 次

主要功能:

    三关卡递进设计,含策略性"挡路箭头"

    箭头飞出动画

    被挡时箭头震动+闪烁反馈

    中文字体支持

    菜单 / 游戏 / 通关 / 失败多状态切换

四、实现思路

4.1 整体架构

游戏采用面向对象设计,核心类为 Arrow(箭头)和 Game(游戏状态管理器)。主循环负责事件处理、状态更新和画面绘制。游戏状态分为:菜单(MENU)、游戏中(PLAYING)、关卡通过(LEVEL_CLEAR)、游戏失败(GAME_OVER)四种,通过 self.state 变量切换。

4.2 数据结构设计

箭头类 Arrow:每个箭头实例封装了自身在棋盘上的所有信息:

class Arrow:
    def __init__(self, x, y, direction):
        self.cx = x           # 所在列,范围 0~5
        self.cy = y           # 所在行,范围 0~5
        self.dir = direction  # 方向字符串:"up"/"down"/"left"/"right"
        self.alive = True     # 是否存活(被消除后设为 False)
        self.flying = False   # 是否正在播放飞出动画
        self.fly_progress = 0 # 飞出动画进度,0.0~1.0
        self.fly_target = (0, 0)  # 飞出动画终点坐标(像素)
        self.shake_timer = 0  # 被挡时震动计时器
        self.shake_offset = 0 # 震动偏移量

关卡数据 LEVELS:采用列表嵌套字典结构,每个关卡包含名称和箭头布局。箭头以元组 (列, 行, 方向) 表示,便于手工编辑和扩展:

LEVELS = [
    {
        "name": "第一关",
        "arrows": [
            (0, 0, "right"),
            (0, 2, "up"),
            (2, 5, "left"),
            (2, 3, "left"),
        ],
    },
    # 第二关、第三关类似...
]

方向映射 DIRS:将方向名映射为网格坐标偏移量,用于路径检测和飞出动画:

DIRS = {
    "up":    ( 0, -1),
    "down":  ( 0,  1),
    "left":  (-1,  0),
    "right": ( 1,  0),
}

4.3 坐标系统

游戏使用两套坐标系:

  • 网格坐标:(cx, cy),范围 0~5,用于逻辑判断和路径检测。
  • 像素坐标:(px, py),通过 grid_to_pixel(cx, cy) 转换,用于 Pygame 绘制。转换公式为:px = BOARD_X + cx * CELL_SIZE + CELL_SIZE // 2py = BOARD_Y + cy * CELL_SIZE + CELL_SIZE // 2

这种设计将逻辑与渲染解耦,方便后续调整棋盘位置和格子大小。

4.4 路径检测算法(核心)

点击箭头后,调用 blocked_in_direction(arrow) 判断该箭头朝向的路径上是否有阻挡。

  1. 从被点击箭头的相邻格子开始(nx = cx + dx, ny = cy + dy)。
  2. 沿方向逐步移动,每次步进一个格子。
  3. 每到一个格子,遍历当前所有箭头,检查是否有存活且未飞出的箭头占据该位置。
  4. 若找到阻挡,立即返回 True
  5. 若步进到棋盘边界仍未找到阻挡,返回 False
def blocked_in_direction(self, arrow):
    dx, dy = DIRS[arrow.dir]
    nx, ny = arrow.cx + dx, arrow.cy + dy
    while 0 <= nx < COLS and 0 <= ny < ROWS:
        for a in self.arrows:
            if a.alive and not a.flying and a.grid_pos == (nx, ny):
                return True   # 路径上有箭头,被阻挡
        nx += dx
        ny += dy
    return False  # 路径畅通,无阻挡

设计要点:

  • 排除 a.flying 的箭头,避免正在飞出的箭头被误判为阻挡。
  • 边界条件使用 0 <= nx < COLS 而非 <= COLS - 1,防止索引越界。
  • 允许同行/同列但方向不互挡的布局,增加策略深度。

4.5 飞出动画实现

当路径畅通时,箭头不立即消失,而是播放一段飞出动画:

  1. 调用 fly_out_target(arrow) 计算终点:沿方向继续延伸一步到棋盘外。
  2. 设置 arrow.flying = Truearrow.fly_progress 从 0 开始每帧递增。
  3. 每帧根据 fly_progress 用线性插值计算当前像素位置:current = start + (target - start) * progress
  4. fly_progress >= 1.0 时,设置 arrow.alive = False,箭头彻底消失。

插值公式为:

arrow.pixel_pos = (
    arrow.pixel_pos[0] + (arrow.fly_target[0] - arrow.pixel_pos[0]) * arrow.fly_progress,
    arrow.pixel_pos[1] + (arrow.fly_target[1] - arrow.pixel_pos[1]) * arrow.fly_progress,
)

4.6 碰撞反馈

当路径被阻挡时:

  • 触发 arrow.shake_timer = 20(帧数),每帧在绘制时对箭头位置施加正弦偏移量,产生左右震动效果。
  • 同时设置失误次数减 1,并在屏幕中央显示"被挡住了!失误。"消息,消息持续 60 帧后自动消失。
  • 箭头颜色短暂闪烁为红色(通过 shake_timer 控制颜色切换)。

4.7 关卡切换与状态管理

每帧检查当前存活箭头数量:

  • alive_count == 0 且当前关卡不是最后一关,切换到 LEVEL_CLEAR 状态,显示"关卡通过!"按钮。
  • alive_count == 0 且是最后一关,显示"游戏通关!"。
  • mistakes <= 0,切换到 GAME_OVER 状态,显示失败界面和"重试本关"按钮。

重新开始功能通过重新加载当前关卡的箭头数据实现,重置所有状态变量。

4.8 中文字体处理

Windows 下 Pygame 默认字体不支持中文,解决方案:

try:
    font = pygame.font.Font("C:/Windows/Fonts/msyh.ttc", size)
except:
    font = pygame.font.SysFont("microsoftyahei", size)

使用 try-except 确保在不同环境下都能正常显示文字。

4.9 主循环结构

while running:
    dt = clock.tick(60)  # 限制 60 FPS
    for event in pygame.event.get():
        handle_event(event)   # 处理鼠标点击、键盘等
    update(dt)               # 更新动画进度、震动计时器等
    draw(screen)             # 根据当前状态绘制画面
    pygame.display.flip()

事件处理中,点击逻辑为:遍历所有存活箭头,检查鼠标坐标是否在箭头所在的格子内,若是则调用路径检测并执行业务逻辑。

五、AIGC 使用过程

子任务借助何种 AIGCAI 提供了什么效果如何人工修改
路径检测逻辑deepseek生成四个方向的射线检测代码框架向上检测时边界条件写反导致越界修改 while 边界判断顺序,增加 not a.flying 条件
关卡设计deepseek生成三关的箭头布局数组第二关存在两个箭头互相正对导致无法通关手工调整箭头位置和方向,反复试玩验证每关可解
中文乱码修复deepseek提供 Windows 下 Pygame 中文字体加载方案字体加载成功,中文正常显示增加 try-except 回退到 pygame.font.SysFont
界面布局调整deepseek分析文字超出界面的原因并给出调整方案调整后底部提示和按钮不再重叠根据实际运行效果微调 BOARD_Y、HEIGHT 等参数

六、测试结果

编号测试内容预期结果实际结果是否通过
T01点击前方无阻挡的箭头箭头飞出棋盘并消失箭头播放飞出动画后消失,剩余箭头数减1通过
T02点击前方有阻挡的箭头箭头不消失,失误次数减1箭头震动闪烁,显示"被挡住了!失误。",失误次数减1通过
T03点击位于边缘且朝向棋盘外的箭头箭头正常消失,不发生越界错误箭头直接飞出,无异常报错通过
T04消除本关全部箭头显示通关并进入下一关弹出"关卡通过!"界面,点击"下一关"进入第二关通过
T05失误次数耗尽显示失败并允许重新开始第3次失误后跳转失败界面,点击"重试本关"可重置通过
T06游戏进行中点击"重新开始"箭头布局和失误次数恢复箭头全部重置为初始位置,失误次数恢复为3通过

测试环境:Windows 11 + Python 3.13 + pygame 2.6.1,采用手工测试。

七、PSP 表格

任务预估耗时(小时)实际耗时(小时)差异(小时)
需求分析与游戏设计111
Python 与图形库学习1.520.5
游戏界面实现220
路径与碰撞逻辑实现1.51.50
关卡设计121
AIGC 辅助开发220
测试与修改1.520.5
README 与博客撰写121
合计11.515.54

八、心得体会

AI 带来的帮助

    快速搭建 Pygame 代码框架与类结构,节省查文档时间

    辅助定位中文显示、界面布局、边界判断等问题

    提供关卡布局与文档结构参考

出现的问题

    AI 生成代码边界条件需人工核对

    AI 给出的关卡布局不一定都可解,需试玩调整

    界面参数不能只靠 AI 反复改,自己理解坐标后效率更高

收获

    掌握 Pygame 事件循环、绘制、动画插值、字体渲染

    理解网格游戏坐标映射、射线检测、状态管理

    实践"提需求—验证—修改—提交"的 AI 协作流程

    完成从开发、测试、文档到 GitHub 提交的完整流程

posted @ 2026-09-21 20:13  Kerical  阅读(8)  评论(0)    收藏  举报