知識 分享 互助 懶人建站

    懶人建站專注于網頁素材下載,提供網站模板、網頁設計、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制作多人版掃雷小游戲-最新評論

    • 貨源網 2017-03-27 04:51:40
      [nono拜年]精仿? CASIO(卡西歐) 韓嫻宮 Alexander Wang(亞歷山大王) Gianvito Rossi MiuMiu(繆繆) Tmo Ford(湯姆·福特) EVISU(福神) ahuo.ml
    福利彩票排列7走势图