知识 分享 互助 懒人建站

    懒人建站专注于网页素材下载,提供网站模板、网页设计、ps素材、图片素材等,服务于【个人站长】【网页设计师】和【web开发从业者】的代码素材与设计素材网站。

    懒人建站提供网页素材下载、网站模板
    知识 分享 互助!

    微信小程序利用websocket制作多人版扫雷小游戏

    作者:速博娱乐是什么 来源:jquery插件 2016-12-04 人气:
    微信小程序利用websocket制作多人版扫雷小游戏,当然这主要是练习使用微信小程序中的websocket和小游戏制作思路,微信小程序不允许发布游戏类应用。
    微信小程序利用websocket制作多人版扫雷小游戏,当然这主要是练习使用微信小程序中的websocket和小游戏制作思路,微信小程序不允许发布游戏类应用。

    为什么需要websocket?

    传统的实时交互的游戏,或服务器主动发送消息的行为(如推送服务),如果想做在微信上,可能你会使用轮询的方式进行,不过这太消耗资源,大量的请求也加重了服务器的负担,而且延迟问题比较严重。如果是自己开发的app,为了解决这些问题,很多团队会自建socket,使用tcp长链接、自定协议的方式与服务器进行相对实时的数据交互。有能力的团队,采用这种方式自然没什么大问题。不过小团队可能就要花费很多时间去调试,要解决很多难题,这个在成本上就划不来。
    H5引入了webSocket来解决网页端的长链接问题,而微信小程序也支持websocket。这是一个非常重要的特性,所以本系列的文章会专门拿出一篇来讨论websocket。

    webSocket本质上也是TCP连接,它提供全双工的数据传输。一方面可以避免轮询带来的连接频繁建立与断开的性能损耗,另一方面数据可以是比较实时的进行双向传输(因为是长链接),而且WebSocket允许跨域通信(这里有个潜在的跨域安全的问题,得靠服务端来解决)。目前除IE外的浏览器已经对webSocket支持得很好了,微信小程序再推一把之后,它会变得更加流行。
    我们来设计一个新的demo,一个比较有趣的小游戏,多人版扫雷,准确地讲,多人版挖黄金。

    后台代码:https://github.com/jsongo/mime-server
    前端代码:https://github.com/jsongo/wx-mime
    微信小程序利用websocket制作多人版扫雷小游戏
    游戏规则是这样的:把雷换成金子,挖到金子加一分,每人轮流一次(A挖完轮到B,B挖完A才能再点击),点中金子就算你的,也不会炸,游戏继续,直到把场上所有的金子都挖完游戏才结束。跟扫雷一样,数字也是表示周边有几个金子,然后用户根据场上已经翻出来的数字来猜哪一格可能有金子。
    这种交互的游戏难点在于,用户的点击操作都要传到服务器上,而且服务器要实时的推送到其它玩家的应用上。另外用户自己也要接收对方操作时实时传过来的数据,这样才不至于重复点中同一个格子。简单讲,就是你要上报操作给服务器,而服务器也要实时给你推消息。为了简化整个模型,我们规定玩家必须轮流来点击,玩家A点完后,才能轮到玩家B,玩家B操作完,玩家A才能点。
    我们分几步来实现这个功能。

    一、实现思路

    1、第一步,我们要先生成扫雷的地图场景

    这个算法比较简单,简述一下。随机取某行某列就可以定位一个格子,标记成金子(-1表示金子)。mimeCnt表示要生成的金子的数量,用同样的方式循环标记mimeCnt个随机格子。生成完后,再用一个循环去扫描这些-1的格子,把它周边的格子都加1,当然必须是非金子的格子才加1。代码放在这里。
    微信小程序利用websocket
    其中increaseArround用来把这格金子周边的格子都加1,实现也比较简单:
    执行genMimeArr(),随机生成结果如下:
    微信小程序利用websocket制作多人版扫雷小游戏数组
    -1表示金子。看了下貌似没什么问题。接下去,我们就要接入webSocket了。
    (这个是js版本的,其实生成地图场景的工作是在后台生成,这个js版本只是一个演示,不过算法是一样的。)

    2、我们需要一个支持webSocket的服务端

    本例子中,我们使用python的tornado框架来实现(tornado提供了tornado.websocket模块)。当然读者也可以使用socket.io,专为webSocket设计的js语言的服务端,用起来非常简单,它也对不支持webSocket的浏览器提供了兼容(flash或comet实现)。
    笔者本人比较喜欢使用tornado,做了几年后台开发,使用最多的框架之一的就是它,NIO模型,而且非常轻量级,同样的rps,java可能需要700-800M的内存,tornado只要30-40M,所以在一台4G内存的机子上可以跑上百个tornado服务,而java,对不起,只能跑3个虚拟机。微服务的时代,这一点对小公司很重要。当然如果读者本人对java比较熟悉的话,也可以选择netty框架尝试一下。
    webSocket用tornado的另一个好处是,它可以在同一个服务(端口)上同时支持webSocket及http两种协议。tornado的官方demo代码中展示了怎么实现同时使用两种协议。在本游戏中,可以这么用:用户进入首页,用http协议去拉取当前的房间号及数据。因为首页是打开最多的,进了首页的用户不一定会玩游戏。所以首页还没必要建立webSocket链接,webSocket链接主要用来解决频繁请求及推送的操作。首页只有一个请求操作。选了房间号后,进去下一个游戏页面再开始建立webSocket链接。

    3、客户端

    使用微信小程序开发工具,直接连接是会报域名安全错误的,因为工具内部做了限制,对安全域名才会允许连接。所以同样的,这里我们也继续改下工具的源码,把相关的行改掉就行修改方式如下:
    找到asdebug.js的这一行,把它改成: if(false)即可。
    1. if (!i(r, "webscoket"))

    懒得修改的读者可以直接使用我破解过的IDE。 发起一个websocket链接的代码也比较简单:
    1. wx.connectSocket({
    2.     url: webSocketUrl,
    3. });

    在调用这个请求代码之前,先添加下事件监听,这样才知道有没有连接成功:
    1. wx.onSocketOpen(function(res){
    2.     console.log('websocket opened.');
    3. });

    连接失败的事件:
    1. wx.onSocketError(function(res){
    2.   console.log('websocket fail');
    3. })

    收到服务器的消息时触发的事件:
    1. wx.onSocketMessage(function(res){
    2.     console.log('received msg: ' + res.data);
    3. })

    当链接建立之后,发送消息的方法如下:
    1. wx.sendSocketMessage({
    2.     data:msg
    3. })

    消息发送
    由于建立链接是需要几次握手,需要一定的时间,所以在wx.connectSocket成功之前,如果直接wx.sendSocketMessage发送消息会报错,这里做一个兼容,如果连接还没建立成功,则用一个数组来保存要发送的信息;而链接第一次建立时,把数据遍历一遍,把消息拿出来一个个补发。这个逻辑我们封装成一个send方法,如下:
    1. function sendSocketMessage(msg) {
    2.     if (typeof(msg) === 'object') { // 只能发送string
    3.         msg = JSON.stringify(msg);
    4.     }
    5.     if (socketOpened) { // socketOpened变量在wx.onSocketOpen时设置为true
    6.         wx.sendSocketMessage({
    7.             data:msg
    8.         });
    9.     } else { // 发送的时候,链接还没建立
    10.         socketMsgQueue.push(msg);
    11.     }
    12. }

    二、demo功能解析

    1、首页entry

    为了简化模型,把重点放在webSocket上,我们把首页做成自己填写房间号的形式。读者如果自己有时间和能力的话,可以把首页做成一个房间列表,并显示每个房间有多少人在玩,只有一人的可以进去跟他玩。甚至后面还可以加上观看模式,点击别人的房间进去观看别人怎么玩。
    填写房间号的input组件,添加一个事件,取得它的值event.detail.value后setData到本page里面。
    点击“开始游戏”,再把房间号存入app的globalData里面,然后wx.navigateTo到主游戏页面index。
    这个页面比较简单。

    2、主游戏页面

    我们封装一个websocket/connect.js模块,专门用来处理websocket链接。主要有两个方法,connect发起webSocket链接,send用来发送数据。 index主页面:
    初始化状态,9x9的格子,每一格子其实都是一个button按钮。我们生成的地图场景数据,分别对应着每一格。比如1表示周边的1个金子,0表示周边没有金子,-1表示这格是个金子,我们的目标就是找到这些-1。找得越多得分越高。
    这里讨论一个安全性问题。相信一句话:在前端做的安全措施大都是不靠谱的。上图中的矩阵,每个格子背后的数据,不应该放在前端,因为js代码是可以调试的,可以下断点在相应的变量上,就可以看到整个矩阵数据,然后就知道哪些格子是金子,就可以作弊,这是非常不公平的。所以最好的方法是把这些矩阵数据存在后端,每次用户操作的时候,把用户点击的坐标发到后台,后台再判断相应的坐标是什么数据,再返回给前端。这个看似有很多数据传输的交互方式,其实是不会浪费资源,因为用户的每个点击操作,本来就要上报到后台,这样游戏的另一玩家才知道你点了哪个格子。反正都是要传数据的,所以肯定要传坐标,这样前端就完全没有必要知道哪个格子是什么数据,因为后台的推送消息会告诉你。
    这样我们就绕过了前端存矩阵数据的问题。但是我们还是需要一个数组来存储当前矩阵状态的,比如哪个格子已经被翻开,里面是什么数据,也就是说要存储场上已经被打开的格子。所以在后台,我们要存储两个数据,一个是所有的矩阵数据,也就是地图场景数据;另一个是当前状态的数据,这个要用来同步双方的界面。

    3、结束页面

    游戏结束的判断条件,就是场上所有的金子都被挖完了。这个条件也是在后台判断的。
    在每次用户挖到金子的时候,后台都会多一个判断逻辑,就是看这个金子是否是最后一个。如果是的话,就发送一个over类型的消息给游戏的所有玩家。
    玩家终端接收到这个消息时,就会结束当前的游戏,并跳到结束页面。
    没有专门的设计师,随便网上偷了张图片贴上去,界面比较丑。下方显示自己的得分和当前的房间号。
    三、实现细节

    1、代码结构

    前端代码,分了几个模块:pages放所有的页面,common放通用的模块,mime放挖金子的主逻辑(暂时没用到),res放资源文件,websocket放webSocket相关的处理逻辑。
    后台代码,读者稍微了解一下就行了,不讨论太多。里面我放了docker文件,熟悉docker的读者可以直接一个命令跑起整个服务端。笔者在自己的服务器上跑了这个webSocket服务,ip和端口已经写在前端代码里,读者轻虐。可能放不久,读者可以自己把这个服务跑起来。

    2、消息收发

    (1)消息协议

    我们简单地定义下,消息的格式如下。 发送消息:
    1. {type: 'dig', …}

    type是必带字段。
    服务器返回的消息:
    1. {errCode: 0, data: {type: 'dig', …} }

    errCode为0的时候,表示请求处理成功,后面带上data字段表示返回的数据,里面的type也是必带字段,表示的是什么类型的消息。
    因为webSocket类型的消息跟传统的http请求不太一样,http请求没有状态,一个请求过去,一会儿就返回,返回的数据肯定是针对这个请求的。而webSocket的模型是这样的:客户端发过去很多请求,然后也不知道服务器返回的数据哪个是对应哪个请求,所以需要一个字段来把所有的返回分成多种类型,并进行相应的处理。

    (2)发送消息

    发送消息就比较容易了,上面我们定义了一个send方法及未连接成功时的简单的消息列表。

    (3)接收消息

    读者在阅读代码的时候,可能会有一个疑惑,websocket/connect.js里只有send发送方法,而没有接收推送消息的处理,那接收消息的处理在哪?怎么关联起来的?
    websocket/目录里面还有另一个文件,msgHandler.js,它就是用来处理接收消息的主要处理模块:
    从服务器推送过来的消息,主要有这三种类型:1挖金子操作,可能是自己的操作,也可能是对方的操作,里面有一个字段isMe来表示是否是自己的操作。接收到这类消息时,会翻转地图上相应的格子,并显示出挖的结果。2创建或进入房间的操作,一个房间有两个用户玩,创建者先开始。3游戏结束的消息,当应用接收到这类消息时,会直接跳转到结束页面。
    这个处理逻辑,是在websocket/connect.js的wx.onSocketMessage回调里关联上的。
    在消息的收发过程中,每个消息交互,调试工具都会记录下来。可以在调试工具里看到,在NetWork->WS里就可以看到:

    3、前端挖金子

    代码如下:
    1. var websocket = require('../../websocket/connect.js');
    2. var msgReceived = require('../../websocket/msgHandler.js');
    3. Page({
    4.     data: {
    5.         mimeMap: null,
    6.         leftGolds: 0, // 总共有多少金子
    7.         score: 0,     // 我的得分
    8.         roomNo: 0     // 房间号
    9.     },
    10.     x: 0, // 用户点中的列
    11.     y: 0, // 用户点中的行
    12.     onLoad: function () {
    13.         var roomNo = app.getRoomNo();
    14.         this.setData({
    15.             roomNo: roomNo
    16.         });
    17.         // test
    18.         // websocket.send('before connection');
    19.  
    20.         if (!websocket.socketOpened) {
    21.             // setMsgReceiveCallback
    22.             websocket.setReceiveCallback(msgReceived, this);
    23.             // connect to the websocket
    24.             websocket.connect();
    25.             websocket.send({
    26.               type: 'create'
    27.             });
    28.         }
    29.         else {
    30.             websocket.send({
    31.               type: 'create',
    32.               no: roomNo
    33.             });
    34.         }
    35.     },
    36.     digGold: function(event) { // 不直接判断,而把坐标传给后台判断
    37.         // 被开过的就不管了
    38.         if (event.target.dataset.value < 9) {
    39.           return;
    40.         }
    41.  
    42.         // 取到这格的坐标
    43.         this.x = parseInt(event.target.dataset.x);
    44.         this.y = parseInt(event.target.dataset.y);
    45.         console.log(this.x, this.y);
    46.         // 上报坐标
    47.         this.reportMyChoice();
    48.     },
    49.     reportMyChoice: function() {
    50.         roomNo = app.getRoomNo();
    51.         websocket.send({
    52.             type: 'dig',
    53.             x: this.x,
    54.             y: this.y,
    55.             no: roomNo
    56.         });
    57.     },
    58. });

    在page的onLoad事件里,先更新界面上的房间号信息。然后开始我们的重点,websocket.connect发起webSocket链接,websocket是我们封装的模块。然后把我们msgHandler.js处理逻辑设置到服务端推送消息回调里面。接着,发送一个create消息来创建或加入房间。服务端会对这个消息做出响应,返回本房间的地图场景数据。
    digGold是每个格子的点击事件处理函数。这儿有一个逻辑,一个格子周边最多有8个格子,所以每个格子的数据最大不可能大于8,上面代码中可以看到有一个9,这其实是为了跟0区分,用来表示场上目前的还没被翻开的格子的数据,用9来表示,当然你也可以用10,100都行。
    wxml的矩阵数据绑定代码如下:
    1. <view wx:for="{{mimeMap}}" wx:for-item="row" wx:for-index="i"
    2.     class="flex-container">
    3.     <button wx:for="{{row}}" wx:for-item="cell" wx:for-index="j"
    4.         class="flex-item {{cell<0?'gold':''}} {{cell<9?'open':''}}"
    5.         bindtap="digGold" data-x="{{j}}" data-y="{{i}}" data-value="{{cell}}">
    6.         {{cell<9?(cell<0?'*':cell):"-"}}
    7.     </button>
    8. </view>

    这个比较复杂些。两层for,第一层遍历行,第二层遍历行里的每个格子的数据。wx:for-item指定里面用到的item的名字,wx:for-index指定里面用到的key的名字。每个格子是一个button,class值做了两次判断,如果这个格子的数据小于0,表示它是金子,加上gold这个class,主要是为了给它加样式。而当数据是非9的时候,表示这个格子被翻开了,这时就加上open样式,把颜色设置成橙色。data-x和data-y用来记录格子的坐标,这样的话,用户点击的时候,就可以直接从dataset里取出坐标值,再把这个值发到服务端进行判断。

    4、服务端实现

    简单的提一下就好,因为后台不是本系列文章的重点,虽然这个demo的开发也花了大半的时候在写后台。前后端的消息交互,借助了webSocket通道,传输我们自己定义格式的内容。上面有个截图显示了后台代码目录的结构,划分得比较随意,handlers里存放了的是主要的处理逻辑。webSocketHandler是入口,在它的on_message里,对收到的客户端的消息,根据类型进行分发,dig类型,分发到answerHandler去处理,create类型,分发到roomHandler里去处理。
    还有一点稍微提一下,本例子中的后台webSocket消息处理也跟传统的http处理流程有一点不一样。就是在最后返回的时候,不是直接返回的,而是广播的形式,把消息发送给所有的人。比如用户A点击了格子,后台收到坐标后,会把这个坐标及坐标里的数据一起发送给房间里的所有人,而不是单独返回给上报坐标的人。只是会有一个isMe字段来告诉客户端是否是自己的操作。
    总之,在做webSocket开发的时候,上面提到的,前后端都可能会有一些地方跟传统的http接口开发不太一样。读者尝试在做webSocket项目的时候,转换一下思维。
    最后提下一个注意点:微信小程序的websocket链接是全局只能有一个,官方提示:“一个微信小程序同时只能有一个 WebSocket 连接,如果当前已存在一个 WebSocket 连接,会自动关闭该连接,并重新创建一个 WebSocket 连接。”
    ↓ 查看全文

    微信小程序利用websocket制作多人版扫雷小游戏由懒人建站收集整理,您可以自由传播,请主动带上本文链接

    懒人建站就是免费分享,觉得有用就多来支持一下,没有能帮到您,懒人也只能表示遗憾,希望有一天能帮到您。

    微信小程序利用websocket制作多人版扫雷小游戏-最新评论

    网站地图 环球娱乐bc2013 博狗博彩 新世纪娱乐不违法吗
    申博龙虎 AG国际馆SunBet平台 申博亚洲上网导航 澳门网上正规赌场
    优彩开奖网二分彩 菲律宾申博太阳城网站多少登入 mg电子娱乐登入 彩99平台登入
    欧华娱乐 明陞国际娱乐 明陞国际备用 环球娱乐游戏
    圣淘沙娱乐官网 速博娱乐是什么 时时博娱乐bc2013 KK娱乐备用
    898XTD.COM 688BBIN.COM 858XTD.COM na138.com 526SUN.COM
    1112127.COM 987sj.com 171sj.com 989sj.com 122TGP.COM
    887XTD.COM 958sj.com 388TGP.COM 99sbmsc.com 766BBIN.COM
    3333XSB.COM 151sj.com 18888shenbo.com 987cw.com 698psb.com