博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
手把手教你将单机游戏改造成对战网游(附详细教程)
阅读量:6510 次
发布时间:2019-06-24

本文共 9701 字,大约阅读时间需要 32 分钟。

前言:本Demo原来是Cocos Creator官方的一个Demo,本文章利用了第三方联网插件工具 将其改造成了一个三人对战的Demo,( )。
注意:
1.游戏满三人才可以开启,匹配成功后,玩家通过键盘AD键操纵小怪物向左向右移动抢摘星星。
2.下载Demo源码后,需用Cocos Creator打开工程(建议使用1.7.0及以上版本)。

游戏配置

Demo运行之前需要去配置游戏相关信息,以获取Demo运行所需要的GameID、AppKey、SecretID。如图:

获取到相关游戏信息之后,运行Demo,即可进入房间,准备开始游戏,如图所示:

初始化SDK

在引入SDK之后,在初始化前需要先调用Matchvs.MatchvsEngine.getInstance()获取一个Matchvs引擎对象实例:

var engine = Matchvs.MatchvsEngine.getInstance();复制代码

另外我们需要定义一个对象,该对象定义一些回调方法,用于获取游戏中玩家加入、离开房间、数据收发的信息,这些方法在特定的时刻会被SDK调用。

var response = {    // 可以现在定义一些回调方法,也可以过后再定义。};复制代码

为方便使用,我们把engine和reponse放到单独的文件Mvs.js中,使用module.exports将它们作为全局变量使用:

var engine = Matchvs.MatchvsEngine.getInstance();var response = {};module.exports = {    engine: engine,    response: engine};// 文件路径:assets\scripts\Mvs.js复制代码

其他文件可以用require函数引入engine和reponse:

var mvs = require("Mvs");// 引擎实例:mvs.engine// 引擎回调实现:mvs.response复制代码

完成以上步骤后,我们可以调用初始化接口建立相关资源。

mvs.engine.init(response, channel, platform, gameId);// 文件路径:assets\scripts\Lobby.js复制代码

注意 在整个应用全局,开发者只需要对引擎做一次初始化。

建立连接

接下来,我们就可以从Matchvs获取一个合法的用户ID,通过该ID连接至Matchvs服务端。

获取用户ID:

cc.Class({    onLoad: function() {        mvs.response.registerUserResponse = this.registerUserResponse.bind(this);        mvs.engine.registerUser();    },    registerUserResponse: function(userInfo) {        // 注册成功,userInfo包含相关用户信息    },    // ...})// 文件路径:assets\scripts\Lobby.js复制代码

用户信息需要保存起来,我们使用一个类型为对象的全局变量GLB来存储:

GLB.userInfo = userInfo;复制代码

登录:

cc.Class({    onLoad: function() {        // ...        mvs.engine.login(userInfo.id, userInfo.token, gameId, gameVersion, appKey,            secret, deviceId, gatewayId);        // ...    },    loginResponse: function(loginRsp) {        // 登录成功,loginRsp包含登录相关信息    },    // ...})// 文件路径:assets\scripts\Lobby.js复制代码

加入房间

成功连接至Matchvs后,立即随机匹配加入一个房间进行游戏。

代码如下:

cc.Class({    loginResponse: function() {        // ...        mvs.response.joinRoomResponse = this.joinRoomResponse.bind(this);        mvs.engine.joinRandomRoom(maxPlayer, userProfile);        // ...    },    joinRoomResponse: function(status, userInfoList, roomInfo) {        // 加入房间成功,status表示结果,roomUserInfoList为房间用户列表,roomInfo为房间信息        // ...    },    // ...})// 文件路径:assets\scripts\Lobby.js复制代码

停止加入

我们设定如果有3个玩家匹配成功则满足开始条件且游戏设计中不提供中途加入,此时需告诉Matchvs不要再向房间里加人。

代码如下:

cc.Class({    joinRoomResponse: function(status, userInfoList, roomInfo) {        // 加入房间成功,status表示结果,roomUserInfoList为房间用户列表,roomInfo为房间信息        // ...        if (userIds.length >= GLB.MAX_PLAYER_COUNT) {            mvs.response.joinOverResponse = this.joinOverResponse.bind(this); // 关闭房间之后的回调            var result = mvs.engine.joinOver("");            this.labelLog("发出关闭房间的通知");            if (result !== 0) {                this.labelLog("关闭房间失败,错误码:", result);            }            GLB.playerUserIds = userIds;        }    },    joinOverResponse: function(joinOverRsp) {        if (joinOverRsp.status === 200) {            this.labelLog("关闭房间成功");            // ...        } else {            this.labelLog("关闭房间失败,回调通知错误码:", joinOverRsp.status);        }    },})// 文件路径:assets\scripts\Lobby.js复制代码

在这里需要记下房间的用户列表,记入到全局变量GLB.playerUserIds中,后面要使用到。

发出游戏开始通知

如果收到服务端的房间关闭成功的消息,就可以通知游戏开始了。

cc.Class({    // ...    joinOverResponse: function(joinOverRsp) {        if (joinOverRsp.status === 200) {            this.labelLog("关闭房间成功");            this.notifyGameStart();        } else {            this.labelLog("关闭房间失败,回调通知错误码:", joinOverRsp.status);        }    },    notifyGameStart: function () {        GLB.isRoomOwner = true;        var event = {            action: GLB.GAME_START_EVENT,            userIds: GLB.playerUserIds        }        mvs.response.sendEventResponse = this.sendEventResponse.bind(this); // 设置事件发射之后的回调        mvs.response.sendEventNotify = this.sendEventNotify.bind(this); // 设置事件接收的回调        var result = mvs.engine.sendEvent(JSON.stringify(event));        // ...        // 发送的事件要缓存起来,收到异步回调时用于判断是哪个事件发送成功        GLB.events[result.sequence] = event;     },    sendEventResponse: function (info) {        // ... 输入校验        var event = GLB.events[info.sequence]        if (event && event.action === GLB.GAME_START_EVENT) {            delete GLB.events[info.sequence]            this.startGame()        }    },    sendEventNotify: function (info) {        if (info            && info.cpProto            && info.cpProto.indexOf(GLB.GAME_START_EVENT) >= 0) {            GLB.playerUserIds = [GLB.userInfo.id]            // 通过游戏开始的玩家会把userIds传过来,这里找出所有除本玩家之外的用户ID,            // 添加到全局变量playerUserIds中            JSON.parse(info.cpProto).userIds.forEach(function(userId) {                if (userId !== GLB.userInfo.id) GLB.playerUserIds.push(userId)            });            this.startGame()        }    },    startGame: function () {        this.labelLog('游戏即将开始')        cc.director.loadScene('game')    },})// 文件路径:assets\scripts\Lobby.js复制代码

游戏数据传输

游戏进行中在创建星星、玩家进行向左、向右操作时,我们将这些操作广播给房间内其他玩家。界面上同步展示各个玩家的状态变化。

其中星星是房主创建和展示,然后通知其他玩家,其他玩家收到消息后展示,相关的代码如下:

cc.Class({    onLoad: function() {        mvs.response.sendEventNotify = this.sendEventNotify.bind(this);        // ...    },    sendEventNotify: function (info) {        // ...        if (info.cpProto.indexOf(GLB.NEW_START_EVENT) >= 0) {            // 收到创建星星的消息通知,则根据消息给的坐标创建星星            this.createStarNode(JSON.parse(info.cpProto).position)        } /* 其他else if条件 */    },    // 根据坐标位置创建渲染星星节点    createStarNode: function (position) {        // ...    },    // 发送创建星星事件    spawnNewStar: function () {        if (!GLB.isRoomOwner) return;    // 只有房主可创建星星        var event = {            action: GLB.NEW_START_EVENT,            position: this.getNewStarPosition()        }        var result = mvs.engine.sendEvent(JSON.stringify(event))        if (!result || result.result !== 0)            return console.error('创建星星事件发送失败');        this.createStarNode(event.position);    },    // 随机返回'新的星星'的位置    getNewStarPosition: function () {        // ...    },    // ...})// 文件路径:assets\scripts\Game.js复制代码

玩家进行向左、向右操作时,这些消息会发送给其他玩家:

cc.Class({    setInputControl: function () {        var self = this;        cc.eventManager.addListener({            event: cc.EventListener.KEYBOARD,            onKeyPressed: function (keyCode, event) {                var msg = { action: GLB.PLAYER_MOVE_EVENT };                switch (keyCode) {                    case cc.KEY.a:                    case cc.KEY.left:                        msg.accLeft = true;                        msg.accRight = false;                        break;                    case cc.KEY.d:                    case cc.KEY.right:                        msg.accLeft = false;                        msg.accRight = true;                        break;                    default:                        return;                }                var result = mvs.engine.sendEvent(JSON.stringify(msg));                if (result.result !== 0)                    return console.error("移动事件发送失败");                self.accLeft = msg.accLeft;                self.accRight = msg.accRight;            },            onKeyReleased: function (keyCode, event) {                var msg = { action: GLB.PLAYER_MOVE_EVENT };                switch (keyCode) {                    case cc.KEY.a:                        msg.accLeft = false;                        break;                    case cc.KEY.d:                        msg.accRight = false;                        break;                    default:                        return;                }                var result = mvs.engine.sendEvent(JSON.stringify(msg));                if (result.result !== 0)                    return console.error("停止移动事件发送失败");                if (msg.accLeft !== undefined) self.accLeft = false;                if (msg.accRight !== undefined) self.accRight = false;            }        }, self.node);    },    onLoad: function () {        // ...        this.setInputControl();    }    // ...})// 文件路径:assets\scripts\Player1.jscc.Class({    sendEventNotify: function (info) {        if (/* ... */) {            // ...        } else if (info.cpProto.indexOf(GLB.PLAYER_MOVE_EVENT) >= 0) {            // 收到其他玩家移动的消息,根据消息信息修改加速度            this.updatePlayerMoveDirection(info.srcUserId, JSON.parse(info.cpProto))        } /* 更多else if条件*/    },    // 更新每个玩家的移动方向    updatePlayerMoveDirection: function (userId, event) {        // ...     },    // ...})// 文件路径:assets\scripts\Game.js复制代码

考虑到数据同步会有延迟,不同客户端收到的数据的延迟也会有差异,如果只在同步玩家左右移动的操作数据,那么过一段时间之后,不同客户端的小怪物位置可能会不一样,因此每隔一段时间还是需要再同步一次小怪物的位置、速度和加速度数据:

cc.Class({    onLoad: function () {        // ...        setInterval(() => {            mvs.engine.sendEvent(JSON.stringify({                action: GLB.PLAYER_POSITION_EVENT,                x: this.node.x,                xSpeed: this.xSpeed,                accLeft: this.accLeft,                accRight: this.accRight,                ts: new Date().getTime()            }));        }, 200);        // ..    }    // ...})// 文件路径:assets\scripts\Player1.jscc.Class({    sendEventNotify: function (info) {        if (/* ... */) {            // ...        } else if (info.cpProto.indexOf(GLB.PLAYER_POSITION_EVENT) >= 0) {            // 收到其他玩家的位置速度加速度信息,根据消息中的值更新状态            this.receiveCountValue++;            this.receiveCount.string = "receive msg count: " + this.receiveCountValue;            var cpProto = JSON.parse(info.cpProto);            var player = this.getPlayerByUserId(info.srcUserId);            if (player) {                player.node.x = cpProto.x;                player.xSpeed = cpProto.xSpeed;                player.accLeft = cpProto.accLeft;                player.accRight = cpProto.accRight;            }            // ...         } /* 更多else if条件 */    },    // ...})// 文件路径:assets\scripts\Game.js复制代码

最终效果如下:

搞定。

转载地址:http://vebfo.baihongyu.com/

你可能感兴趣的文章
apache 开启 gzip 压缩服务
查看>>
python mysql
查看>>
开源 免费 java CMS - FreeCMS1.5-建站向导
查看>>
jquery 1.6以上版本 全选
查看>>
AppCan 学习
查看>>
flask框架
查看>>
《疯狂Java讲义》学习笔记(十)异常处理
查看>>
ELK 5.x日志分析 (二) Elasticserach 5.2 安装
查看>>
一次奇怪的AP注册异常问题处理
查看>>
TableStore: 海量结构化数据分层存储方案
查看>>
Unity 4.x游戏开发技巧集锦(内部资料)
查看>>
自适应网页设计
查看>>
HTML5:理解head
查看>>
java SpringUtil获取bean
查看>>
赛门铁克开启“容灾即服务”时代
查看>>
复杂度归纳--小结
查看>>
PHP学习笔记 第八讲 Mysql.简介和创建新的数据库
查看>>
Mysql
查看>>
跨越企业的“中等收入陷阱”
查看>>
Android 开发者必知的开发资源
查看>>