前陣子頭腦王者類的微信答題PK遊戲很火,本身也是很癡迷,玩了一陣子以後,想本身嘗試來模仿下答題這一部分的實現。服務端打算在下swoole和socket.io之間選擇,由於socket.io能夠不借助redis直接緩存一些變量,因此選擇了socket.io。簡單實現了對戰這一部分,實現的並不完善,只是一種思路javascript
index.htmlphp
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0,minimum-scale=1.0,user-scalable=0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>答題</title> <script type="text/javascript" src="vue.js"></script> <style> .player { display: flex; color: #fff; text-align: center; } .player .item { height: 80px; } .player .item.select { border: 4px solid #ff9933; box-sizing: border-box; } .player .item:first-child { background-color: #6dcffb; flex: 3; } .player .item:nth-child(2) { background-color: #5247a4; flex: 1; line-height: 80px; } .player .item:last-child { background-color: #d64e8c; flex: 3; } #app { background-color: #5a8ced } .question { color: #fff; text-align: center; } .options { padding-bottom: 40px } .options li { width: 280px; height: 60px; background-color: #fff; margin-top: 40px; display: block; border-radius: 50px; text-align: center; line-height: 60px; } .options li.correct { background-color: #00e2bc; color: #fff; } .options li.wrong { background-color: #fe6f5f; color: #fff; } .tips { text-align: center; color: #fff } </style> </head> <body> <div id="app"> <div class="player"> <div :class="[playerIndex===0?'item select':'item']"> <p class="name">選手1</p> <p class="score">{{score[0]}}</p> </div> <div class="item">{{remain_time}}</div> <div :class="[playerIndex===1?'item select':'item']"> <p class="name">選手2</p> <p class="score">{{score[1]}}</p> </div> </div> <p class="question">{{question}}</p> <ul class="options"> <li v-for="(item, key, index) in options" :class='item.class' @click="sendAnswer(key)">{{item.index}}</li> </ul> <p class="tips">{{msg}}</p> </div> </body> </html> <script> </script> <script src="./socket.io.js"></script> <script> var header = new Vue({ el: "#app", data: { socket: null, //socket.io對象 playerIndex: null, //當前選手索引 question: '', //問題內容 msg: '', //下方提示語 options: [], //選項 canAnswer: true, //是否能夠回答 remainTime: 10, //倒計時剩餘時間 score: [0, 0] //兩名選手的分數 }, created: function () { let that = this; this.socket = io.connect('http://localhost:1024'); //獲取選手索引 this.socket.emit('getPlayerIndex', '') this.socket.on('getPlayerIndex', (data) => { that.playerIndex = data; if (that.playerIndex == 1) { that.socket.emit('getQuestion', that.playerIndex) } }); //服務端發送問題,更新問題、選項和提示信息 this.socket.on('sendQueston', (data) => { that.canAnswer = true; //拿到問題後,容許回答 let res = JSON.parse(data); that.question = res.content; that.options = res.options; that.msg = res.msg; }); //服務端發送更新倒計時時間,更新倒計時 this.socket.on('updateTime', (time) => { that.remain_time = time; }); //遊戲結束,直接alert this.socket.on('gameOver', (text) => { alert('遊戲結束,' + text); }); //服務端發出獲取問題 this.socket.on('getQuestion', (time) => { if (this.playerIndex == 0) { let that = this; //3秒後獲取下一題 setTimeout(function () { that.socket.emit('getQuestion', this.playerIndex) }, 3000) } }); //服務端發送答題結果 this.socket.on('sendResult', (data) => { that.canAnswer = false; //回答後,禁止回答,防止另外一名選手點擊 let res = JSON.parse(data); that.msg = res.msg; that.options = res.options; that.score = res.score; }); }, methods: { //點擊選項後的事件 sendAnswer(index) { if (!this.canAnswer) { return false; } this.socket.emit('sendAnswer', index, this.playerIndex) }, } }) </script>
大概長這樣,後端的審美,比較很差看html
我用了redis來保存問題,內容格式以下前端
{ "id": 9, "content": "9.一年幾天",//問題內容 "options": [ //選項 "1", "2", "3", "365" ], "answer_index": 3 //答案的索引 }
選用redis的list格式,順手本身編幾個問題,用php寫進去,多編幾條,多寫幾回vue
$redis = new \Redis(); $redis->connect('127.0.0.1'); $content = ['id' => 1, 'content' => '1.一天有幾小時', 'options' => ['1', '2', '3', '48'], 'answer_index' => 3]; $redis->lPush('questions', json_encode($content));
answerserver.jsjava
const app = require('express')(); const server = require('http').Server(app); const io = require('socket.io')(server); const redisModule = require('redis'); const redis = redisModule.createClient(6379, '127.0.0.1'); let playerIndex = 0; //選手索引 let options = []; //選項 let answerIndex = 0; //答案索引 let timer = null; //定時器 let remainTime = 10; //剩餘時間 let score = [0, 0]; //得分 ['選手1分數','選手2分數'] let questionCount = 0; //問題數 let startGame = false; //是否已經開始遊戲 const maxRemainTime = 10; //最大倒計時秒數 server.listen(1024); io.on('connection', (socket) => { //客戶端發送 獲取問題 socket.on('getQuestion', (playerIndex) => { if (startGame == false) { //首次發送 倒計時開始 timer = setInterval(() => { remainTime--; if (remainTime <= 0) { socket.emit('getQuestion', remainTime); remainTime = maxRemainTime; } socket.emit('updateTime', remainTime); socket.broadcast.emit('updateTime', remainTime); }, 1000); startGame = true; } //初始化倒計時 remainTime = maxRemainTime; questionCount++; redis.lpop('questions', function (err, data) { let res = JSON.parse(data); let new_options = []; answerIndex = res.answer_index; res.options.forEach(function (v, k) { let o = new Object(); o.index = v; o.class = '' //這裏的class供前端使用,爲空時,選項是白色背景 new_options.push(o) }) res.options = new_options; options = new_options; socket.emit('sendQueston', JSON.stringify(res)) socket.broadcast.emit('sendQueston', JSON.stringify(res)) }) }); //發送答案結果 socket.on('sendAnswer', (userSelectIndex, playerIndex) => { let result = { msg: '' }; options.forEach(function (v, k) { if (answerIndex == k) { //正確的選項 背景改爲綠色 options[k].class = 'correct' } else if (k == userSelectIndex && userSelectIndex != answerIndex) { //正確的選項 背景改爲紅色 result.msg = '選手' + (playerIndex + 1) + '答錯了'; options[k].class = 'wrong' } }) //答對的選手+10分 if (userSelectIndex == answerIndex) { score[playerIndex] += 10; result.msg = '選手' + (playerIndex + 1) + '答對了'; } result.score = score; result.options = options; socket.emit('sendResult', JSON.stringify(result)); socket.emit('getQuestion'); socket.broadcast.emit('getQuestion'); socket.broadcast.emit('sendResult', JSON.stringify(result)); if (questionCount >= 5) { let winText = '平局'; //結束時的提示信息 if (score[0] > score[1]) { winText = '選手1獲勝' } else if (score[0] < score[1]) { winText = '選手2獲勝' } socket.emit('gameOver', winText); socket.broadcast.emit('gameOver', winText); clearInterval(timer); } }); //獲取選手號數 socket.on('getPlayerIndex', (data) => { socket.emit('getPlayerIndex', playerIndex); playerIndex++; if (playerIndex >= 2) { playerIndex = 0; } }); });