client端使用簡單的h5+js實現了棋局的整體佈局。 server端使用node的socket.io模塊與客戶端進行數據交互,棋子的落點和輸贏校驗均是在server端完成。
五子棋ui界面請見..css
client端的界面這裏就不作過多解釋了,只要稍微懂點h5就能夠自行去 這裏下載源代碼觀看,由於今天的主題主要是socket.io這一塊,因此本章只概述client和server是如何經過tcp鏈接進行交互的。html
首先先帶你們看一下目錄結構node
| server.js (socket服務器)
| gobang-ui.html (是玩家下棋頁面)
| index.html (是用戶登錄界面)
| home.html (是用戶大廳界面, 用來匹配等待的 若是在線人數少於2人, 則匹配失敗, 並會返回錯誤信息)
| game.html (client端程序的入口,內嵌iframe來顯示各個頁面,經過改變iframe的src屬性,來達成僞頁面跳轉)
| img (圖片資源文件夾)
| tou.jpg (棋盤界面用戶的頭像,由於登陸界面只要輸入用戶名就能夠開始遊戲了,因此全部用戶的頭像都是同樣的)
複製代碼
game.html主界面git
<!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請求同樣,頁面每次因此咱們應儘可能避免頁面跳轉這個操做。github
// 這行代碼表示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事件less
function login(username) {
socket.emit('login', username)
}
複製代碼
server.jsdom
// 監聽鏈接
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
// 玩家登錄成功
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
}
複製代碼
若是你們喜歡的話,請在github上下載個人源碼,謝謝你們支持!