前言

前两章教程,我们使用WebSocket的基础特性打造了一个小小聊天室,并在第二章对其进行了集群化改造。

系列教程回顾:

在本文中,我将介绍如何使用WebSocket向实时多人答题对战游戏提供服务端,并详细介绍通接口的设计。

这是我在最近作业竞赛中设计的小项目,和小伙伴们一起设计了整个游戏流程和后端代码,前端页面暂时就不放开给大家了,大家可以参考前两章教程自己动手写一下前端页面。

本文内容摘要:

在线游戏常用的通讯方案

如何使用WebSocket实现游戏对战实时通信

游戏步骤的画面演示和对应的WebSocket接口设计

本文源码:(妈妈再也不用担心我无法复现文章代码啦)

正文

WebSocket实现在线多人游戏——对战答题

在线游戏常用的通讯方案

参考:

HTTP

优点:协议较成熟,应用广泛、基于TCP/IP,拥有TCP优点、研发成本很低,开发快速、开源软件较多,nginx,apache,tomact等

缺点:无状态无连接、只有PULL模式,不支持PUSH、数据报文较大

特性:基于TCP/IP应用层协议、无状态,无连接、支持C/S模式、适用于文本传输

TCP

优点:可靠性 、全双工协议、开源支持多、应用较广泛、面向连接、研发成本低、报文内容不限制(IP层自动分包,重传,不大于1452bytes)

缺点:操作系统:较耗内存,支持连接数有限、设计:协议较复杂,自定义应用层协议、网络:网络差情况下延迟较高、传输:效率低于UDP协议

特性:面向连接、可靠性、全双工协议、基于IP层、OSI参考模型位于传输层、适用于二进制传输

WebScoket

优点:协议较成熟、基于TCP/IP,拥有TCP优点、数据报文较小,包头非常小、面向连接,有状态协议、开源较多,开发较快

缺点:

特性:有状态,面向连接、数据报头较小、适用于WEB3.0,以及其他即时联网通讯

UDP

优点:操作系统:并发高,内存消耗较低、传输:效率高,网络延迟低、传输模型简单,研发成本低

缺点:协议不可靠、单向协议、开源支持少、报文内容有限,不能大于1464bytes、设计:协议设计较复杂、网络:网络差,而且丢数据报文

特性:无连接,不可靠,基于IP协议层,OSI参考模型位于传输层,最大努力交付,适用于二进制传输

总结

对于弱联网类游戏,必须消除类的,卡牌类的,可以直接HTTP协议,考虑安全的话直接HTTPS,或者对内容体做对称加密;

对于实时性,交互性要求较高,可以优先选择Websocket,其次TCP协议;

对于实时性要求极高,且可达性要求一般可以选择UDP协议;

局域网对战类,赛车类,直接来UDP协议吧;

WebSocket实现双人在线游戏实时通信

我们采用websocket作为我们的通信方案,主要是因为我们希望对战双方能够实时显示对方的得分。

本小节详细介绍了我们在线问答对战游戏中,具体的websocket通讯方式定义。

本问答游戏规则如下:

用户打开h5页面后,输入自己的昵称,发送给服务端,服务端将用户昵称保存到hashmap,并记录用户状态(空闲,游戏中),接着用户进入大厅。

大厅中用户可以互相选择,一旦某用户选择了另一位用户,将触发开始游戏,双方进入答题模式。

答题的两位用户各回答10题,每题答对为10分,共100分,左上角页面显示自己的分数,右上角显示对方分数,实时通过websocket接收对方分数。

10题结束,双方等待对方总分,最后判断输赢,显示结果界面。

所以我们需要设计三个WebSocket协议:

用户创建昵称,进入玩家大厅

用户选择对手,双方进入游戏

对战过程实时显示双方分数

接下来详细介绍这三种WebSocket接口

用户创建昵称,进入玩家大厅

打开界面,进入游戏:

我们使用了HashMap存储用户状态,

private Map userToStatus = new HashMap<>();

用户状态分为空闲和游戏中:

public enum StatusEnum {

IDLE,

IN_GAME

}

WebSocket接口设计如下:

WebSocket接口代码如下:

@MessageMapping("/game.add_user")

@SendTo("/topic/game")

public MessageReply addUser(@Payload ChatMessage chatMessage, SimpMessageHeaderAccessor headerAccessor) throws JsonProcessingException {

MessageReply message = new MessageReply();

String sender = chatMessage.getSender();

ChatMessage result = new ChatMessage();

result.setType(MessageTypeEnum.ADD_USER);

result.setReceiver(Collections.singletonList(sender));

if (userToStatus.containsKey(sender)) {

message.setCode(201);

message.setStatus("该用户名已存在");

message.setChatMessage(result);

log.warn("addUser[" + sender + "]: " + message.toString());

} else {

result.setContent(mapper.writeValueAsString(userToStatus.keySet().stream().filter(k -> userToStatus.get(k).equals(StatusEnum.IDLE)).toArray()));

message.setCode(200);

message.setStatus("成功");

message.setChatMessage(result);

userToStatus.put(sender, StatusEnum.IDLE);

headerAccessor.getSessionAttributes().put("username",sender);

log.warn("addUser[" + sender + "]: " + message.toString());

}

return message;

}

用户选择对手,双方进入游戏

在大厅中选择玩家,随后会进入对战:

我们使用了HashMap存储了正在对战的用户,给双方配对。

private Map userToPlay = new HashMap<>();

WebSocket接口设计如下:

WebSocket接口代码如下:

@MessageMapping("/game.choose_user")

@SendTo("/topic/game")

public MessageReply chooseUser(@Payload ChatMessage chatMessage) throws JsonProcessingException {

MessageReply message = new MessageReply();

String receiver = chatMessage.getContent();

String sender = chatMessage.getSender();

ChatMessage result = new ChatMessage();

result.setType(MessageTypeEnum.CHOOSE_USER);

if (userToStatus.containsKey(receiver) && userToStatus.get(receiver).equals(StatusEnum.IDLE)) {

List list=new ArrayList<>();

questionService.getQuestions(limit).forEach(item->{

QuestionRelayDTO relayDTO=new QuestionRelayDTO();

relayDTO.setTopic_id(item.getId());

relayDTO.setTopic_name(item.getQuestion());

List answers=new ArrayList<>();

answers.add(new Answer(1,item.getId(),item.getOptionA(),item.getResult()==1?1:0));

answers.add(new Answer(2,item.getId(),item.getOptionB(),item.getResult()==2?1:0));

answers.add(new Answer(3,item.getId(),item.getOptionC(),item.getResult()==3?1:0));

answers.add(new Answer(4,item.getId(),item.getOptionD(),item.getResult()==4?1:0));

relayDTO.setTopic_answer(answers);

list.add(relayDTO);

});

result.setContent(mapper.writeValueAsString(list));

result.setReceiver(Arrays.asList(sender, receiver));

message.setCode(200);

message.setStatus("匹配成功");

message.setChatMessage(result);

userToStatus.put(receiver, StatusEnum.IN_GAME);

userToStatus.put(sender, StatusEnum.IN_GAME);

userToPlay.put(receiver,sender);

userToPlay.put(sender,receiver);

log.warn("chooseUser[" + sender + "," + receiver + "]: " + message.toString());

} else {

result.setContent(mapper.writeValueAsString(userToStatus.keySet().stream().filter(k -> userToStatus.get(k).equals(StatusEnum.IDLE)).toArray()));

result.setReceiver(Collections.singletonList(sender));

message.setCode(202);

message.setStatus("该用户不存在或已在游戏中");

message.setChatMessage(result);

log.warn("chooseUser[" + sender + "]: " + message.toString());

}

return message;

}

对战过程实时显示双方分数

对战过程中的演示图:左边显示我方分数,右边显示对方分数

WebSocket接口设计如下:

WebSocket接口代码如下:

@MessageMapping("/game.do_exam")

@SendTo("/topic/game")

public MessageReply doExam(@Payload ChatMessage chatMessage) throws JsonProcessingException {

MessageReply message = new MessageReply();

String sender = chatMessage.getSender();

String receiver = userToPlay.get(sender);

ChatMessage result = new ChatMessage();

result.setType(MessageTypeEnum.DO_EXAM);

log.warn("userToStatus:" + mapper.writeValueAsString(userToStatus));

if (userToStatus.containsKey(receiver) && userToStatus.get(receiver).equals(StatusEnum.IN_GAME)) {

result.setContent(chatMessage.getContent());

result.setSender(sender);

result.setReceiver(Collections.singletonList(receiver));

message.setCode(200);

message.setStatus("成功");

message.setChatMessage(result);

log.warn("doExam[" + receiver + "]: " + message.toString());

}else{

result.setReceiver(Collections.singletonList(sender));

message.setCode(203);

message.setStatus("该用户不存在或已退出游戏");

message.setChatMessage(result);

log.warn("doExam[" + sender + "]: " + message.toString());

}

return message;

}

进一步

这个只是个两天赶出来的Demo,当然里成品还有非常大的差距。这里有几个需要继续解决的事情:

实现自动匹配/排行榜

WebSocket通讯优化:在某些地方使用点对点通讯,而非全部使用广播通讯。

我们可以使用convertAndSendToUser()方法,按照名字就可以判断出来,convertAndSendToUser()方法能够让我们给特定用户发送消息。

spring webscoket能识别带”/user”的订阅路径并做出处理,例如,如果浏览器客户端,订阅了’/user/topic/greetings’这条路径,

stompClient.subscribe('/user/topic/greetings', function(data) {

//...

});

就会被spring websocket利用UserDestinationMessageHandler进行转化成”/topic/greetings-usererbgz2rq”,”usererbgz2rq”中,user是关键字,erbgz2rq是sessionid,这样子就把用户和订阅路径唯一的匹配起来了

参考文献

点对点通讯:

总结

我们在本文中实现了在线多人对战游戏的服务端WebSocket接口设计,进一步巩固了对WebSocket的基础和应用范围的理解。

本文工程源代码:

关注我

我目前是一名后端开发工程师。主要关注后端开发,数据安全,爬虫,边缘计算等方向。

微信:yangzd1102(请注明来意)

个人博客:

原创博客主要内容

Java知识点复习全手册

Leetcode算法题解析

剑指offer算法题解析

SpringCloud菜鸟入门实战系列

SpringBoot菜鸟入门实战系列

爬虫相关技术文章

后端开发相关技术文章

个人公众号:后端技术漫谈

如果文章对你有帮助,不妨收藏起来并转发给您的朋友们~

答题对战方案java_使用WebSocket实现实时多人答题对战游戏相关推荐

  1. .net 实时通信_【WebSocket】实时多人答题对战游戏

    本文公众号来源:后端技术漫谈 作者:蛮三刀把刀 前言 前两章教程,我们使用WebSocket的基础特性打造了一个小小聊天室,并在第二章对其进行了集群化改造. 系列教程回顾: 手把手搭建WebSocke ...

  2. [WebSocket]使用WebSocket实现实时多人答题对战游戏

    前言 前两章教程,我们使用WebSocket的基础特性打造了一个小小聊天室,并在第二章对其进行了集群化改造. 系列教程回顾: [WebSocket]第一章:手把手搭建WebSocket多人在线聊天室( ...

  3. vue项目中通过WebSocket实现实时消息提示及遇到的问题

    vue项目中通过WebSocket实现实时消息提示(前端代码) 需求说明 后台有新增消息通知,并实时推送给用户端,用websocket可以很方便的解决这个问题,但是websocket有个弊端不兼容IE ...

  4. unity3d-多人坦克对战

    这次的作业是在上一次坦克大战作业的基础上进行改进,实现多人网络对战版. 视频演示 https://www.bilibili.com/video/av25649733/ 游戏实现 游戏主要对象还是利用了 ...

  5. 微信小程序 | 基于小程序+Java+WebSocket实现实时聊天功能

    一.文章前言 此文主要实现在小程序内聊天对话功能,使用Java作为后端语言进行支持,界面友好,开发简单. 二.开发流程及工具准备 2.1.注册微信公众平台账号. 2.2.下载安装IntelliJ ID ...

  6. 一款集合了对战、策略、养成与一体的魔幻战术对战策略手游——第八秩序

    第八秩序是一款集合了对战.策略.养成与一体的魔幻战术对战策略手游,游戏画风精美魔幻,操作体验流畅,融入了精彩的剧情故事.在这里玩家可以感受到新奇冒险玩法带来的挑战冒险,游戏内的操作节奏都格外考验技术, ...

  7. 战滩斗水:一个长江航道人的风雨回忆

    1982年鸡扒子滩-裸露爆破施工 受访者供图 摄 1982年鸡扒子滩-裸露爆破施工 受访者供图 摄 中新网重庆1月17日电 题:战滩斗水:一个长江航道人的风雨回忆 作者 王淳熙 临近年末,长江重庆航道 ...

  8. 你在玩金铲铲之战,必须打倒敌人以强化自己的战斗力。这个游戏中有若干个 boss,你把所有boss 全部打败即完成游戏。打boss 的顺序可以自选。

    你在玩金铲铲之战,必须打倒敌人以强化自己的战斗力.这个游戏中有若干个 boss,你把所有boss 全部打败即完成游戏.打boss 的顺序可以自选.为了让你们计算起来更加轻松,我大大简化了每个 boss ...

  9. springboot集成webSocket实现实时推送

    springboot集成webSocket实现实时推送 webSocket实现推送 webSocket是什么? 需求说明 websocket集成步骤 pom.xml webSocket实现 自定义处理 ...

最新文章

  1. crontab 最小间隔_今天我间隔了:如何找到不在数组中的最小数字
  2. pyecharts 应用5:视觉映射配置项VisualMapOpts
  3. SpringBoot项目优化和Jvm调优(楼主亲测,真实有效)
  4. 关于Unity中NGUI的背包实现之Scrollview(基于Camera)
  5. 《Android/OPhone开发完全讲义》连载(7):使用SharedPreferences存取复杂数据
  6. 计算机多媒体应用软件有超媒体特点吗,《多媒体技术与应用》(本)阶段练习一...
  7. Linux-dns基础知识和BIND的简单配置-3(主从DNS服务器及转发)
  8. 列出一个工作簿中所有已使用的自定义函数
  9. Scratch 3.X中的部分错误汇总
  10. etc fstab 详解linux,Linux下/etc/fstab文件详解
  11. 川崎机器人示教盒维修_阳江市川崎机器人示教器维修中心
  12. 零基础Bootstrap入门教程(19)--下拉菜单、按钮式下拉菜单
  13. Spark Steaming流式日志过滤与分析
  14. 最新微信ipad协议 CODE获取 公众号授权等
  15. 【行人轨迹预测数据集——ETH、UCY】
  16. vue——初学Vue
  17. Particle Filter 粒子滤波
  18. Tkinter教程之Scrollbar篇
  19. jquery 滑块_如何使用jQuery创建动画图库(滑块工具)
  20. 树的前序遍历和后序遍历

热门文章

  1. pytorch采用GPU加速方法
  2. 《Miss Talk》第08期:对话桥吧英语联合创始人兼CTO 于双印
  3. echarts 横向条形图 对比
  4. android手势动画
  5. 关于playerunknown's battlegrounds中归零距离小谈
  6. Excel表格数据导入到Oracle
  7. iOS疯狂讲解之手势识别器
  8. MySQL中建表时的int(m)中的m代表什么意思?
  9. 主会场与分会场直播场景自由切换的实际应用效果
  10. 上网代理设置会被自动清空_关于代理被自动设置问题的排查