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 相关文章推荐
IE和Firefox下javascript的兼容写法小结
Dec 10 Javascript
js弹出层永远居中实现思路及代码
Nov 29 Javascript
Javascript技术栈中的四种依赖注入小结
Feb 27 Javascript
AngularJS利用Controller完成URL跳转
Aug 09 Javascript
Bootstrap基本样式学习笔记之按钮(4)
Dec 07 Javascript
浅谈js中的this问题
Aug 31 Javascript
Vue项目全局配置页面缓存之按需读取缓存的实现详解
Aug 01 Javascript
jQuery实现鼠标移到某个对象时弹出显示层功能
Aug 23 jQuery
小程序中设置缓存过期的实现方法
Jan 14 Javascript
javascript中可能用得到的全部的排序算法
Mar 05 Javascript
解决vue动态下拉菜单 有数据未反应的问题
Aug 06 Javascript
微信小程序实现点赞业务
Feb 10 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(2)――PHP类型
2010/02/15 PHP
浅谈apache和nginx的rewrite的区别
2013/02/22 PHP
PHP转换文件夹下所有文件编码的实现代码
2013/06/06 PHP
php读取纯真ip数据库使用示例
2014/01/26 PHP
php中使用getimagesize获取图片、flash等文件的尺寸信息实例
2014/04/29 PHP
PHP基于自定义类随机生成姓名的方法示例
2017/08/05 PHP
PHP7扩展开发之hello word实现方法详解
2018/01/15 PHP
Yii2.0 RESTful API 基础配置教程详解
2018/12/26 PHP
CSS中一些@规则的用法小结
2021/03/09 HTML / CSS
MSN消息提示类
2006/09/05 Javascript
百度留言本js 大家可以参考下
2009/10/13 Javascript
制作高质量的JQuery Plugin 插件的方法
2010/04/20 Javascript
jquery 使用点滴函数代码
2011/05/20 Javascript
JS中toFixed()方法引起的问题如何解决
2012/11/20 Javascript
JavaScript中如何通过arguments对象实现对象的重载
2014/05/12 Javascript
Javascript显示和隐藏ul列表的方法
2015/07/15 Javascript
javascript动画算法实例分析
2015/07/31 Javascript
JS实现把鼠标放到链接上出现滚动文字的方法
2016/04/06 Javascript
js数组操作方法总结(必看篇)
2016/11/22 Javascript
BootStrap模态框和select2合用时input无法获取焦点的解决方法
2017/09/01 Javascript
vue.js做一个简单的编辑菜谱功能
2018/05/08 Javascript
详解element上传组件before-remove钩子问题解决
2020/04/08 Javascript
Node.js文本文件BOM头的去除方法
2020/11/22 Javascript
Python的Bottle框架中获取制定cookie的教程
2015/04/24 Python
python检查序列seq是否含有aset中项的方法
2015/06/30 Python
Python 16进制与中文相互转换的实现方法
2018/07/09 Python
django中SMTP发送邮件配置详解
2019/07/19 Python
Sephora丝芙兰印尼官方网站:购买化妆品和护肤品
2018/07/02 全球购物
Diptyque英国官方网站:源自法国的知名香氛品牌
2019/08/28 全球购物
付款委托书范本
2014/04/04 职场文书
爬山的活动方案
2014/08/16 职场文书
党委班子对照检查材料
2014/08/19 职场文书
干部作风整顿个人剖析材料
2014/10/06 职场文书
文案策划岗位个人自我评价(范文)
2019/08/08 职场文书
Java后台生成图片的完整步骤
2021/08/04 Java/Android
python_tkinter事件类型详情
2022/03/20 Python