您好,登錄后才能下訂單哦!
怎么在node.js中利用socket.io實現多人在線匹配聯機五子棋?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
首先先帶大家看一下目錄結構
| server.js (socket服務器) | gobang-ui.html (是玩家下棋頁面) | index.html (是用戶登陸界面) | home.html (是用戶大廳界面, 用來匹配等待的 如果在線人數少于2人, 則匹配失敗, 并會返回錯誤信息) | game.html (client端程序的入口,內嵌iframe來顯示各個頁面,通過改變iframe的src屬性,來達成偽頁面跳轉) | img (圖片資源文件夾) | tou.jpg (棋盤界面用戶的頭像,因為登錄界面只要輸入用戶名就可以開始游戲了,所以所有用戶的頭像都是一樣的)
game.html主界面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> * { margin: 0; padding: 0; width: 100%; height: 100%; } </style> <!-- 引入cdn上的socket.io庫 --> <script src="https://cdn.bootcss.com/socket.io/2.1.0/socket.io.js"></script> </head> <body> <!-- 這里是程序的入口,通過js改變src屬性,來切換頁面 --> <iframe id="game" src="index.html" width="100%" height="100%" scrolling="no"></iframe> </body> </html>
為什么我們要用嵌入iframe改變src屬性的方式來制造頁面跳轉的現象?因為頁面的每一次跳轉或刷新都會使socket連接斷開。就好像http中的request請求一樣,頁面每次所以我們應盡量避免頁面跳轉這個操作。
// 這行代碼表示client端對server端進行第一次連接 var socket = io('ws://localhost:3000')
在index.html也就是用戶的登錄界面
<!-- 這是用戶登錄的按鈕 --> <div onclick="login()">開始游戲</div>
當點擊了這個按鈕之后,它會觸發js中的login方法,但這個方法并不會直接去連接server,因為socket連接在game.html中,所以目前來看,這個頁面只是game.html的子頁面,這個方法在判斷input中的value是否為空后,立即通過全局對象parent調用的父頁面(game.html)中的login方法
// index.html中的login方法 function login() { if (username.value === undefined || username.value === '') { return } // 調用父窗口的login方法 parent.login(username.value) }
game.html中的login方法,這個方法通過socket向server觸發了login事件
function login(username) { socket.emit('login', username) }
server.js
// 監聽連接 io.on('connection', function (socket) { // 玩家登陸, socket.emit('login', username)就是觸發了這個事件 // 監聽了login事件 socket.on('login', function (name) { // players是一個全局數組,里面存放了所有的玩家對象,如果players中 var flag = players.some(function (value) { return value.name === name }) if (flag) { socket.emit('home', {'flag': true}) } else { console.log(name + '已登陸') // 創建玩家 new Player(socket, name) // 將玩家放進數組中 // players.push(player) // 如果用戶名沒有重名,那么觸發client端的home事件 socket.emit('home', {'playerCount': playerCount, 'name': name}) } }) })
玩家client對home事件的監聽
// 玩家登陸成功 socket.on('home', function (data) { if (data.flag) { game.contentWindow.flag.hidden = false } else { game.contentWindow.flag.hidden = true // 保存用戶名和玩家在線人數到localStorage中 localStorage.setItem('name', data.name) localStorage.setItem('playerCount', data.playerCount) // location.href = './home.html' game.src = 'home.html' } })
home.html玩家等待大廳, home.html和index.html長得基本一致,所以它也有一個按鈕,匹配按鈕,通過它來觸發play事件
// 玩家開始匹配 this.socket.on('play', function () { // 如果空閑玩家總數大于或等于2,那么開始游戲 if (playerCount >= 2) { self.pipei = true // 如果已經有人在開始匹配了,那么這個玩家就不需要走下面函數了,因為繼續執行的話相當于再開一個棋局 if (isExistFZ(self) > 0) { // 保持不動就好,房主會自動找到你的 return } // 如果沒有房主,那么這個玩家將成為房主 self.fz = true // 可用的玩家數 var player2 = null self.timer = setInterval(function () { console.log('正在匹配...') if (player2 = findPlayer(self)) { console.log('匹配成功') self.gamePlay = new Game(self, player2) player2.gamePlay = self.gamePlay clearInterval(self.timer) } }, 1000) } else { socket.emit('player less') } })
server.js中有兩個類,一個是Player玩家類,另一個是Game棋局類,一個棋局對應兩個玩家。
Player類的屬性
this.socket = socket // socket對象,玩家通過它來監聽數據 this.name = name // 玩家的名稱 this.color = null // 玩家棋子的顏色 this.state = 0 // 0代表空閑, 1在游戲中 this.pipei = false // 是否在匹配 this.gamePlay = null // 棋局對象 this.flag = true // 是否輪到這個玩家出棋 this.fz = false // 是否是房主
Player類對象監聽的事件
// 監聽玩家是否退出游戲 this.socket.on('disconnect', function () { // 刪除數組中的玩家 // players.splice(players.indexOf(self), 1) // 刪不掉 // delete players[players.indexOf(self)] // 新的刪除方式 players = players.filter(function (value) { return value.name !== self.name }) playerCount-- // 如果退出游戲的玩家正在進行游戲,那么這局游戲也該退出 if (self.state === 0) { gameCount-- } console.log(self.name + '已退出游戲') }) // 玩家開始匹配 this.socket.on('play', function () { // 如果空閑玩家總數大于或等于2,那么開始游戲 if (playerCount >= 2) { self.pipei = true // 如果已經有人在開始匹配了,那么這個玩家就不需要走下面函數了,因為繼續執行的話相當于再開一個棋局 if (isExistFZ(self) > 0) { // 保持不動就好,房主會自動找到你的 return } // 如果沒有房主,那么這個玩家將成為房主 self.fz = true // 可用的玩家數 var player2 = null self.timer = setInterval(function () { console.log('正在匹配...') if (player2 = findPlayer(self)) { console.log('匹配成功') self.gamePlay = new Game(self, player2) player2.gamePlay = self.gamePlay clearInterval(self.timer) } }, 1000) } else { socket.emit('player less') } }) // 玩家取消匹配按鈕 this.socket.on('clearPlay', function () { clearInterval(self.timer) }) // 監聽數據,玩家下棋的時候觸發 this.socket.on('data', function (data) { if (self.flag) { add_pieces(self.gamePlay, data, self.color) } }) // 最后將當前玩家實例放到players全局玩家數組中去 players.push(this)
Game(棋局類)
// 棋盤的格子數 this.column = 21 this.arr = init_arr() // 存儲棋盤坐標的二位數組 // 一局棋局上的兩個玩家 this.play1 = play1 this.play2 = play2 // 修改游戲狀態 this.play1.state = 1 this.play2.state = 1 // 在游戲中,是否匹配為false this.play1.pipei = false this.play2.pipei = false this.play1.fz = false this.play1.fz = false // 隨機給兩個玩家分配棋子顏色 this.play1.color = ~~(Math.random() * 2) === 0 ? 'white' : 'black' this.play2.color = this.play1.color === 'white' ? 'black' : 'white' // 誰是白棋誰先走 this.play1.flag = this.play1.color === 'white'? true: false this.play2.flag = this.play2.color === 'white'? true: false
添加棋子方法
// 添加棋子 function add_pieces(self, position, color) { if (self.arr[position.x][position.y] === undefined) { self.arr[position.x][position.y] = color if (color === self.play1.color) { self.play1.flag = false self.play2.flag = true } else if (color === self.play2.color) { self.play1.flag = true self.play2.flag = false } check_result(self, self.arr, position, color) } } // 初始化數組 function init_arr() { var arr = [] for (var i = 0; i < 21; i++) { arr.push(new Array(21)) } return arr }
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。