继以前用游戏引擎(青瓷引擎)作了斗地主单机版游戏以后,这里分享下使用socket.io来实现网络对战,代码可已放到github上,在此谈谈本身整个的开发思路吧。javascript
客户端代码html
服务端代码java
(点击图片进入游戏体验)node
前文连接:mysql
javascript开发HTML5游戏--斗地主(单机模式part1)git
javascript开发HTML5游戏--斗地主(单机模式part2)github
javascirpt开发HTML5游戏--斗地主(单机模式part3)sql
本文章为网络对战第一部份内容。主要内容以下:数据库
多人对战游戏,我用Nodejs作开发服务端,使用socket.io作通信。整个斗地主游戏流程是同样的,只是单机版啥都要本身作,网络版更多交给服务器,本身主要就是游戏界面展现了,先看看整个服务器的大体结构:npm
服务器中用Nodejs的mysql模块作mysql数据库存储,这里并非很是必要的,只是我尝试使用熟悉这个东西,这里我只是存了个uid在客户端浏览器缓存中,因此也不能真正达到持久化做用。若是要作持久化应该是要作个用户登录了,这里引擎给咱们提供了微信支持,后面优化完善能够考虑加入微信登录来保存用户数据。
对于Nodejs项目的搭建建,仍是比较简单的,简单说明下项目搭建,在工做区下建立个目录landlordServer,做为项目目录。而后用命令行进入到该目录下,加入Nodejs的socket.io和mysql模块,命令以下:
npm install socket.io npm install mysql
出于以前编写java服务端的习惯,我在项目目录下建立了src目录,用于放置代码。在src下建立server.js做为入口脚本,加入socket.io的端口监听,而后就能够用Nodejs来启动了。这样子咱们的服务端也算是搭建完成了,相对java还真是简易,接下来就能够写逻辑代码了。因为用了Nodejs,小弟对这个也不算很是熟悉,不过它的模块化用起来感受很像java类,一个文件一个类这样的形式,这个类须要什么就import进来,Nodejs是require,这样的话就不用去考虑脚本前后的问题。
客户端须要有socket.io.js的客户端文件,这个文件在服务端项目中 node_modules/socket.io-client /路径下,找到它并把它丢到客户端Scripts下就能够了,这里我是放到了Scripts/operation目录下,由于青瓷引擎也是基于Nodejs开发的,因此这里只要在Scripts也不须要声明其余什么引用便可使用。
在网络对战斗地主中,都是三个玩家一桌,这样的形式,有一些斗地主游戏还会把桌位合成再分为房间,再到大厅。我这里的实现只有桌位,一开始服务器启动是没有任何桌位的,有玩家进入建立桌位,后面的玩家进来就是进未满员的桌位,找不到再建立新桌位,若是有一个桌位玩家所有离开了,就把该桌位删除。这些事情都交给桌位管理器DeskMgr.js。我用的是一个desks对象来缓存座位信息,利用了js中对象能够是当成一个Map使用。socket.io还提供了一个分组功能,差很少是一个房间的概念,我把每一桌的玩家都加入到同一个分组中,这个分组的标识也就是这个桌位的桌位号,固然玩家退出的时候也要退出该分组。每一个桌位有三个阶段:
每一个桌位上都须要有3个座位,这里不只是方便统计,也是为了方便确认前后顺序。同时是用一个对象当作Map使用来存放三个座位,分别名为p一、p二、p3,这样顺序也就很容易确认,好比p1出了牌,轮到下家时,能够写个获取下家座位号的方法,而后获得p2,去通知p2出牌。
相比单机下的玩家信息,服务器上的玩家须要有其余属性:
整个游戏流程跟单机模式是差很少的,只是网络对战是由服务器来作发牌、轮换等操做,至关三我的在斗地主,有一个助手替他们发牌,告诉他们轮到谁抢地主,轮到谁出牌,谁赢了,这样,细分下服务器作的操做以下:
玩家加入与退出:当有玩家加入的时候,服务器会返回给玩家的客户端他所在桌位的信息,还有该玩家获得的桌位号。这样在客户端就能够显示出整个桌位的信息了,其实就是显示左右边玩家的名字。除此以外,若是该桌位还有其余玩家,还须要给其余玩家广播有新玩家加入的消息。退出(点击退出、刷新、关闭页面)时若是该桌位还有其余玩家也须要进行广播通知。
玩家准备:每次玩家切换准备状态时判断玩家所在桌位是否有3个玩家准备了,符合条件就能够开始发牌了,这里都准备后就已经进入了抢地主阶段。发牌由服务器来进行,因此原有的发牌代码还须要搬一份到服务器上去,发完牌后分别通知给每一位玩家,给玩家对应的手牌信息,还要随机生成哪一位座位号玩家先开始抢地主,这样客户端收到开始游戏信息后就播放发牌动画,播放完毕后若是是本身先开始抢地主就显示抢地主操做的按钮。
抢地主:玩家叫分后将数据(主要是玩家叫的分数和玩家信息)发给服务器,服务器处理后,将当前叫分交给下一家,广播给当前桌位全部玩家,通知他们上一家叫分多少,还有如今轮到谁叫分抢地主了。这样的客户端就能够显示相应信息,每一个玩家根据本身的座位号显示上家叫分和当前叫分,是本身就显示叫分的操做按钮。肯定地主后再发生底牌信息给每一个玩家,客户端接收到消息后将底牌显示出来。
退出/断线:在这个阶段退出的话我作的处理是直接结束游戏回到准备阶段。由于地主尚未肯定,也没法进行出牌,直接结束是比较好的作法,再让AI帮离线玩家叫分也是能够的,这就看开发者想怎么作了,对于离开的玩家也能够进行减分处罚。
玩家出牌:出牌阶段的轮换跟逻辑抢地主差很少,主要是在客户端渲染会有所差别。玩家将出的牌发给服务器,服务器判断玩家出牌后是否还有手牌,有就继续下家出牌,没有就断定该玩家胜利。这跟单机版逻辑都是同样的,只是这里服务器来作控制了。
退出/断线:在出牌阶段退出后,只有该桌位还有在线玩家,游戏就不会结束,离开的玩家会有AI代为出牌。为了让断线玩家会有回来的时间,AI第一次出牌会延迟出牌,这个使用定时器很容易实现。等玩家重连以后若是这个计时器还没执行再去把这个计时器取消了。
断线重连:这个问题一开始我比较没思路,本身想了个方法实现,不知道算不算好的。实现思路:在出牌阶段,有玩家离开了,就将这个玩家加入到一个离线列表中。当有玩家加入游戏后,再也不是直接分配桌位,而是先去查离线列表,若是找到了,就进到原来的桌位,没有找到再去匹配桌位。这样找到了桌位信息,就能够在客户端显示当前游戏的情况。玩家要是直到游戏结束都没有重连,游戏结束时将本桌离线的玩家从离线列表删除。或者一桌玩家都离线了,那就所有都从离线列表删除。
有玩家完牌后,服务器给全部玩家广播有人游戏结束,发送计算完后的玩家分数、没出完玩家的手牌信息以及胜利玩家最后出的一手牌,该桌位回到准备阶段。客户端个人处理是先显示剩下手牌,最后一手牌等信息,添加个定时器,3秒后游戏界面才渲染成准备阶段状态。
我在开发过程当中,遇到过个问题,控制台一直提示死循环,在报错的地方查了很久,也没发现异样。后面跟进socket.io源码才发现,是当我要发送给客户端的数据中,有一个循环引用的问题,其实在我里面有个对象存着计时器,这样将这个数据对象转换成json时,转换代码就会陷入死循环。这个问题虽然不大,我也知道这种状况会引发转换异常,可是不容易发现,一开始没有引发个人注意,在此记录下。网络对战的斗地主就介绍到这里,有须要改进望提出,互相学习。