[light-game-room] 重构 room 扩展模块 - 适用桌游类的游戏
iohao opened this issue · comments
计划重构 room 模块,简化相关 api 的使用,并提供相关文档。在此之前,由于该模块较偏特定业务,且示例流程涉及较多,所以一直没有提供相关文档与示例。
room 模块比较适合桌游类的游戏基础搭建,基于该模型可以做一些如,炉石传说、三国杀、斗地主、麻将 ...等类似的桌游。或者说只要是房间类的游戏,该模型都适用。比如,CS、泡泡堂、飞行棋、坦克大战 ...等。
简介 - light-game-room 房间
light-game-room 房间,是 ioGame 提供的一个轻量小部件 - 可按需选择的模块。
该模块的主要职责有三个方面,分别是房间、玩家、操作。
- 房间相关的包括,房间的创建、房间的管理及对房间内玩家的管理(如玩家退出房间、加入房间,控制房间内的人数 ...等)。
- 玩家方面相关的包括,玩家的信息及在房间内所持有的资源(资源指的是玩家所持有的子弹、牌、道具 ...等,需根据游戏来确定)。
- 操作方面相关,由于不同游戏之间的具体操作是不相同的,如坦克的射击,飞行棋的投骰子行走,炉石的战前选牌、出牌,麻将的吃、碰、杠、过、胡,回合制游戏的普攻、防御、技能 ...等。上述这些不同的操作,我们可以通过扩展 OperationHandler 接口,来处理具体的业务玩法;同时这种扩展方式更符合单一职责,使得我们后续的扩展与维护成本更低。
基于该模块的扩展,可以减少大量的重复工作,基本只需关注自身游戏的特定业务。
桌游戏类游戏的大致流程
- 房间创建
- 玩家创建
- 进入房间
- 退出房间
- 解散房间
- 游戏开始
游戏开始后,就是房间内的各种操作了,即上面提到的,如坦克的射击,飞行棋的投骰子行走,炉石的战前选牌、出牌,麻将的吃、碰、杠、过、胡,回合制游戏的普攻、防御、技能 ...等。
该模块是一个较综合的实战模块,通常在制作一个游戏时,至少可能涉及以下几个点
- TaskKit (yuque.com) - 是一个任务、时间、延时监听、超时监听...等相结合的一个工具模块。
- 属性监听 (yuque.com) - 属性变更触发的事件
- 动态属性 (yuque.com) - 扩展对象属性
- domain-event 领域事件 (yuque.com) 规避房间内的多人操作时的并发问题
后续计划做一个相关实战的 demo,将包括前后端。前端计划使用 FXGL 引擎,这样开发者在学习时,只需 JDK 环境就可以了,而不需要安装更多的环境。
重构之后,将提供 room 模块的详细使用文档。
相关文档: room 桌游、房间类 (yuque.com)
基于 room 模块的实战简介
现在,我们基于该 room 模块做一个实战示例,该示例整体比较简单,多名玩家在房间里猜拳(石头、剪刀、布)得分。
实战示例包括了前后端,前端使用 FXGL 引擎,这样开发者在学习时,只需 JDK 环境就可以了,而不需要安装更多的环境。
源码统计与功能介绍
基于 room 模块的扩展,只需要数百行代码就完成了该实战示例,包含的功能如下:
- 登录
- 玩家进入大厅(地图)
- 玩家可在大厅移动
- 玩家移动时相互可见
- 玩家离开大厅(玩家下线)
- 查询房间列表
- 房间信息实时变更通知(房间内有玩家数量变化,等待中、游戏中 ...等状态)
- 玩家创建房间
- 玩家进入房间
- 玩家退出房间
- 解散房间
- 玩家准备
- 开始游戏
- 玩家在房间内的玩法操作
- 对接文档生成..等
正如开头介绍所说的,room 模块很好的帮助开发者屏蔽这些重复性的工作,并可为项目中的功能模块结构、开发流程等进行清晰的组织定义,减少了后续的项目维护成本。更重要的是有相关文档,将来当你的团队有新进成员时,可以快速的上手。
大厅
启动游戏后玩家会将加入大厅(类似地图),多名玩家相互可见,并且玩家可以在大厅内移动。
图中左边是玩家的可移动区域,右边房间列表信息。
大厅的玩家可以看见当前的房间列表信息,房间列表信息会实时的变化,比如游戏开始后,会由等待中变成游戏中。房间内的玩家数有变更时,也会实时的更新。
玩家可以创建一个房间,等待其他玩家的加入,也可以加入一个已经存在的房间与其他玩家共同游戏。
准备、游戏开始
游戏开始后,每个玩家可以选择要出的(石头、剪刀、布),选择好后会显示到左边的面板中;右边是当前房间内的得分排行,得分最高的排在前。
游戏时,每局有一定的时间供玩家选择,倒计时结束后会公布结算结果。如果倒计时结束玩家没有选择,那么服务器会强制帮未操作的玩家做出随机选择。
游戏结束
游戏结束后,玩家可以离开房间。
小结、扩展玩法操作
现在我们知道了,基于 room 模块的扩展,可以帮助开发者屏蔽大量重复性的工作,减少大量的代码。并可为项目中的功能模块结构、开发流程等进行清晰的组织定义,减少了后续的项目维护成本。
此外,该模型还可以帮助我们在编码时,减少心智上的负担。以玩家的操作玩法为例,下面是我们扩展的一个操作玩法,即选择(石头、剪刀、布)其中一个。
该扩展将验证与业务逻辑分离,这样可以使得我们的代码更加的清晰。
代码之所以清晰,是因为我们的验证使用了断言机制(断言 + 异常机制 = 清晰简洁的代码)。传统的框架需要开发者疯狂 if else,且每次需要手动手动传输错误码,相关讨论可阅读传统写法或祖传代码写法。
// 验证与实际业务逻辑分离
// 将玩家的选择(石头、剪刀、布)广播给房间内的其他玩家。
public class ChooseOperationHandler implements OperationHandler {
@Override
public void verify(PlayerOperationContext context) {
// 玩家操作的数据
FightOperationCommand command = context.getCommand();
int elementIndex = command.elementIndex;
// 验证玩家的操作数据是否合法(石头、剪刀、布)
GameCode.illegalOperation.assertTrue(FiveElementKit.verify(elementIndex));
}
@Override
public void process(PlayerOperationContext context) {
FightRoomEntity room = context.getRoom();
long userId = context.getUserId();
// 将玩家的元素选择(石头、剪刀、布)保存到当前对局中
var fightRound = room.getCurrentFightRound();
var item = fightRound.getItemByUserId(userId);
FightOperationCommand command = context.getCommand();
item.setElementIndex(command.elementIndex);
// 房间内广播 - 有玩家做了选择
room.ofRangeBroadcast()
.setResponseMessage(RoomCmd.of(RoomCmd.operationBroadcast), LongValue.of(userId))
.execute();
}
}