頭腦王者——小程序核心功能開發


頭腦王者答題對戰源碼分析教程

  接到業務需求:用微信小程序開發一個答題對戰類的游戲,借此機會呢把小程序好好研究一下(小程序出來很長時間了現在才看)。前段時間超級火爆的“頭腦王者”就是我最好的研究對象。在研究階段呢,沒有配置前端、設計。所以樣式基本是仿照的。廢話不多說,進入正題。

  我們實現最核心的玩法就行:單人答題、雙人pk答題、排行榜(好友/世界)。

  知識儲備:涉及到實時通訊,在nodejsworkman之間,選擇了workman需要過一遍微信小程序官方文檔。如果有vue基礎那么上手就更快了。最重要的一點:ES6語法要熟悉。

  一、用戶信息

    在小程序內,因為多個頁面都需要用戶信息,所以用戶信息的獲取放在app.js里,做一個本地存儲。小程序在啟動后是有兩個不同步的線程:view thread appservice thread。為了解決線程不同步造成用戶信息獲取出現的問題,用promise+callback進行了改造。獲取用戶openid的過程我們放在了第一步。獲取用戶信息:要判斷是否有緩存、是否有授權等情況。

    

 1  const promise = new Promise(function (resolve, reject) {
 2       var openid = wx.getStorageSync('openid');
 3       if (openid == "" || openid == null) {
 4         wx.login({
 5           success: res => {
 6             wx.request({
 7               url: 'https://fotonpickup.risingad.com/wxapi2.php',
 8               data: { 'code': res.code },
 9               success(res) {
10                 wx.setStorage({
11                   key: "openid",
12                   data: res.data.openid
13                 });
14                 resolve(res.data.openid);
15               },
16               fail(res) {
17                 reject(res);
18               }
19             });
20           }
21         })
22       } else {
23         resolve(openid);
24       }
25     });
26     promise.then((openid) => {
27       return new Promise((resolve, reject) => {
28         //先從本地取用戶信息
29         var wxinfo = wx.getStorageSync('wxinfo');
30         if (wxinfo) {
31           resolve(wxinfo);
32         } else {
33           wx.getSetting({
34             success: res => {
35               if (res.authSetting['scope.userInfo']) {
36                 // 已經授權,可以直接調用 getUserInfo 獲取頭像昵稱,不會彈框
37                 wx.getUserInfo({
38                   success: res => {
39                     resolve(res.userInfo);
40                   }
41                 })
42               } else {
43                 //沒有授權的情況,不知道正式版本是什么樣子的。
44                 wx.getUserInfo({
45                   success: res => {
46                     resolve(res.userInfo);
47                   },fail:res=>{
48                     //resolve(false);
49                     //沒有授權的預覽版本,用open-type獲取,並跳轉統一頁面,其他頁面默認為有用戶授權的狀態
50                     wx.navigateTo({
51                       url: '/pages/scope/scope'
52                     });
53                   }
54                 })
55               }
56             }
57           })
58         }
59       }).then((wxinfo) => {
60         if (wxinfo) {
61           //將用戶信息上傳服務器保存一份
62           this.uploadUserInfo(wxinfo);
63           //本地存儲用戶cookie
64           wx.setStorage({
65             key: "wxinfo",
66             data: wxinfo
67           });
68         }
69         this.globalData.userInfo=wxinfo;
70         if(this.userInfoReadyCallback){
71           this.userInfoReadyCallback(wxinfo);
72         }
73       })
74     })
75   },
View Code

 

  注意看,第74行。此處注入了一個回調函數。是為了解決view thread 和app thread不同步的時候出現的問題。我們看一下怎么使用app中獲取的用戶信息

 1   onLoad: function () {
 2     wx.showLoading({
 3       title: '加載中',
 4     });
 5     if (app.globalData.userInfo) {
 6       this.setData({
 7         userInfo: app.globalData.userInfo
 8       });
 9       wx.hideLoading();
10 
11     } else {
12       app.userInfoReadyCallback = res => {
13         if (res) {
14           this.setData({
15             userInfo: res
16           });
17           wx.hideLoading();
18         }
19       };
20     }
21 
22   }
View Code

  注意:本地調試的,小程序不再提供彈框的方式授權。需要使用button組件,讓用戶點擊彈出授權。我在獲取用戶信息的時候,如果沒有授權,會跳轉到一個專門的授權頁面。

 

 

   1 <button wx:if="{{!scopeUserinfo}}" open-type="getUserInfo"bindgetuserinfo="getUserInfo">測試版:請同意授權 2 </button> 

 

 二、workman實時通訊

  這個workman可以查看官方文檔,配置起來很簡單。不過需要注意的一點就是:小程序只支持wss的協議,必須是帶證書的https的請求。我們需要改一下workman中的代碼。進入start_gateway.php這個文件,配置下證書參數。transport改為ssl

  

 1 // gateway 進程,這里使用Text協議,可以用telnet測試
 2 // $gateway = new Gateway("tcp://0.0.0.0:8282");
 3 $context = array(
 4     // 更多ssl選項請參考手冊 http://php.net/manual/zh/context.ssl.php
 5     'ssl' => array(
 6         // 請使用絕對路徑
 7         'local_cert'                 => 'E:/www/GatewayWorker-for-win/1535601_wxapp.x-nev.com.pem', // 也可以是crt文件
 8         'local_pk'                   => 'E:/www/GatewayWorker-for-win/1535601_wxapp.x-nev.com.key',
 9         'verify_peer'               => false,
10         // 'allow_self_signed' => true, //如果是自簽名證書需要開啟此選項
11     )
12 );
13 $gateway = new Gateway("Websocket://0.0.0.0:1451",$context);
14 $gateway->transport = 'ssl';

  在小程序的開發工具中要配置,wss域名:1451 。這個端口是在workman中配置,可以試任意端口。至此workman和小程序的連接打通了。

  

  邀請好友pk,就是開一個房間(在workman中對應組的概念),然后在分享的鏈接中加入roomid這個參數。在用戶登入之后,通過workman通知對方。

  分享按鈕對應的代碼:

 1  onShareAppMessage: function (res) {
 2     if (res.from === 'button') {
 3       var timestamp = new Date().getTime();
 4       var r = parseInt(Math.random() * (100000 - 5000 + 1) + 5000, 10);
 5       var roomid = timestamp + '' + r;
 6       var s_endtime = timestamp + 600000;
 7       var s_openid = wx.getStorageSync('openid');
 8       return {
 9         title: '來啊,狗蛋!對戰吧',
10         path: '/pages/dz/dz?pkroom=' + roomid+'&endtime='+s_endtime+'&fqzid='+s_openid,
11         success: function () {
12           var datastr= {pkroom: roomid, endtime: s_endtime, fqzid: s_openid};
13           wx.setStorage({
14             key: 'pkinfo',
15             data:datastr
16           })
17           wx.navigateTo({
18             url: '/pages/dz/dz'
19           });
20         }
21 
22       }
23     } else {
24       return {
25         title: '來啊,狗蛋!',
26         path: '/pages/index/index',
27         imageUrl: '/imgs/yang.jpg'
28       }
29     }
30   }
View Code

  用戶通過鏈接進入對戰頁面初始化代碼:

 1  onLoad: function (options) {
 2 
 3     inituserinfo = new Promise(function (resolve, reject) {
 4       if (app.globalData.userInfo) {
 5         resolve(options);
 6       } else {
 7         app.userInfoReadyCallback = res => {
 8           resolve(options);
 9         };
10       }
11     });
12     inituserinfo.then(options => {
13       if (options.pkroom) {
14         //有參數,判斷是否有效期內,判斷pkinfo,對比openid
15         if (options.endtime > new Date().getTime()) {
16           //有效期內。
17           var pkinfo = wx.getStorageSync('pkinfo') || {};
18           pkinfo.endtime = options.endtime;
19           pkinfo.fqzid = options.fqzid;
20           pkinfo.pkroom = options.pkroom;
21           wx.setStorage({
22             key: 'pkinfo',
23             data: { 'pkroom': pkinfo.pkroom, 'endtime': pkinfo.endtime, 'fqzid': pkinfo.fqzid }
24           })
25           this.intime(pkinfo);
26         } else {
27           //url上房間過期
28           this.overtime();
29         }
30       } else {
31         //沒有參數,則判斷有無pkinfo;
32         var pkinfo = wx.getStorageSync('pkinfo');
33         console.log("沒有參數:取pkinfo:" + pkinfo.endtime);
34         console.log("沒有參數:當前時間:" + new Date().getTime());
35         if (pkinfo && (pkinfo.endtime > new Date().getTime())) {
36           //在有效期內,更新計時器。對比openid,加載用戶信息
37           this.intime(pkinfo);
38           //開始監聽
39         } else {
40           //沒有,或則過期,清空cookie
41           this.overtime();
42         }
43       }
44     });
45   },
View Code

  拿到url參數,我們要判斷,這個房間誰是房主,誰是挑戰者。 要判斷房間是否在有效期內,房主是否關閉房間。因為用戶進入挑戰頁的方式太多,不同方式對應這不同的情況。

 1  //有效期內
 2   intime: function (pkinfo) {
 3     //在有效期內,更新計時器。對比openid,加載用戶信息
 4     //計算剩余時間
 5     var stime = pkinfo.endtime - new Date().getTime();
 6     endtime = parseInt(stime / 1000);
 7     var s_openid = wx.getStorageSync('openid');
 8     var userInfo = {
 9       'name': app.globalData.userInfo.nickName,
10       'img': app.globalData.userInfo.avatarUrl,
11       'openid': s_openid
12     };
13     if (s_openid == pkinfo.fqzid) {
14       this.setData({
15         fqz: userInfo,
16         type: 'fqz',
17         pkroom: pkinfo.pkroom
18       })
19     } else {
20       this.setData({
21         tzz: userInfo,
22         type: 'tzz',
23         pkroom: pkinfo.pkroom
24       })
25     }
26   },
27   //房間過期
28   overtime: function () {
29     wx.showToast({
30       title: '對戰房間已過期',
31       icon: 'info',
32       duration: 1000
33     });
34     wx.removeStorage({
35       key: 'pkinfo'
36     })
37     setTimeout(() => {
38       wx.redirectTo({
39         url: '/pages/index/index'
40       })
41     }, 1800);
42   },
View Code

 

  現在最關鍵的地方來了:創建socket連接及監聽!!!!!

  小程序只允許同時創建不超過兩個socket連接。在頁面創建連接的時候尤其得注意,頁面跳轉、退出、隱藏、顯示的過程中,要保護好線程。所以我們統一在onunload的時候,主動關閉socket連接。下次進來的時候根據儲存到本地的房間信息重新創建連接。我封裝了一個sockethelper.js,因為這個init/open的過程都是異步的,所以使用了promise封裝了一下。(class、constructor是es6的語法,不清楚的可以先看一下es6的語法)

 1 class webSocket {
 2 
 3     constructor(obj = {}) {
 4         this.hasConn = false
 5         this.hasOpen = false
 6         this.msghandle = obj;
 7         this.msghandle['ping'] = function (e, _this) {
 8             var data = { 'type': 'pong' };
 9             _this.SendMsg(data);
10         }
11         var _this = this;
12         this.SocketTask = wx.connectSocket({
13             url: 'wss://wxapp.x-nev.com:1451',
14             header: {
15                 'content-type': 'application/json'
16             },
17             method: 'post',
18             success: function (res) {
19                 _this.hasConn = true;
20             },
21             fail: function (err) {
22                 wx.showToast({
23                     title: '網絡異常!',
24                 })
25             },
26         });
27     }
28     StartListen() {
29         var _this = this;
30         return new Promise((resolve, reject) => {
31 
32             this.SocketTask.onOpen(function (res) {
33                 console.log("page:socket:open");
34                 _this.hasOpen = true;
35                 resolve(res);
36             })
37             this.SocketTask.onClose(function (res) {
38                 console.log("page:socket:close")
39             })
40 
41             this.SocketTask.onError(function (res) {
42                 console.log("page:socket:error-");
43                // reject(res);
44             })
45 
46             this.SocketTask.onMessage(function (onMessage) {
47                 var data = JSON.parse(onMessage.data);
48                 var msgtype = data['type'];
49                 if (msgtype in _this.msghandle) {
50                     _this.msghandle[msgtype](data, _this);
51                 }
52             })
53 
54         });
55 
56     }
57     SendMsg(msg, callback) {
58         if (this.hasOpen && this.hasConn) {
59             this.SocketTask.send({
60                 data: JSON.stringify(msg),
61                 success: (e) => { if (callback) { callback(e) } }
62             })
63         } else {
64             console.log("沒有open,調用一下");
65             this.StartListen().then(() => {
66                 this.SocketTask.send({
67                     data: JSON.stringify(msg),
68                     success: (e) => { if (callback) { callback(e) } }
69                 })
70             });
71         }
72 
73     }
74 }
75 
76 module.exports = { webSocket };
View Code

  用戶進入房間后,初始化參數后,要通知對方。

 1 onReady: function () {
 2 
 3     var info = {};
 4     var s_openid = wx.getStorageSync('openid');
 5     info.openid = s_openid;
 6     info.pkroom = this.data.pkroom;
 7     if (this.data.type == "fqz") {
 8       info.mark = "fqz";
 9       info.name = this.data.fqz.name;
10       info.img = this.data.fqz.img;
11     } else {
12       info.mark = "tzz"
13       info.name = this.data.tzz.name;
14       info.img = this.data.tzz.img;
15     }
16     //如果從分享挑戰頁面進來,要注冊獲取用戶信息的回調,注冊socket
17     this.registResponse(info);
18     app.globalData.stask.SendMsg({
19       'type': 'pk',
20       'mark': info.mark,
21       'openid': info.openid,
22       'name': info.name,
23       'img': info.img,
24       'pkroom': info.pkroom
25     });
26     this.formattime();
27     t2 = setInterval(() => {
28       this.formattime();
29     }, 1000);
30   },
31   formattime: function () {
32     endtime--;
33     var m = parseInt(endtime / 60);
34     var s = parseInt(endtime % 60);
35     m = m.toString().length > 1 ? m : '0' + m;
36     s = s.toString().length > 1 ? s : '0' + s;
37     var tem = `${m}:${s}`;
38     this.setData({
39       time: tem
40     });
41   },
View Code

  當雙方都進入房間后。房主出發start事件,開始答題。start事件請求后台分配對應等級的題目,並通知對方題目

 1 start: function () {
 2     //請求對應等級的題目,
 3     wx.request({
 4       url: 'https://fotonpickup.risingad.com/wxxldev/index.php?c=WXSignApi&a=Question',
 5       data: { 'roomid': this.data.pkroom, 'one': this.data.fqz.openid, 'two': this.data.tzz.openid },
 6       success: (result) => {
 7         this.setData({
 8           qlist: result.data.row,
 9           recordid: result.data.flag,
10           current: 0
11         });
12         //開始答題通知workman,提供題目id。跳轉對戰頁面。
13         app.globalData.stask.SendMsg({
14           'type': 'ready',
15           'recordid': result.data.flag,
16           'uid': this.data.tzz.openid
17         });
18       }
19     });
20 
21   },
View Code

  雙方開始答題,配置響應事件。這個就不一一列舉了,上代碼

  1  registResponse: function (pkinfo) {
  2     var obj = {
  3       'pk': (res, _this) => {
  4         //拿到用戶信息,綁定用戶頭像。
  5         if (res.content.length == 1) {
  6           if (res.content[0].mark == "fqz") {
  7             this.setData({
  8               'fqz': res.content[0]
  9             });
 10           } else {
 11             this.setData({
 12               'tzz': res.content[0]
 13             });
 14           }
 15         } else {
 16           if (res.content[0].mark == "fqz") {
 17             this.setData({
 18               'fqz': res.content[0],
 19               'tzz': res.content[1]
 20             });
 21           } else {
 22             this.setData({
 23               'fqz': res.content[1],
 24               'tzz': res.content[0]
 25             });
 26           }
 27         }
 28       },
 29       'ready': (res, _this) => {
 30         //此處注冊的監聽,只有tzz可以捕獲到。后端是單獨推送的
 31         //挑戰者請求加載相應的題目,並准備好界面。
 32         console.log("拉取題:" + res.id);
 33         wx.request({
 34           url: 'https://fotonpickup.risingad.com/wxxldev/index.php?c=WXSignApi&a=GetQuestion',
 35           data: { 'id': res.id },
 36           success: (result) => {
 37             this.setData({
 38               qlist: result.data,
 39               current: 0
 40             });
 41             //通知服務器tzz准備好了,
 42             app.globalData.stask.SendMsg({ 'type': 'start' });
 43           }
 44         });
 45       },
 46       'start': (res, _this) => {
 47         //開始各自准備pk界面。
 48         this.setData({
 49           start: true
 50         });
 51         //在這里開啟當前計時器。
 52         console.log("1.開啟計時器");
 53         t1 = setInterval(() => {
 54 
 55           if (this.data.time2 > 0) {
 56             this.setData({
 57               time2: this.data.time2 - 1
 58             });
 59             if (this.data.myself && this.data.opposite) {
 60               //都已答題,如果不是最后一題,進入下一題。並積分,初始化狀態
 61               console.log("2.都已答題");
 62               if (this.data.current == this.data.qlist.length - 1) {
 63                 //結束,出成績。
 64                 clearInterval(t1);
 65                 console.log("9.結束計時器");
 66                 this.setData({
 67                   isend: true
 68                 });
 69                 this.next(true);
 70               } else {
 71                 console.log("3.調用下一題");
 72                 this.next();
 73               }
 74             }
 75           } else {
 76             //時間到了,未答題。展示正確答案。進入下一題。
 77             //初始化 答題狀態、時間、樣式。
 78             console.log("4.時間到了有人未答題");
 79             if (this.data.current == this.data.qlist.length - 1) {
 80               //結束,出成績。
 81               clearInterval(t1);
 82               this.setData({
 83                 isend: true
 84               });
 85               this.next(true);
 86               console.log("10.結束計時器");
 87             } else {
 88               this.data.classlist[this.data.qlist[this.data.current].ropt - 1] = 'right';
 89               setTimeout(() => {
 90                 console.log("5.時間到了有人未答題,顯示正確答案,進入下一題");
 91                 this.next();
 92               }, 1000);
 93 
 94             }
 95 
 96           }
 97         }, 1000);
 98       },
 99       'dt': (res, _this) => {
100         console.log("7." + res.mark + "選擇了" + res.opts);
101         var isright = res.opts == this.data.qlist[this.data.current].ropt ? true : false;
102         if (res.mark != this.data.type) {
103           this.setData({
104             opposite: true,//對方已答題。
105             oppositeright: isright,
106             oppositeopt: res.opts
107           })
108         } else {
109           this.setData({
110             myself: true,//自己已答題
111             myselfright: isright
112           });
113           if (isright) {
114             //答對
115             this.data.classlist[res.opts - 1] = 'right';
116           } else {
117             this.data.classlist[res.opts - 1] = 'error';
118           }
119         }
120         if (this.data.myself && this.data.opposite) {
121           //都已答題
122           this.data.classlist[this.data.qlist[this.data.current].ropt - 1] = 'right';
123           this.data.classlist[this.data.oppositeopt - 1] = this.data.oppositeright ? 'right' : 'error';
124         }
125         this.setData({
126           'classlist': this.data.classlist
127         });
128       },
129       'gameover': (res, _this) => {
130         wx.showToast({
131           title: '房主關閉房間',
132           icon: 'info',
133           duration: 1000
134         });
135         wx.removeStorage({
136           key: 'pkinfo',
137           complete: function (e) {
138             console.log(e);
139           }
140         });
141         setTimeout(() => {
142           wx.redirectTo({
143             url: '/pages/index/index'
144           })
145         }, 2000);
146       }
147     }
148     //開啟監聽
149     app.globalData.stask = new webSocket();
150     //注冊監聽響應事件
151     Object.assign(app.globalData.stask.msghandle, obj);
152 
153   },
View Code

  在上一下workman都監聽的代碼

  1  /**
  2      * 當客戶端發來消息時觸發
  3      * @param int $client_id 連接id
  4      * @param mixed $message 具體消息
  5      */
  6     public static function onMessage($client_id, $message)
  7     {
  8         // 向所有人發送
  9 
 10         $message_data = json_decode($message, true);
 11         $now = date('y-m-d H:i:s', time());
 12         $filepath="E:\\www\\GatewayWorker-for-win\\msg.txt";
 13         file_put_contents($filepath, "time:$now,client_id:".$client_id.':msg:'.$message.PHP_EOL, FILE_APPEND);
 14         if (!$message_data) {
 15             return ;
 16         }
 17 
 18         // 根據類型執行不同的業務
 19         switch ($message_data['type']) {
 20 
 21             // 客戶端回應服務端的心跳
 22             case 'pong':
 23                 return;
 24               // 客戶端登錄 message格式: {type:login, name:xx, room_id:1} ,添加到客戶端,廣播給所有客戶端xx進入聊天室
 25             case 'login':
 26                 // 判斷是否有房間號
 27                 if (!isset($message_data['room_id'])) {
 28                     return '';
 29                 }
 30                 // 把房間號昵稱放到session中
 31                 $room_id = $message_data['room_id'];
 32                 //昵稱
 33                 $client_name = htmlspecialchars($message_data['client_name']);
 34                 //頭像
 35                 $client_img= $message_data['client_img'];
 36                 // 設置當前用戶的sesion可直接設置。等同於 Gateway::setSession(string $client_id, array $session);
 37                 $_SESSION['room_id'] = $room_id;
 38                 $_SESSION['client_name'] = $client_name;
 39                 $_SESSION['client_img'] = $client_img;
 40                 // 轉播給當前房間的所有客戶端,xx進入聊天室 message {type:login, client_id:xx, name:xx}
 41                 $new_message = array('type'=>$message_data['type'], 'client_id'=>$client_id, 'client_name'=>htmlspecialchars($client_name), 'time'=>date('Y-m-d H:i:s'));
 42                 Gateway::sendToGroup($room_id, json_encode($new_message));
 43                 Gateway::joinGroup($client_id, $room_id);
 44                  // 獲取房間內所有用戶列表
 45                  $clients_list = Gateway::getClientSessionsByGroup($room_id);
 46                  foreach ($clients_list as $tmp_client_id=>$item) {
 47                      $clients_list[$tmp_client_id] = $item['client_name'];
 48                  }
 49                 // 給當前用戶發送用戶列表
 50                 $new_message['client_list'] = $clients_list;
 51                 Gateway::sendToCurrentClient(json_encode($new_message));
 52                 return;
 53                 
 54             // 客戶端發言 message: {type:say, to_client_id:xx, content:xx}
 55             case 'say':
 56                 // 非法請求
 57                 if (!isset($_SESSION['room_id'])) {
 58                     throw new \Exception("\$_SESSION['room_id'] not set. client_ip:{$_SERVER['REMOTE_ADDR']}");
 59                 }
 60                 $room_id = $_SESSION['room_id'];
 61                 $client_name = $_SESSION['client_name'];
 62                 $client_img = $_SESSION['client_img'];
 63                 // 私聊
 64                 if ($message_data['to_client_id'] != 'all') {
 65                     $new_message = array(
 66                         'type'=>'say',
 67                         'from_client_id'=>$client_id,
 68                         'from_client_name' =>$client_name,
 69                         'to_client_id'=>$message_data['to_client_id'],
 70                         'content'=>nl2br(htmlspecialchars($message_data['content'])),
 71                         'time'=>date('Y-m-d H:i:s'),
 72                     );
 73                     Gateway::sendToClient($message_data['to_client_id'], json_encode($new_message));
 74                     return Gateway::sendToCurrentClient(json_encode($new_message));
 75                 }
 76                 
 77                 $new_message = array(
 78                     'type'=>'say',
 79                     'from_client_id'=>$client_id,
 80                     'from_client_name' =>$client_name,
 81                     'to_client_id'=>'all',
 82                     'client_img'=> $client_img ,
 83                     'content'=>nl2br(htmlspecialchars($message_data['content'])),
 84                     'time'=>date('Y-m-d H:i:s'),
 85                 );
 86                 return Gateway::sendToGroup($room_id, json_encode($new_message));
 87             case 'pk':
 88                     //{type:pk, pkroom:xx,name:'xxx',img:'xxx'}
 89                     $pkroom= $message_data['pkroom'];
 90                     $_SESSION['pkroom']= $pkroom;
 91                     $_SESSION['name']= $message_data['name'];
 92                     $_SESSION['img']= $message_data['img'];
 93                     $_SESSION['mark']= $message_data['mark'];
 94                     $_SESSION['openid']= $message_data['openid'];
 95                     //綁定openid到client_id;
 96                     Gateway::bindUid($client_id, $message_data['openid']);
 97                     //開一個新房間
 98                     Gateway::joinGroup($client_id, $pkroom);
 99                     //返回當前對戰房間的人員
100                     
101                     $clients_list = Gateway::getClientSessionsByGroup($pkroom);
102                     foreach ($clients_list as $tmp_client_id=>$item) {
103                         $pklist[]=$item;
104                     }
105                     $new_message = array(
106                         'type'=>'pk',
107                         'content'=>$pklist
108                     );
109                     return Gateway::sendToGroup($pkroom, json_encode($new_message));
110             case 'dt':
111                  $opts=$message_data['opts'];
112                  $mark= $_SESSION['mark'];
113                  $pkroom= $_SESSION['pkroom'];
114                  $new_message = array(
115                     'type'=>'dt',
116                    'opts'=>$opts,
117                    'mark'=>$mark
118                 );
119                 return Gateway::sendToGroup($pkroom, json_encode($new_message));
120             case 'ready':
121                 $recordid= $message_data['recordid'];
122                 $uid= $message_data['uid'];//tzz的uid
123                 $new_message = array(
124                    'type'=>'ready',
125                    'id'=>$recordid
126                 );
127                 //只給tzz響應
128                 return  Gateway::sendToUid($uid, json_encode($new_message));
129             case 'start':
130                  $pkroom= $_SESSION['pkroom'];
131                  $new_message = array(
132                     'type'=>'start'
133                 );
134                  return Gateway::sendToGroup($pkroom, json_encode($new_message));
135             case 'gameover':
136                     $pkroom= $_SESSION['pkroom'];
137                     $new_message = array(
138                     'type'=>'gameover'
139                 );
140                 Gateway::sendToGroup($pkroom, json_encode($new_message));
141                return Gateway::ungroup($pkroom);
142         }
143     }
View Code

  答題基本的思路就是這樣。里面有個聊天室也是基於workman開發的,見圖5,樣式是模仿微信。有空會重新補充一下細節。現在上一下圖。本人是后台程序,前端不太擅長,一些細節沒有優化,請見諒。有問題的可以聯系我:mian_wu@qq.com

     

  

   

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM