软件工程第二次个人作业

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

0.项目展示

开始界面

游戏过程

成功界面

失败界面

游戏展示视频我放在百度网盘了,博文导入不了mp4 QwQ
链接: https://pan.baidu.com/s/1o4-AU23duTQ847k3cH9-7Q?pwd=gxwq 提取码: gxwq

1.项目介绍

(一)游戏规则
·《飞箭》游戏中存在上、下、左、右四个方向的箭头,玩家通过让一个一个箭头飞出棋盘,清空棋盘上的箭头来取得游戏胜利。
· 当箭头飞出方向上有其他未被点击飞出的箭头阻挡时,箭头无法飞出,且点击后会消耗失误次数。
· 每一关存在3次失误机会,失误机会耗尽后将会失败,并重新开始该关卡。
· 随着关卡推进,棋盘会变大,箭头数量会变多,在后三关还会出现倒计时。
(二)界面设计及主要功能
· 游戏开始界面采用GPT生成的休闲卡通界面,与背景音乐一起整体轻松活泼,在中间有游戏提示,正下方提供了开始菜单,可以通过点击“开始游戏”、“选择关卡”、“成就”和“设置”跳转至不同界面。
· 游戏关卡选择界面以三个关卡为一行,共有六个关卡可供玩家选择游玩,点击后即可进入对应关卡。
· 游戏成就关卡共提供五个成就,每达成一个成就可以点亮该成就前面的方框。
· 游戏设置界面提供了可调节音乐和音效大小的滑杆,点击开始音乐和开始音效前的方框可以打开和关闭音乐和音效。
· 游戏界面左上角HUD显示关卡名称,失误次数和剩余箭头,在有时间限制的关卡还会显示倒计时,右上角提供重新开始按钮和返回主菜单按钮,右下角提供提示按钮,可在点击后获取下一步提示。
(三)主要特色
· 添加了成就系统(因为免费版的GPT生成图片数量有限导致现在还没生成合适的成就图片,暂且沿用原来的图形背景)
· 拥有独特的音效和音乐功能,且可在设置中选择打开和关闭并调节大小(同上)

2.实现思路

接下来我将从结构设计、游戏机制实现等角度来介绍这个游戏

  1. 结构设计
    看完作业要求之后,我先试玩了微信小游戏,并且大致规划了一下我都想要实现什么功能,为了方便自己修改管理和让AI核查代码,我将整个文件分成了如下结构
    ArrowArrow
    -image(存放按钮及背景的png图片的文件夹)
    -music(存放音效和音乐的文件夹)
    -main.py(主程序)
    -ui.py(通用配置,例如字体,按钮的悬停放大等功能的实现)
    -config.py(全局常量管理)
    -game_screen.py(调整游戏界面)
    -start_screen.py(开始界面)
    -level_select.py(控制关卡选择界面)
    -achievements.py(控制成就界面)
    -setting.py(控制设置界面)
  2. 游戏机制实现
    关卡生成机制:我采用的是直接随机生成,然后验证是否可解,不可解重新生成的暴力方法,主要通过以下几个函数实现:
     def create_once():
        arrows = []
        cells = [ 
            (r, c)
            for r in range(grid_size)
            for c in range(grid_size)
        ]
        random.shuffle(cells)
        for i in range(min(arrow_count, len(cells))):
            r, c = cells[i]
            d = random.choice(directions)
            arrows.append(
                (r, c, d)
            )
        return arrows

该函数通过cells列出棋盘上的点并打乱,随后随机选取给定数量的点并放置箭头,通过随机选择箭头方向模拟关卡生成,最后返回这个保存的关卡

    def path_clear(target, current):
        r, c, d = target
        dr, dc = DIRS[d]
        r += dr
        c += dc
        while (0 <= r < grid_size and 0 <= c < grid_size):
            for ar, ac, ad in current:
                if ar == r and ac == c:
                    return False
            r += dr
            c += dc
        return True

该函数通过模拟检测箭头飞出路径,判断在飞出棋盘前是否受到阻碍,如果没有阻碍便判断箭头是可以飞出的

    def can_finish(test_arrows):
        remaining = test_arrows.copy()
        while remaining:
            found = False
            for arrow in remaining:
                if path_clear(
                    arrow,
                    remaining
                ):
                    remaining.remove(arrow)
                    found = True
                    break
            if not found:
                return False
        return True

该函数通过一轮一轮迭代,判断每一轮是否都有棋盘上的箭头能飞出,直到棋盘清空,判断关卡是否存在可行解
通过上述三个箭头的循环,就能得到必然有可行解的关卡,虽然暴力生成的复杂度可能较大,但是棋盘大小和箭头数量又保证了该方法在游戏中使用的可行性。
箭头路径判断及飞出:我沿用了上面的path_clear()并在其基础上进行了修改,实现了箭头飞出路径的判断,再在函数click_cell函数中调用并计算飞出距离,并调用成就中对飞出箭头数量的统计,用GameArrow中的updata中的代码实现箭头的移动(最初用DeepSeek生成的判断代码出现了所有方向全部顺时针多了90°的问题,于是我先在PyCharm的终端加入print语句输出他飞出路径的判断情况,然后发现了判断代码的问题并修正)。

        if self.state == "flying":
            #self.anim_t += dt * 6
            dr, dc = DIRS[self.direction]
            distance = self.fly_speed * dt
            self.offset[0] += dc * distance
            self.offset[1] += dr * distance
            self.anim_t += distance
            if self.anim_t >= self.fly_distance:
                self.state = "gone"

箭头方向:
在game_screen.py文件中用class _GameArrow表示

点击查看代码
class _GameArrow:
    def __init__(self, row, col, direction):
        self.row = row
        self.col = col
        self.direction = direction
        self.state = "idle"
        self.anim_t = 0.0
        self.offset = [0.0, 0.0]
        self.blocked_timer = 0.0
        self.fly_distance = 0
        self.fly_speed = 700

    def start_flying(self, distance):
        self.state = "flying"
        self.anim_t = 0.0
        self.fly_distance = distance

    def start_blocked(self):
        self.state = "blocked"
        self.blocked_timer = 0.0

    def update(self, dt):
        if self.state == "flying":
            #self.anim_t += dt * 6
            dr, dc = DIRS[self.direction]
            distance = self.fly_speed * dt
            self.offset[0] += dc * distance
            self.offset[1] += dr * distance
            self.anim_t += distance
            if self.anim_t >= self.fly_distance:
                self.state = "gone"
        elif self.state == "blocked":
            self.blocked_timer += dt
            shake = math.sin(self.blocked_timer * 30) * 6
            dr, dc = DIRS[self.direction]
            self.offset[0] = -dc * shake
            self.offset[1] = dr * shake
            if self.blocked_timer > 0.4:
                self.state = "idle"
                self.offset = [0.0, 0.0]
**关卡如何表示:** 使用level_select.py来选择关卡,关卡界面的棋盘及其他使用了class _GameScreen表示
点击查看代码
    def __init__(self, level_index, on_back, achievement_manager=None, settings_manager=None):
        self.background = pygame.image.load("image/game_background.png").convert()
        self.level_index = level_index
        self.fail_sound = pygame.mixer.Sound("music/ngm.mp3")
        self.fail_sound.set_volume(0.8)
        self.fail_sound_played = False
        self.mistake_sound = pygame.mixer.Sound("music/ikun.flac")
        self.mistake_sound.set_volume(0.8)
        self.time_limit = LEVELS[level_index].get("time_limit",None)
        self.remaining_time = self.time_limit
        self.on_back = on_back
        self.ach = achievement_manager or AchievementManager()
        self.settings_manager = settings_manager
        self.load_level(level_index)
        self.restart_btn = Button("重新开始", (WIDTH - 90, 50), (160, 120), self.restart,image="image/restart.png")
        self.back_btn = Button("返回主菜单", (WIDTH - 90, 120), (160, 120), self.on_back,image="image/back_menu.png")
        self.tip_btn = Button( "提示", (WIDTH - 90, HEIGHT - 80), (130, 80),  self.show_tip,image="image/tip.png")
        self.tip_arrow = None  # 当前提示的箭头
        self.hud_font = get_font(18)
        self.sub_font = get_font(22)
        self.unlock_toast_timer = 0.0   # 剩余总时间
        self.unlock_toast_text = ""
        self.unlock_toast_duration = 2.5  # 总时长
        self.unlock_toast_slide = 0.0   #滑入进度,范围在0-1
        self.tip_arrow = None
        self.tip_timer = 0

有点长就不全部放了

3.AIGC使用过程

子任务 借助何种 AIGC 技术 AI 实现或提供了什么 效果如何 人工修改
关卡生成 DeepSeek 以逆序方式生成可将所有箭头飞出的关卡 不仅箭头无法按照游戏规则正确飞出,设计出的关卡也会出现死局 通过在碰撞检测部分加入检测标记语句,修改判断方向,更换AIGC重新生成保证关卡可解
关卡生成 ChatGPT 以逆序方式生成可将所有箭头飞出的关卡 基本可用
路径检测 DeepSeek 生成判断箭头是否可正确飞出的检测代码 判断方向错误 改正判断方向后再改正飞出方向
跳转按钮绘制 ChatGPT 生成各项功能跳转按钮 基本可用 调整大小尺寸位置
背景及按钮图片生成 ChatGPT 生成按钮和背景的图片 调教过后生成效果不错,但是额度限制只能将一部分替换成精良图片 调整大小尺寸位置,优化生成图片

4.测试结果

编号 测试内容 预期结果 是否通过
T01 点击开始游戏 跳转至游戏界面 Y
T02 点击选择关卡 跳转至关卡选择界面 Y
T03 点击成就 跳转至成就界面 Y
T04 点击设置 弹出设置界面 Y
T05 点击前方无阻挡的箭头 箭头飞出棋盘并消失 Y
T06 点击前方有阻挡的箭头 箭头不消失且变红,失误次数减 1 ,发出失误音效 Y
T07 点击位于边缘且朝向棋盘外的箭头 箭头正常消失,不发生越界错误 Y
T08 消除本关全部箭头 显示通关并进入下一关 Y
T09 失误次数耗尽 显示失败并允许重新开始,播放失败音效 Y
T10 游戏进行中重新开始 箭头布局和失误次数恢复 Y
T11 点击提示按钮 提示箭头变绿且点击箭头可以成功飞出 Y
T12 点击返回主菜单按钮 返回开始界面 Y
T13 点击任意关卡 进入游戏界面且关卡正确 Y
T14 点击返回按钮 返回开始界面 Y
T15 点击开启音效 开启音效 Y
T16 点击开启音乐 开启音乐 Y
T17 拉动音乐大小划杠 音乐大小正确变化 Y
T18 拉动音效大小划杠 音效大小正确变化 Y
T19 等待游戏倒计时结束 显示失败并允许重新开始,播放失败音效 Y

经过最终测试,游戏各项功能已基本实现且没有出现问题,达到预期目标。

5.PSP表格

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

据统计,实际完成时间比预估时间多了4个小时,主要增加的时间主要在修改关卡生成上,因为刚开始错误的生成逻辑导致出现无解关卡和箭头判断错误,花费了大量时间修正,同时发现ChatGPT可以生成图片,于是又用图片改善了各个界面。

6.心得体会

相较于新建文件夹时对能否完成一个让我满意的小游戏我的担忧,对目前制作的成品我还是比较满意的,在过程中不断训练AI,理解并调整AI生成的代码最终打磨出来这个成品,我无疑是开心的,也体验到了AICG在辅助编程方面的巨大能力。很惊喜的发现了GPT的生图功能,使我能够用图片来填充背景,生成更加美观的按键,虽然由于免费版的额度限制导致部分界面的图片还没有填充,但是接下来我还是会等有额度之后完善其他界面使其更加美观成为一个超级完善的小游戏。这也是我第一次自己制作小游戏,感谢软件工程这个课程给我这个机会,在制作过程体会到了游戏制作的乐趣(在制作的过程中不断调试修正bug并体验自己做的游戏的play过程)同时也可以看到ArrowArrow是我在仓库中添加的文件夹Game底下的子文件夹,说不定以后还有别的游戏会在Game文件夹底下出现(doge),等整个项目完善之后我也会将其打包成exe文件,最后再在这里再感谢GPT和DeepSeek对本项目的大力支持。

posted @ 2026-09-22 23:11  Rophocale_KBlue  阅读(12)  评论(0)    收藏  举报