node.js利用socket.io实现多人在线匹配联机五子棋


Posted in Javascript onMay 31, 2018

项目地址,已上传github ——>

 client端使用简单的h5+js实现了棋局的总体布局。 server端使用node的socket.io模块与客户端进行数据交互,棋子的落点和输赢校验均是在server端完成。

五子棋ui界面请见..

client端的界面这里就不做过多解释了,只要稍微懂点h5就可以自行去这里 下载源代码观看,因为今天的主题主要是socket.io这一块,所以本章只概述client和server是如何通过tcp连接进行交互的。

首先先带大家看一下目录结构

| 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
}

如果大家喜欢的话,请在github上下载我的源码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
javascript 操作cookies及正确使用cookies的属性
Oct 15 Javascript
javascript GUID生成器实现代码
Oct 31 Javascript
Javascript实现的类似Google的Div拖动效果代码
Aug 09 Javascript
js从10种颜色中随机取色实现每次取出不同的颜色
Oct 23 Javascript
推荐8款jQuery轻量级树形Tree插件
Nov 12 Javascript
详解JavaScript ES6中的Generator
Jul 28 Javascript
Javascript实现可旋转的圆圈实例代码
Aug 04 Javascript
Jquery easyui 实现动态树
Nov 17 Javascript
搭建Bootstrap离线文档的方法
Dec 02 Javascript
Angular2里获取(input file)上传文件的内容的方法
Sep 05 Javascript
简化vuex的状态管理方案的方法
Jun 02 Javascript
js实现贪吃蛇游戏(简易版)
Sep 29 Javascript
JS与jQuery实现ListBox上移,下移,左移,右移操作功能示例
May 31 #jQuery
微信小程序登录换取token的教程
May 31 #Javascript
Rollup处理并打包JS文件项目实例代码
May 31 #Javascript
手把手教你vue-cli单页到多页应用的方法
May 31 #Javascript
详解基于Node.js的HTTP/2 Server实践
May 31 #Javascript
jQuery基于Ajax实现读取XML数据功能示例
May 31 #jQuery
jQuery实现动态加载select下拉列表项功能示例
May 31 #jQuery
You might like
php set_time_limit()函数的使用详解
2013/06/05 PHP
php中的PHP_EOL换行符详细解析
2013/10/26 PHP
PHP使用递归算法无限遍历数组示例
2017/01/13 PHP
Windows下wamp php单元测试工具PHPUnit安装及生成日志文件配置方法
2018/05/28 PHP
ThinkPHP5.0多个文件上传后找不到临时文件的修改方法
2018/07/30 PHP
PHP使用PDO实现mysql防注入功能详解
2019/12/20 PHP
IE与Firefox下javascript getyear年份的兼容性写法
2007/12/20 Javascript
jQuery fadeTo方法调整图片的透明度使用介绍
2013/05/06 Javascript
用js一次改变多个input的readonly属性值的方法
2014/06/11 Javascript
兼容Firefox的Javascript XSLT 处理XML文件
2014/12/31 Javascript
JavaScript实现的Tween算法及缓冲特效实例代码
2015/11/03 Javascript
轻松实现jquery手风琴效果
2016/01/14 Javascript
three.js绘制地球、飞机与轨迹的效果示例
2017/02/28 Javascript
Angular 4依赖注入学习教程之ValueProvider的使用(七)
2017/06/04 Javascript
ajax +NodeJS 实现图片上传实例
2017/06/06 NodeJs
BootStrap Table实现server分页序号连续显示功能(当前页从上一页的结束序号开始)
2017/09/12 Javascript
解决vue 格式化银行卡(信用卡)每4位一个符号隔断的问题
2018/09/14 Javascript
解决layer弹出层msg的文字不显示的问题
2019/09/11 Javascript
jQuery实现评论模块
2020/08/19 jQuery
vue.js封装switch开关组件的操作
2020/10/26 Javascript
vue组件中节流函数的失效的原因和解决方法
2020/12/02 Vue.js
[01:14]英雄,所敬略同——2018完美盛典宣传视频
2018/12/05 DOTA
Python中解析JSON并同时进行自定义编码处理实例
2015/02/08 Python
Python语言描述KNN算法与Kd树
2017/12/13 Python
Python Web程序部署到Ubuntu服务器上的方法
2018/02/22 Python
python中ASCII码字符与int之间的转换方法
2018/07/09 Python
pycharm执行python时,填写参数的方法
2018/10/29 Python
Python3实现的判断回文链表算法示例
2019/03/08 Python
如何理解python中数字列表
2020/05/29 Python
python开发一款翻译工具
2020/10/10 Python
python 30行代码实现蚂蚁森林自动偷能量
2021/02/08 Python
美国领先的户外服装与装备用品店:Moosejaw
2016/08/25 全球购物
澳大利亚便宜的家庭购物网站:CrazySales
2018/02/06 全球购物
党员批评与自我批评思想汇报(集锦)
2014/09/14 职场文书
租房协议书范例
2014/10/14 职场文书
javascript之Object.assign()的痛点分析
2022/03/03 Javascript