基于Qt实现的RPG模式连连看[2023-09-29]

基于Qt实现的RPG模式连连看[2023-09-29]

QLink(连连看)

在连连看游戏中,会有⼀个地图,地图上有许多不同种类的⽅块,通过将相同种类的两个⽅块相连,可以将这两个⽅块消除,⽤户获得分数。
在整个连连看的过程中,除了处理⽤户的操作之外,还有⼏个⽐较特殊的部分特别需要注意:

  • 随机地图的⽣成
  • 判断两个⽅块是否可以通过两次以内的折线进⾏连接
  • 判断剩余⽅块是否还有解
    除此之外,具体的功能要求如下:

源码联系UP主 -> https://space.bilibili.com/329101171

RPG 机制

不同于传统的连连看,我们的 QLink 使⽤ RPG 模式进⾏,即玩家需要控制⼀个⻆⾊在地图的空地上移动(⻆⾊显示可⾃⾏选择)。
激活:当⻆⾊处于⽅块旁且再次向⽅块⽅向移动,会激活该⽅块(请使⽤某种⽅式表示该⽅块被激活)。
消除:如果此次激活的⽅块和上次激活的⽅块是同种类,且可以通过两次以内的折线连接,则该两个⽅块被消除,玩家获得分数。(请绘制出将两个⽅块连接在⼀起的折线) 否则,上次激活的⽅块被⾃动变为未激活状态,换句话说,每个⻆⾊在地图中只有 0 个(游戏刚开始时,或刚刚消除完⼀对⽅块时)或者 1 个激活的⽅块。

计分

不同种类的⽅块可以有不同的分值,具体规则可以⾃⾏制定。界⾯中应时刻显示玩家的分数。

倒计时和游戏结束
有两个情况可以导致游戏结束:

  1. 倒计时结束;
  2. 没有可消除的⽅块对(所有⽅块均被消除也属于这⼀种)。
    界⾯中应时刻显示游戏的倒计时。

开始菜单

⾄少包括以下按钮:
开始新游戏
可选择游戏模式:单⼈模式、双⼈模式(具体看后⽂)
载⼊游戏
退出游戏

单⼈模式

游戏开始时,会随机⽣成地图,并随机玩家⻆⾊位置。随后玩家可控制⻆⾊移动,以激活和消除⽅块。

道具

道具通过随机⽅式出现在地图的空地上,当⻆⾊与道具出现在同⼀位置时,该⻆⾊触发道具效果,道具消失。
+1s:延⻓剩余时间 30s
Shuffle:所有⽅块位置重排
Hint:10s 内会⾼亮⼀对可能链接的⽅块,被消除后会⾼亮下⼀对,直到 10s 时间结束
Flash:5s 内允许通过⿏标单击移动⻆⾊位置,⻆⾊移动到的位置必须通过空地可到达,否则点击不产⽣任何
效果。如果点击到⽅块,且⻆⾊可以移动到该⽅块旁,则⻆⾊移动到该⽅块旁,且该⽅块被激活。如果⽅块四
周有多个位置可以让⻆⾊停留,则⻆⾊移动到其中任何⼀个位置均可。

双⼈模式

两个玩家的两个⻆⾊在相同的地图上进⾏游戏,以结束游戏时双⽅的分数决定谁为赢家。

道具

在单⼈模式的基础上,增加:
Freeze:对⼿ 3s 内⽆法移动
Dizzy:对⼿ 10s 内移动⽅向颠倒(上下左右颠倒)
此外,
Hint 道具的效果对两个玩家均可⻅;
+1s 道具的效果对两个玩家均有效。
双⼈模式下,没有 Flash 道具
暂停和存档
在暂停时,可以保存游戏(Save)和载⼊游戏(Load)
保存游戏会将当前游戏的所有状态以任意格式保存到磁盘上的⽂件
载⼊游戏时,读取⽂件,并从中恢复状态

源码联系UP主 -> https://space.bilibili.com/329101171

开发环境

  • 语言:C++
  • 框架:Qt 5.12

玩家模式选择

模式选择.png

单人模式演示

单人模式.gif

双人模式演示

双人模式.gif

重新排列演示

重新排列.gif

存档和读档演示

存档和读档.gif

分阶段任务

Step 1: 地图⽣成和展示
⽣成⼀个M*N的地图
M和N可以⾃定义
⽣成不同类型的箱⼦,并在界⾯上显示出类型的不同
实现⾄少3种类型的箱⼦
Step 2: ⼩⼈能在地图上移动,并能与边缘的block交互
⼩⼈能在地图上⾃由移动
⼩⼈能够对箱⼦进⾏操作,操作成功后界⾯上要有反馈
⼩⼈对箱⼦的操作包括:靠近,选定
Step 3: 选中⽅块后的判定逻辑
将判定逻辑单独作为⼀个函数编写
单元测试要求:对判定逻辑的函数,需要额外编写的单元测试,对正常运⾏情况和边界情况都进⾏测试
单元测试的样例在⽂档末尾给出
每次⼩⼈选中2个⽅块时,运⾏判定函数,输出判定结果,显示到界⾯上
判定结果包括:不可消除,消除
若消除,则将对应箱⼦从地图中删去
每次⽅块消除后,都要判断当前地图是否可解,若⽆解则显示在界⾯上,并使得游戏结束
Step 4: 时间和分数
在地图上显示倒计时,并实时更新
若倒计时完成,则显示游戏结束
Step 5: 道具
先实现+1s道具
随机在地图上⽣成道具
判断⼈物是否接触到道具
实现其他道具
Shuffle 必须实现,Hint 和 Flash ⾄少选择⼀个实现
Hint:遍历整个地图上现存的⽅块,找到⼀对能消除的⽅块⾼亮,消除后找到并⾼亮下⼀对,直到持续
时间结束
Flash:⿏标单击触发⻆⾊位置变化,⻆⾊位置定位到⿏标点击的位置
Step 6: 暂停和存档
暂停
暂停游戏时间,不允许玩家操作
存档
将游戏状态输出⾄⽂件,如⼈物位置、剩余时间、剩余⽅块等。并且可以读取这个⽂件,恢复游戏状态
Step 7: 实现双⼈模式
实现双⼈模式
在游戏中添加第⼆个⻆⾊,使⽤不同按键操作,其他逻辑相同
注:将⻆⾊实现成⼀个类会让代码更简洁。两个⻆⾊就对应类的两个实例
双⼈模式下的道具为可选项(不计如最终分数)
Step 8: 开始菜单
制作⼀个开始菜单
可以是任意形式
⼏个按钮
⼀个列表包含多个选项
⼀个单独的窗⼝
和游戏属于同⼀个窗⼝的不同状态

评分标准
评分标准
以上 8 个步骤,每个 10 分
此外,单元测试占 10 分,代码质量(⻛格和注释等)占 10 分
附录1:测试⽤例样例
Step1
创建测试类头⽂件.h
新建的测试类 SimpleTest ,必须要继承 QObject ,才能在测试的时候调⽤QT提供的测试框架
Step2
创建simpletest.h的实现.cpp⽂件
通过调⽤ QVERIFY 函数来测试⾃⼰的⽬标函数,其中⽤绿⾊的框圈出来的是预期的正确结果(⼀般可以通过⼿算来获取,⼀定要保证是正确的),等于号左侧是测试⽬标函数。
QTest_Main 是QT⾃带测试框架中所要求的⼊⼝函数,功能相当于 main()
上图来说,add是⼀个实现正确的,⽬标为加⼀的函数;addError是⼀个实现错误的,⽬标为加⼀的函数,运⾏的结果如下所示:
PS: 如果项⽬启动出错的话,⽐较⼀下⾃⼰的.pro⽂件和助教的.pro⽂件配置,看看有没有缺少的内容
什么是好的测试
⼀个测试应该覆盖正常的执⾏流程以及corner case(也就是异常处理)。以除法为例,假设我们需要⼀个除法divide(a,b)
那么测试⽤例应该包括两个部分:

  1. 正常的执⾏流程
    e.g. QVERIFY(divide(2,1)==2)
  2. 异常处理
    e.g. QVERIFY(divide(2,0)==NAN)
    ⼀个优秀的测试尤其需要注意异常情况的处理。
    此外正常执⾏流程中的每⼀个逻辑分⽀都应该尽量被覆盖到,⽐如
    若有 if-else 的情况,应该两种执⾏路径都被测试到。
    如果想要更详细的测试,可以⾃⾏查询软件测试分类,尝试去满⾜以下类别下的测试:
  3. 语句覆盖【这⼀点⼀定需要做到,其余随意】 每个语句⾄少被执⾏⼀次。
  4. 判定覆盖(分⽀覆盖) 每个判定语句都⾄少取真⼀次、取假⼀次。
  5. 条件覆盖 每个条件⾄少有⼀次为真。
  6. 条件判定覆盖 每个判定语句都⾄少取真⼀次、取假⼀次。且每个条件⾄少有⼀次为真。
  7. 条件组合覆盖 条件的各种组合⾄少出现⼀次。 (全为真、全为假、⼀部分为真)
  8. 基本路径覆盖 所有可能的路径⾄少执⾏⼀次。 (真真、假假、真假、假真)
    附录2:QLink 代码规范
    下⾯是本项⽬中会被检查的代码规范,下⾯未列出的不做要求。
    命名规范
    所有命名使⽤英⽂单词(不要使⽤拼⾳)或其缩写,以及⼀些常⽤的简写变量。
    举例:

    类名:每个单词的⾸字⺟⼤写
    变量和函数名:使⽤驼峰格式
    除⾮特殊情况,否则头⽂件开头使⽤
    int counter = 0;
    int score;
    int i, j, k;
    int tmp;
    int cnt;
    int sum;
    class Point {
    int x;
    int y;
    };
    class TableElement {
    int numRows;
    int numCols; // Number of columns
    public:
    void setAtCell(int row, int col);
    void getAtCell(int row, int col);
    };
    #pragma once
    思考:这个是做什么⽤的?
    示例代码
    ⼀些具体的注意点
    ⼤括号位置:
    if、else、for、while 等 在同⼀⾏末尾
    函数定义末尾的⼤括号在下⼀⾏
    缩进:每⼀级 4个空格缩进
    每个两元运算符(即 X op Y 形式中的 op )左右各有空格,⽐如 + , >= , = 等
    所有不会被修改的参数需要按照const引⽤的⽅式传递 (int, double等基本类型可以直接传递,不需要使⽤const引⽤)
    例如下⽅示例中,DIYClass的⽤法
    函数头格式
    函数注释要能表示函数具体⾏为,要能涵盖每个参数的含义和作⽤,解释返回值的含义
    函数⻓度要求
    //
    // getScore 接受课程号(classId)作为参数,返回对应课程的分数
    // 未找到时返回-1
    //
    int Student::getScore(int classId, const vector &allClasses) const
    {
    if (classId < 0 || classId >= numClasses) {
    // ⽆效的课程 ID
    return 0;
    } else {
    vector::Iterator cl;
    // 遍历所有课程,找到特定class id的课程分数
    for (cl = allClasses.begin(); cl != allClasses.end(); ++cl) {
    if (cl->id == classId) {
    return cl->getScore(this);
    }
    }
    // 未找回该 class
    return -1;
    }
    }
    int testFunc(int param1, const DIYClass &class)
    {

    }
    函数⻓度要求
    原则上每个函数不超过 50 ⾏。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值