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 版本自动生成文章摘要
Jul 23 Javascript
妙用Jquery的val()方法
Jun 27 Javascript
jQuery实现列表自动循环滚动鼠标悬停时停止滚动
Sep 06 Javascript
javascript教程:关于if简写语句优化的方法
May 17 Javascript
jQuery 1.9.1源码分析系列(十五)之动画处理
Dec 03 Javascript
js中遍历对象的属性和值的方法
Jul 27 Javascript
Web制作验证码功能实例代码
Jun 19 Javascript
简单实现jquery隔行变色
Nov 09 jQuery
JS实现的JSON序列化操作简单示例
Jul 02 Javascript
jQuery实现适用于移动端的跑马灯抽奖特效示例
Jan 18 jQuery
使用Vue实现简单计算器
Feb 25 Javascript
Vue点击切换Class变化,实现Active当前样式操作
Jul 17 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的历史和优缺点
2006/10/09 PHP
php获取字符串中各个字符出现次数的方法
2015/02/23 PHP
PHP实现长文章分页实例代码(附源码)
2016/02/03 PHP
PHP的openssl加密扩展使用小结(推荐)
2016/07/18 PHP
phpstorm最新激活码分享亲测phpstorm2020.2.3版可用
2020/11/22 PHP
让textarea控件的滚动条怎是位与最下方
2007/04/20 Javascript
js wmp操作代码小结(音乐连播功能)
2008/11/08 Javascript
Knockout数组(observable)使用详解示例
2013/11/15 Javascript
js 获取时间间隔实现代码
2014/05/12 Javascript
js+css实现导航效果实例
2015/02/10 Javascript
Jquery实现鼠标移动放大图片功能实例
2015/03/25 Javascript
JavaScript实现的Tween算法及缓冲特效实例代码
2015/11/03 Javascript
Angular2入门教程之模块和组件详解
2017/05/28 Javascript
js es6系列教程 - 基于new.target属性与es5改造es6的类语法
2017/09/02 Javascript
Angular 2使用路由自定义弹出组件toast操作示例
2019/05/10 Javascript
JavaScript数组去重实现方法小结
2020/01/17 Javascript
基于JavaScript实现十五拼图代码实例
2020/04/26 Javascript
深入解析Python中的上下文管理器
2016/06/28 Python
python3设计模式之简单工厂模式
2017/10/17 Python
tensorflow实现softma识别MNIST
2018/03/12 Python
在spyder IPython console中,运行代码加入参数的实例
2020/04/20 Python
python 将视频 通过视频帧转换成时间实例
2020/04/23 Python
python高级特性简介
2020/08/13 Python
iHerb香港:维生素、补充剂和天然保健品
2017/08/01 全球购物
Europcar葡萄牙:葡萄牙汽车和货车租赁
2017/10/13 全球购物
英文版银行求职信
2013/10/09 职场文书
优秀大学生推荐信范文
2013/11/28 职场文书
关爱老人标语
2014/06/21 职场文书
助人为乐好少年事迹材料
2014/08/18 职场文书
孝敬父母的活动方案
2014/08/28 职场文书
高一作文之乐趣
2019/11/21 职场文书
python文件目录操作之os模块
2021/05/08 Python
vue-cli4.5.x快速搭建项目
2021/05/30 Vue.js
分享一些Java的常用工具
2021/06/11 Java/Android
Spring Data JPA的Audit功能审计数据库的变更
2021/06/26 Java/Android
python自动化测试通过日志3分钟定位bug
2021/11/20 Python