软件工程第二次作业

软件工程课程第二次个人作业——一箭又一箭

项目 内容
这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering
这个作业要求在哪里 https://edu.cnblogs.com/campus/fzu/202601SofwareEngineering/homework/16717
这个作业的目标 使用 Python 和 AIGC 完成“一箭又一箭”小游戏
学号 102402117
GitHub 仓库 https://github.com/chizhi2006/chizhi2006

一、项目展示

  • 开始界面截图:c5d868f739ea868ab0a242f587a9c952

  • 游戏过程截图:967a029764d5321a8c662e64d205ca61
    32bc5b0ac1cceee6b44ae3b80ce96aa5
    a4130422faad9863d84a920c48ba3c77

  • 通关界面截图:12a56ad7eaa67fc5f718cf43638a53fb

  • 失败界面截图:48c771457eea6e8068458b7340d04d24

二、项目介绍

1. 游戏规则

棋盘为 5×5 网格,每个格子可能有一个箭头,方向为上、下、左、右。
点击箭头后,程序检查该箭头前进方向到棋盘边界之间是否还有其它箭头:

  • 没有阻挡:箭头飞出并消失;
  • 有阻挡:箭头不能消失,发生碰撞反馈,失误次数减 1;
  • 清空全部箭头:通关,进入下一关;
  • 失误次数耗尽:本关失败,可重新开始。

2. 界面设计

  • 开始界面:标题、说明、开始按钮;
  • 游戏界面:关卡、剩余箭头、剩余失误、棋盘、重新开始按钮;
  • 结果界面:通关或失败提示,以及下一关/重玩按钮。

3. 主要功能

  • 四种方向箭头;
  • 鼠标点击选择;
  • 路径阻挡判断;
  • 飞出动画与碰撞抖动;
  • 失误次数管理;
  • 3 个可通关关卡;
  • 重新开始;
  • 通关/失败切换。

4. 特色

  • Tkinter 实现,无需额外安装图形库;
  • 箭头飞出有移动动画;
  • 碰撞时箭头变红并抖动;
  • 关卡数据与逻辑分离,方便扩展。

三、实现思路

1. 箭头、方向与关卡表示

  • 方向用整数表示:0=上, 1=下, 2=左, 3=右
  • 方向向量 DIRS0:(-1,0), 1:(1,0), 2:(0,-1), 3:(0,1)
  • 棋盘用二维列表 board[5][5],存方向整数或 None
  • 关卡数据 LEVELS 是列表,每关是若干 [行, 列, 方向]

2. 路径检测方法

can_fly(r, c)

  1. 取出 (r,c) 处箭头的方向 d
  2. 根据 DIRS[d] 得到 dr, dc
  3. (r+dr, c+dc) 开始,沿方向逐格前进;
  4. 如果遇到任意非空格子,返回 False
  5. 如果走到棋盘外,返回 True

3. 交互与动画

  • Canvas 绑定 <Button-1>,把点击坐标转成行列;
  • 能飞:逻辑删除,canvas.move 分步移动箭头,最后删除;
  • 不能飞:失误 +1,箭头变红,用 after 做左右抖动,再恢复。

四、关键代码及说明

1. 方向与关卡数据

DIRS = {0: (-1, 0), 1: (1, 0), 2: (0, -1), 3: (0, 1)}
ARROW_CHARS = {0: '↑', 1: '↓', 2: '←', 3: '→'}
LEVELS = [
    [[0,0,3], [0,4,1], [2,1,0], [4,2,0], [4,4,2]],
    [[0,4,1], [1,2,1], [2,0,3], [2,2,3], [2,4,3], [3,1,3], [4,0,0]],
    [[0,4,2], [1,0,3], [1,3,1], [2,2,0], [2,4,0], [3,3,1], [4,0,0], [4,4,2]]
]

说明:
DIRS 把方向整数映射成行列增量,方便计算路径。
ARROW_CHARS 用于在界面上显示对应箭头字符。
LEVELS 保存 3 个关卡,每个箭头用 [行, 列, 方向] 表示,与逻辑完全分离,后续增加关卡只需改这里。

2. 路径检测核心

def can_fly(self, r, c):
    d = self.board[r][c]
    dr, dc = DIRS[d]
    nr, nc = r + dr, c + dc
    while 0 <= nr < GRID_N and 0 <= nc < GRID_N:
        if self.board[nr][nc] is not None:
            return False
        nr += dr
        nc += dc
    return True

说明:
这是游戏最核心的判断逻辑。
从当前格子沿方向逐格前进,只要遇到任意非空格子就返回 False。
走出棋盘范围仍没遇到阻挡,就返回 True。
边界条件 0 <= nr < GRID_N 保证了不会数组越界。

3. 飞出动画与状态更新

def fly_out(self, r, c):
    d = self.board[r][c]
    self.board[r][c] = None
    item = self.arrow_items[r][c]
    self.arrow_items[r][c] = None
    if item:
        dr, dc = DIRS[d]
        dx, dy = dc * CELL_SIZE, dr * CELL_SIZE
        steps = 8
        def animate(step):
            if step < steps:
                self.canvas.move(item, dx/steps, dy/steps)
                self.root.after(20, lambda: animate(step+1))
            else:
                self.canvas.delete(item)
        animate(0)
    self.update_info()
    remaining = sum(1 for rr in range(GRID_N) for cc in range(GRID_N) if self.board[rr][cc] is not None)
    if remaining == 0:
        self.state = "CLEAR"
        self.root.after(300, self.show_result)

说明:
先做逻辑删除,把 board[r][c] 设为 None。
用 canvas.move 分 8 步移动箭头,每步 20ms,形成飞出动画。
动画结束后删除 Canvas 上的箭头对象。
更新信息栏,并检查是否所有箭头都已清除,若是则进入通关状态。

4. 碰撞反馈与失误处理

def collide(self, r, c):
    self.mistakes += 1
    self.update_info()
    item = self.arrow_items[r][c]
    if item:
        self.canvas.itemconfig(item, fill="#f46b6b")
        original_x = MARGIN + c*CELL_SIZE + CELL_SIZE//2
        original_y = MARGIN + r*CELL_SIZE + CELL_SIZE//2
        offsets = [-8, 8, -6, 6, -3, 3, 0]
        def shake(idx):
            if idx < len(offsets):
                self.canvas.coords(item, original_x + offsets[idx], original_y)
                self.root.after(40, lambda: shake(idx+1))
            else:
                self.canvas.coords(item, original_x, original_y)
                self.canvas.itemconfig(item, fill="#60cafa")
        shake(0)
    if self.mistakes >= MAX_MISTAKES:
        self.state = "OVER"
        self.root.after(400, self.show_result)

说明:
失误次数加 1,并刷新界面。
箭头变红,通过 offsets 列表在水平方向左右移动,形成抖动效果。
抖动结束后恢复颜色和位置。
若失误达到上限,切换为失败状态并显示结果界面。

五、AIGC 使用过程

子任务 借助何种 AIGC 技术 AI 实现或提供了什么 效果如何 人工修改
路径检测 DeepSeek 生成四方向检测思路 基本正确,但边界条件需明确 改为 while 0 <= nr < GRID_N
关卡设计 DeepSeek 生成 3 个关卡数组 部分布局可能卡死 手工试玩并调整箭头位置
碰撞动画 DeepSeek 提供 Tkinter 抖动代码 可用 调整抖动幅度和延时
界面布局 DeepSeek 建议 Frame 切换方案 结构清晰 改为三个 Frame 切换
Bug 修复 DeepSeek 分析飞出后坐标越界 定位到动画步长 限制步数并删除 item

六、测试结果

编号 测试内容 预期结果 实际结果 是否通过
T01 点击前方无阻挡的箭头 箭头飞出并消失 箭头飞出并消失 通过
T02 点击前方有阻挡的箭头 箭头不消失,失误减 1 箭头抖动变红,失误减 1 通过
T03 点击边缘且朝外的箭头 正常消失,无越界错误 正常消失 通过
T04 消除本关全部箭头 显示通关并进入下一关 显示通关,可点下一关 通过
T05 失误次数耗尽 显示失败并允许重新开始 显示失败,可重玩 通过
T06 游戏进行中重新开始 布局和失误恢复 恢复初始状态 通过

七、PSP 表格

任务 预估耗时(小时) 实际耗时(小时) 差异(小时)
需求分析与游戏设计 1 0.5 0.5
Python 与图形库学习 2 1 1
游戏界面实现 2 0.2 1.8
路径与碰撞逻辑实现 2 3 1
关卡设计 1 0.2 0.8
AIGC 辅助开发 2 4 2
测试与修改 1.5 2 0.5
README 与博客撰写 2 0.5 1.5
合计 13.5 11.4 2.1

八、心得体会

  • AI 在生成界面框架、方向检测、动画代码方面效率很高;
  • 但 AI 给出的边界条件、关卡可解性需要自己验证;
  • 通过实际试玩发现并修改了若干问题;
  • 学会了把游戏状态、棋盘数据、界面渲染分开管理;
  • 理解了路径检测的本质是沿方向逐格检查。
posted on 2026-09-21 22:32  蔡以恒  阅读(5)  评论(0)    收藏  举报