【Cocos Creator 联机实战教程(2)】——匹配系统

lili

发布日期: 2018-12-07 14:58:43 浏览量: 1762
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

1.知识点讲解

大型多人互动游戏一般都会有一个匹配系统,用来让玩家进行联网游戏,现在我们来讲一讲这种系统吧,我们可以做个比较简单的双人对战匹配系统。

我们让每一对匹配成功的玩家进入一个独立的房间,所以不同的房间的通信应该互不影响,由于不同场景的通信内容不同,所以不同场景的通信也应该独立

我们把这个游戏的匹配过程比作开房的过程,

  • 如果有一个人进入了宾馆,那么他最先进入的区域就是hall(大厅),当然他可能就是逛逛,又推门出去

  • 当他想休息时他就去前台开个房,那么他就进入了queue(队列),并断开hall的通信

  • 当另一个人也想休息的时候也去前台排队,当个queue里有两个人的时候,前台小姐就给了他俩一个空闲房间的钥匙,他们就一起进入了一个独立的room,并断开queue的通信

  • 以上循环,房间数有限,在房间满的时候不能匹配成功

当然,你也可以根据实际情况升级这个匹配系统,比如,分等级的匹配(开不同的队列等待)。

注意:房卡游戏虽然也用到了房间这个概念,但不是匹配,这种游戏更像唱卡拉OK。进入大厅后,组织者去开个房间,其他人一起进。或者迟到的人拿着房间号直接进去。

2. 步骤

我们的游戏分为三个场景

游戏启动的时候进入menu场景,当玩家点击对战时进入match场景,匹配成功进入game场景,取消匹配返回menu场景,游戏结束返回menu场景

我们在Global里定义socket

  1. window.G = {
  2. globalSocket:null,//全局
  3. hallSocket:null,//大厅
  4. queueSocket:null,//队列
  5. roomSocket:null,//房间
  6. gameManager:null,
  7. chessManager:null,
  8. stand:null,
  9. }

menu场景启动时,我们连接hallSocket,开始匹配时,断开hallSocket

  1. cc.Class({
  2. extends: cc.Component,
  3. onLoad: function () {
  4. G.globalSocket = io.connect('127.0.0.1:4747');
  5. //断开连接后再重新连接需要加上{'force new connection': true}
  6. G.hallSocket = io.connect('127.0.0.1:4747/hall',{'force new connection': true});
  7. },
  8. onBtnStart() {
  9. G.hallSocket.disconnect();
  10. cc.director.loadScene('match');
  11. }
  12. });

进入match场景,连接queueSocket,先进入queue的玩家主场黑棋先手,后进入客场白棋后手(这个逻辑是服务端判断的),匹配成功时,服务端会发送roomId,玩家进入相应的房间,并断开queueSocket的通信

  1. const Constants = require('Constants');
  2. const STAND = Constants.STAND;
  3. cc.Class({
  4. extends: cc.Component,
  5. onLoad: function () {
  6. G.queueSocket = io.connect('127.0.0.1:4747/queue', { 'force new connection': true });
  7. G.queueSocket.on('set stand', function (stand) {
  8. if (stand === 'black') {
  9. G.stand = STAND.BLACK;
  10. } else if (stand === 'white') {
  11. G.stand = STAND.WHITE;
  12. }
  13. });
  14. G.queueSocket.on('match success', function (roomId) {
  15. cc.log('match success' + roomId);
  16. G.roomSocket = io.connect('127.0.0.1:4747/rooms' + roomId, { 'force new connection': true });
  17. G.queueSocket.disconnect();
  18. cc.director.loadScene('game');
  19. });
  20. },
  21. onBtnCancel() {
  22. G.queueSocket.disconnect();
  23. cc.director.loadScene('menu');
  24. }
  25. });

在game场景中,如果游戏结束我们就断掉roomSocket回到menu场景

  1. startGame() {
  2. this.turn = STAND.BLACK;
  3. this.gameState = GAME_STATE.PLAYING;
  4. this.showInfo('start game');
  5. },
  6. endGame() {
  7. let onFinished = () =>{
  8. G.roomSocket.disconnect();
  9. cc.director.loadScene('menu');
  10. }
  11. this.infoAnimation.on('finished',onFinished,this);
  12. this.gameState = GAME_STATE.OVER;
  13. this.showInfo('game over');
  14. },

服务端完整逻辑

  1. let app = require('express')();
  2. let server = require('http').Server(app);
  3. let io = require('socket.io')(server);
  4. server.listen(4747, function() {
  5. console.log('listening on:4747');
  6. });
  7. let MAX = 30;//最大支持连接房间数
  8. let hall = null;//大厅
  9. let queue = null;//匹配队列
  10. let rooms = [];//游戏房间
  11. function Hall() {
  12. this.people = 0;
  13. this.socket = null;
  14. }
  15. function Room(){
  16. this.people = 0;
  17. this.socket = null;
  18. }
  19. function Queue(){
  20. this.people = 0;
  21. this.socket = null;
  22. }
  23. hall = new Hall();
  24. queue = new Queue();
  25. for(let n = 0;n < MAX;n++){
  26. rooms[n] = new Room();
  27. }
  28. function getFreeRoom(){
  29. for(let n = 0;n < MAX;n++){
  30. if(rooms[n].people === 0){
  31. return n;
  32. }
  33. }
  34. return -1;
  35. }
  36. io.people = 0;
  37. io.on('connection',function(socket){
  38. io.people++;
  39. console.log('someone connected');
  40. socket.on('disconnect',function(){
  41. io.people--;
  42. console.log('someone disconnected');
  43. });
  44. })
  45. hall.socket = io.of('/hall').on('connection', function(socket) {
  46. hall.people++;
  47. console.log('a player connected.There are '+hall.people+' people in hall');
  48. hall.socket.emit('people changed',hall.people);
  49. socket.on('disconnect',function(){
  50. hall.people--;
  51. console.log('a player disconnected.There are '+hall.people+' people in hall');
  52. hall.socket.emit('people changed',hall.people);
  53. });
  54. });
  55. queue.socket = io.of('/queue').on('connection',function(socket){
  56. queue.people++;
  57. console.log('someone connect queue socket.There are '+queue.people+' people in queue');
  58. if(queue.people === 1){
  59. socket.emit('set stand','black');
  60. }else if(queue.people === 2){
  61. socket.emit('set stand','white');
  62. let roomId = getFreeRoom();
  63. console.log(roomId+"roomId");
  64. if(roomId >= 0){
  65. queue.socket.emit('match success',roomId);
  66. console.log('match success.There are '+queue.people+' people in queue');
  67. }else{
  68. console.log('no free room!');
  69. }
  70. }
  71. socket.on('cancel match',function(){
  72. queue.people--;
  73. console.log('someone cancel match.There are '+queue.people+' people in queue');
  74. });
  75. socket.on('disconnect',function(){
  76. queue.people--;
  77. console.log('someone disconnected match.There are '+queue.people+' people in queue');
  78. });
  79. });
  80. for(let i = 0;i < MAX;i++){
  81. rooms[i].socket = io.of('/rooms'+i).on('connection',function(socket){
  82. rooms[i].people++;
  83. console.log('some one connected room'+i+'.There are '+rooms[i].people+' people in the room');
  84. socket.on('update chessboard',function(chessCoor){
  85. socket.broadcast.emit('update chessboard',chessCoor);
  86. });
  87. socket.on('force change turn',function(){
  88. socket.broadcast.emit('force change turn');
  89. });
  90. socket.on('disconnect',function(){
  91. rooms[i].people--;
  92. console.log('someone disconnected room'+i+'.There are '+rooms[i].people+' people in the room');
  93. });
  94. });
  95. }

3. 总结

我们做的是比较简单的匹配系统,实际上还有匹配算法(选择排队的顺序不仅仅是先来后到)。

这是我们需要掌握的新知识,除此之外我们都可以使用之前的知识点完成游戏。

注意以下问题:

  • 跨场景访问变量

在util下面有两个脚本,Constants用来存储游戏常量,然后其他地方需要常量时

  1. const Constants = require('Constants');
  2. const GAME_STATE = Constants.GAME_STATE;
  3. const STAND = Constants.STAND;
  4. const CHESS_TYPE = Constants.CHESS_TYPE;

Global存储全局控制句柄,需要访问他们的时候,就可以通过(G.)的方式

  • 控制单位应该是脚本而不是节点

本教程部分素材来源于网络。

上传的附件 cloud_download reversi-server.js ( 2.91kb, 5次下载 ) cloud_download client.zip ( 8.75mb, 6次下载 )
eject