Skip to main content

Room 桌游类、房间类的实战

tip
房间模块 + 领域事件 + 内置 Kit = 轻松搞定桌游类游戏

介绍

现在,我们基于 Room + 领域事件做了一个实战。 整体比较简单,多名玩家在房间里猜拳(石头、剪刀、布)得分。

实战包括了前后端,前端使用 FXGL 引擎, 这样开发者在学习时只需 JDK 环境,而不需要安装其他游戏引擎的环境。

源码统计与功能介绍

An

基于 Room 模块的扩展,只用数百行代码就完成了下述功能

  1. 登录
  2. 玩家进入大厅(地图)
  3. 玩家可在大厅移动
  4. 玩家移动时相互可见
  5. 玩家离开大厅(玩家下线)
  6. 查询房间列表
  7. 房间信息实时变更通知(房间内有玩家数量变化,等待中、游戏中 ...等状态)
  8. 玩家创建房间
  9. 玩家进入房间
  10. 玩家退出房间
  11. 解散房间
  12. 玩家准备
  13. 开始游戏
  14. 玩家在房间内的玩法操作
  15. 及各种验证,断言 + 异常机制 = 清晰简洁的代码

Room 模块很好的帮助开发者屏蔽这些重复性的工作, 并可为项目中的功能模块结构、开发流程等进行清晰的组织定义,减少了后续的项目维护成本。 更重要的是有相关文档,将来当你的团队有新进成员时,可以快速的上手。

图文展示

大厅

启动游戏后玩家会将加入大厅(类似地图),多名玩家相互可见,并且玩家可以在大厅内移动。

An

创建房间

[玩家1] 创建一个房间并加入。

大厅的玩家可以看见当前的房间列表信息,房间列表信息会实时的变化,比如游戏开始后会由等待中变成游戏中。 房间内的玩家数有变更时,也会实时的更新。

玩家可以创建一个房间,等待其他玩家的加入,也可以加入一个已经存在的房间与其他玩家共同游戏。 An

玩家在房间内

图中,两个玩家在同一房间内,房主可操作的按钮为退出房间和开始游戏;其他玩家则为退出房间和准备。 An

准备

玩家准备时,按钮则变为取消准备。同时,玩家的准备信息将实时的更新到左侧的面板中。 An

游戏开始

游戏开始后,每个玩家可以选择要出的(石头、剪刀、布),选择好后会显示到左边的面板中。 右边是当前房间内的得分排行,得分最高的排在前。

游戏时,每局有一定的时间供玩家选择,倒计时结束后会公布结算结果。 如果倒计时结束玩家没有选择,那么服务器会强制帮未操作的玩家做出随机选择。 An An

游戏结束

游戏结束后,玩家可以离开房间。 An

获取源码

符合以下条件的可通过 Email 免费获取

  • 已经购买了 回合制网络游戏 MMO 永久产品的开发者。
  • 购买了闭源授权的企业。
  • 在 2024 年 5 月前有过捐赠行为(至少为 1 星)的支持者。(需要发送捐赠截图)

单独购买

该实战源码支持单独购买 998元。 有效期: 永久。

QQ: 262610965

备注: 购买 Room 实战。

部分源码展示

玩法操作

// 验证与实际业务逻辑分离
// 将玩家的选择(石头、剪刀、布)广播给房间内的其他玩家。
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);

var command = context.getCommand();
item.setElementIndex(command.elementIndex);

// 房间内广播 - 有玩家做了选择
MyBroadcastKit.operation(userId, room);
}
}

操作扩展配置

public final class MyOperationRunner implements Runner {
@Override
public void onStart(BarSkeleton skeleton) {
var factory = FightRoomService.me().getOperationFactory();
// ------ mapping internal operation ------
// 进入房间、准备、退出房间、开始游戏
factory.mapping(InternalOperationEnum.enterRoom, new EnterRoomOperationHandler());
factory.mapping(InternalOperationEnum.ready, new ReadyOperationHandler());
factory.mapping(InternalOperationEnum.quitRoom, new QuitRoomOperationHandler());
factory.mapping(InternalOperationEnum.startGame, new StartGameOperationHandler());

// ------ mapping user operation ------
factory.mappingUser(FightOperation.choose, new ChooseOperationHandler());
}
}