Skip to content

用Matchvs SDK 和 白鹭Egret 开发的多人抢球,用于演示多人匹配、数据传输、帧同步、消息订阅等功能

Notifications You must be signed in to change notification settings

matchvs/demo-Egret

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

71 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Demo简介

为了便于开发者使用和理解,用 Matchvs 的实时联网 SDK 和 白鹭 Egret 开发的多人抢足球,提供了简洁的Demo来展示多人实时联网游戏的开发过程和效果,用于演示多人匹配、数据传输、帧同步、创建房间、获取房间列表、消息订阅、断线重连、修改房间属性等功能。Matchvs。

目录

v3.2.7.0 去除之前的 matchvs_wx 目录。现在目录如下:

┌─── matchvs 支持 Egret SDK库文件
├─── MatchvsDemo_Egret Demo 工程
└─── README.md

Demo下载和体验

Demo 支持使用 Matchvs云和独立部署两种配置模式。

注意:下载Demo源码后,需要使用Egret的Wing打开工程(Wing建议使用4.1.0以上的版本,Egret引擎建议使用5.1.5以上版本)。满三人才可以开始游戏,使用三个不同的浏览器运行。Demo支持三人同时游戏,匹配成功后,玩家通过按住按钮左右滑动来推动小鸭子向左向右移动抢足球。

Demo 使用Matchvs云配置

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

img

创建成功

img

修改 Demo 里 GameData.ts 的游戏配置信息为你自己的游戏信息,并确保 CHANNEL 为 Matchvs 或者 MatchVS , 如果出现MatchVS-Test 的配置或者其他配置请修正。ENVIRONMENT 是在Demo登录界面环境选择的配置 确保配置信息为 { "dev": "alpha", "pro": "release" } ,其中alpha 一般用于测试环境,release 用于生产环境。修改参数可参考下图:

img

如果不了解Login这个函数的参数请到官网查看相关的 API接口说明 文档。

注意: 如果运行不成功请查看 egretProperties.json 文件是否配置了加了如下配置:

{ 
    "name": "matchvs",
    "path": "../matchvs"
}

注意:在 v3.7.2.0 及后发布微信小游戏不需要配置 matchvs_wx 。 v3.7.2.0 之前的版本 如果是要发布成微信小游戏应该吧 path 下面的 ../matchvs  改为 ../matchvs_wx, 如下:

{ 
    "name": "matchvs",
    "path": "../matchvs_wx"
}

把游戏信息配置好就可以运行试玩,Demo运行界面如下,可以点击随机匹配开始:

img

Matchvs SDK 引入

为了使demo游戏逻辑处理和 matchvs sdk 模块划分更加明显,我们把 Matchvs SDK 使用封装在 /src/matchvs 目录下。通过事件触发的机制重新转发 MatchvsSDK 联网数据。/src/matchvs 目录下结构:

MsEngine.ts	: MatchvsEngine 实例对象的单例类型
MsEvent.ts 	:事件定义
MsResponse.ts :MatchvsResponse 实例对象的单例类型

MsEngine.ts 文件构建了 MatchvsEngine 的实例,对Matchvs的接口调用全部在这个类型中,示例代码如下:

module mvs {
	/**
	 * 这个是 matchvs 引擎 接口封装模块,对引擎的所有请求接口进行了二次封装,一些接口调用的参数可以在这里组合,所有对mathvs接口请求都在这里
	 */
	export class MsEngine {
		private static _instance = null;
		private _engine:MatchvsEngine = null; //MatchvsEngine 引擎
		public constructor() {
            //给这个类获取 MatchvsEngine 实例
			this._engine = new MatchvsEngine();
		}

		/**
		 * 获取类实例
		 */
		public static get getInstance():MsEngine{
			if(MsEngine._instance == null){
				MsEngine._instance = new MsEngine();
			}
			return MsEngine._instance;
		}
	}
}

MsResponse.ts 对 MatchvsResponse 回调接口 进行封装,使用 事件触发的机制 对消息进行处理,调用者只需要在使用的时候接受该事件消息,然后释放即可。

module mvs {
	export class MsResponse extends egret.EventDispatcher{
		private static _instance:MsResponse = null;
		private _response:MatchvsResponse = null; //Matchvs 引擎
		public constructor() {
			super();
			this.registResponseCall();
		}
		/**
		 * 获取实例
		 */
		public static get getInstance():MsResponse{
			if(MsResponse._instance == null){
				MsResponse._instance = new MsResponse();
			}
			return MsResponse._instance;
		}
        /**
		 * MatchvsResponse 接口回调的重新注册
		 */
		private registResponseCall(){
			this._response = new MatchvsResponse();
			this._response.initResponse = this.initResponse.bind(this);
			this._response.registerUserResponse = this.registerUserResponse.bind(this);
			this._response.loginResponse = this.loginResponse.bind(this);
			this._response.joinRoomResponse = this.joinRoomResponse.bind(this);
			this._response.joinRoomNotify = this.joinRoomNotify.bind(this);
            ....
        }
    }
}

初始化SDK

首先要先初始化,设置好请求回调类型,和一些游戏信息。初始化示例代码如下:

MsEngine.ts 和 MsResponse.ts 文件定义

//MsEngine.ts 请求
public init(channel:string, platform:string, gameID:number):number{
    this._response = MsResponse.getInstance.getResponse();
    let res = this._engine.init(MsResponse.getInstance.getResponse(),channel,platform,gameID);
    if (res !== 200){
        console.info("[MsEngine init failed] resCode:",res);
        return res;
    }
    console.info("[MsEngine init seccess] resCode:",res);
    return res;
}

//MsResponse.ts 回调
private initResponse(status:number){
    console.info("initResponse status:",status);
    this.dispatchEvent(new egret.Event(MsEvent.EVENT_INIT_RSP,false,false,{status:status}));
}

使用示例

//LoginView.ts
 button.addEventListener(egret.TouchEvent.TOUCH_TAP, e => {
     GameData.configEnvir(input.text, cbx.selected);
     console.log(" environment=" + GameData.DEFAULT_ENV + " gameid=" + GameData.gameID);
     //这里调用 MsEngine.ts 中的函数     
     let result = mvs.MsEngine.getInstance.init(GameData.CHANNEL, GameData.DEFAULT_ENV, GameData.gameID);
        }, this);


//LoginView.ts 在页面用户监听到的回调事件
private initResponse(ev:egret.Event) {
    console.log("initResponse,status:" + ev.data.status);
    ...
}

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

注册用户

接下来,我们就可以从Matchvs获取一个合法的用户ID,通过该ID连接至Matchvs服务端。这个用户ID每次获取的都是不同的,如果需要固定的用户ID参考 第三方绑定

MsEngine.ts 和 MsResponse.ts 文件定义

//MsEngine.ts
public registerUser():number{
    //这里调用注册用户接口
    let res = this._engine.registerUser();
    if (res !== 0){
        console.error("[MsEngine registerUser failed] resCode:",res);
        return res;
    }
    console.info("[MsEngine registerUser seccess] resCode:",res);
    return res;
}

//MsResponse.ts 注册回调
private registerUserResponse(userInfo:MsRegistRsp){
    console.info("registerUserResponse userInfo ",JSON.stringify(userInfo));
    this.dispatchEvent(new egret.Event(MsEvent.EVENT_REGISTERUSER_RSP,false,false,userInfo));
}

使用示例

//LoginView.ts
mvs.MsResponse.getInstance.addEventListener(mvs.MsEvent.EVENT_REGISTERUSER_RSP, this.registerUserResponse, this);


 private initResponse(ev:egret.Event) {
     ......
     //获取微信信息失败,注册游客身份登录
     console.info("获取信息失败:",res);
     mvs.MsEngine.getInstance.registerUser();
 }

private registerUserResponse(ev:egret.Event) {
    let userInfo = ev.data;
    GameData.gameUser.id = userInfo.userID;
    GameData.gameUser.name = userInfo.name;
    GameData.gameUser.avatar = userInfo.avatar;
    GameData.gameUser.token = userInfo.token;
    .....
}

登录

获取到有效用户ID就可以登录到 matchvs 联网服务

MsEngine.ts 和 MsResponse.ts 文件定义

// MsEngine.ts 登录请求
public login(userID:number, token:string, gameID:number, appkey:string, secretkey:string):number{
    let res = this._engine.login(userID,token,gameID,1,appkey,secretkey,"eglejjddg",0);
    console.info("[MsEngine login] resCode:",res);
    return res;
}

// MsResponse.ts 登录回调
private loginResponse(login:MsLoginRsp){
    console.info("[loginResponse] "+JSON.stringify(login));
    this.dispatchEvent(new egret.Event(MsEvent.EVENT_LOGIN_RSP,false,false, login));
}

使用示例

//LoginView.ts
private registerUserResponse(ev:egret.Event) {
    if(userInfo.status == 0){
        //登录
        mvs.MsEngine.getInstance.login(userInfo.userID, userInfo.token, GameData.gameID,GameData.appkey, GameData.secretKey);
    }
}

/**
* 调用 matchvs login 接口回调处理
*/
private loginResponse(ev:egret.Event) {
    mvs.MsResponse.getInstance.removeEventListener(mvs.MsEvent.EVENT_LOGIN_RSP, this.loginResponse,this);
    let login = ev.data;
    console.log("loginResponse, status=" + login.status);
    if (login.status != 200) {
        console.log("登陆失败");
    } else {
        console.log("登陆成功 roomID=" + login.roomID);
        ......
    }
}

加入房间

成功连接至Matchvs后,就会进入到Demo的游戏大厅界面,如上面游戏配置中的游戏大厅图。点击随机匹配可以开始加入随机房间啦。

MsEngine.ts 和 MsResponse.ts 文件定义

//MsEngine.ts 随机加入房间
public joinRandomRoom(maxPlayer:number, userProfile:string):number{
    let res = this._engine.joinRandomRoom(maxPlayer,userProfile);
    console.info("[MsEngine joinRandomRoom ] resCode:",res);
    return res;
}
//MsResponse.ts 随机加入房间回调
private joinRoomResponse(status:number, roomUserInfoList:Array<MsRoomUserInfo>, roomInfo:MsRoomInfo){
    if(status == 200){
        let data = {
            status:status,
            userList:roomUserInfoList,
            roomInfo:roomInfo
        }
        this.dispatchEvent(new egret.Event(MsEvent.EVENT_JOINROOM_RSP, false, false, data));
        return ;
    }
    console.error("[joinRoomResponse error:]", status);
    return;
}
private joinRoomNotify(roomUserInfo:MsRoomUserInfo){
    console.info("[joinRoomNotify] "+roomUserInfo.userProfile);
    let data = {
        userId : roomUserInfo.userId, 
        userProfile : roomUserInfo.userProfile};
    this.dispatchEvent(new egret.Event(MsEvent.EVENT_JOINROOM_NTFY, false, false, data));
}

使用示例

//MatchView.ts 加入房间
mvs.MsResponse.getInstance.addEventListener(mvs.MsEvent.EVENT_JOINROOM_RSP, this.joinRoomResponse,this);
mvs.MsResponse.getInstance.addEventListener(mvs.MsEvent.EVENT_JOINROOM_NTFY, this.joinRoomNotify,this);

mvs.MsEngine.getInstance.joinRandomRoom(GameData.maxPlayerNum,infostr);

//加入房间回调
private joinRoomResponse(event:egret.Event) {
    let data = event.data;
    let roomInfo = data.roomInfo;
    let roomuserInfoList = data.userList;
    //加入房间成功,status表示结果,roomUserInfoList为房间用户列表,roomInfo为房间信息
    if (data.status !== 200) {
        console.log("joinRoomResponse,status:" + data.status);
        return;
    }
    //这里处理自己加入房间的逻辑
}
//加入房间异步回调
private joinRoomNotify(ev:egret.Event) {
    let roomUserInfo = ev.data;
    //获取用户头像和昵称
    let usr = this.addPlayUser(roomUserInfo.userId,roomUserInfo.userProfile);
    this.showPlayUser(usr);
}

停止加入

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

MsEngine.ts 和 MsResponse.ts 文件定义

//MsEngine.ts 关闭房间
public joinOver(cpProto:string):number{
    let res = this._engine.joinOver(cpProto);
    console.info("[MsEngine joinOver ] resCode:",res);
    return res;
}

//MsResponse.ts 关闭房间回调
private joinOverResponse(rsp:MsJoinOverRsp){
    console.info("[joinOverResponse] "+ JSON.stringify(rsp));
    let data = {status:rsp.status,cpProto:rsp.cpProto};
    this.dispatchEvent(new egret.Event(MsEvent.EVENT_JOINOVER_RSP, false, false,data));
}
//其他玩家收到异步回调
private joinOverNotify(Info:MsJoinOverNotifyInfo){
    console.info("[joinOverNotify] ");
    let data = {roomID:Info.roomID,userID:Info.srcUserID,cpProto:Info.cpProto};
    this.dispatchEvent(new egret.Event(MsEvent.EVENT_JOINOVER_NTFY, false, false,data));
}

使用示例

//MatchView.ts 关闭房间
mvs.MsResponse.getInstance.addEventListener(mvs.MsEvent.EVENT_JOINOVER_NTFY, this.joinOverNotify,this);
mvs.MsResponse.getInstance.addEventListener(mvs.MsEvent.EVENT_JOINOVER_RSP, this.joinOverResponse,this);
......
mvs.MsEngine.getInstance.joinOver("关闭房间");
......
//关闭房间回调
private joinOverResponse(ev:egret.Event) {
    let rsp = ev.data;
    if (rsp.status === 200) {
        console.log("关闭房间成功");
        ......
    } else {
        console.log("关闭房间失败,回调通知错误码:", rsp.status);
    }
    ......
}

/**
* 关闭房间异步回调
*/
private joinOverNotify(ev:egret.Event) {
    let notifyInfo = ev.data;
    console.log("userID:" + notifyInfo.userID + " 关闭房间:" + notifyInfo.roomID + " cpProto:" + notifyInfo.cpProto);
    ......
}

发出游戏开始通知

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

MsEngine.ts 和 MsResponse.ts 文件定义

//MsEngine.ts 发送消息
public sendEvent(data:string):any{
    let res = this._engine.sendEvent(data);
    return res;
}
//MsResponse.ts 接收消息
private sendEventResponse(rsp:MsSendEventRsp){
    let data = {
        status:rsp.status,
        sequence:rsp.sequence
    };
    this.dispatchEvent(new egret.Event(MsEvent.EVENT_SENDEVENT_RSP, false, false, data));
}
/**
* 发送消息异步回调
*/
private sendEventNotify(eventInfo:MsSendEventNotify){
    //console.info("[sendEventNotify] "+JSON.stringify(eventInfo));
    let data = {
        srcUserId:eventInfo.srcUserId,
        cpProto:eventInfo.cpProto
    };
    this.dispatchEvent(new egret.Event(MsEvent.EVENT_SENDEVENT_NTFY, false, false, data));
}

使用示例

class MatchView ...{

    ....

    /**
     * 开始游戏
     */
    private notifyGameStart() {
        GameData.isRoomOwner = true;
        let arrs = [];
        this._gameUserList.forEach((element)=>{
            arrs.push({id:element.id,name:element.name,avatar:element.avatar});
        });

        let event = {
            action: GameData.gameStartEvent,
            userIds: arrs
        };

        /**
         * 发送开始游戏消息
         */
        let result = mvs.MsEngine.getInstance.sendEvent(JSON.stringify(event));
        if (result.result !== 0){
            return console.log('发送游戏开始通知失败,错误码' + result.result);
        }
        // 发送的事件要缓存起来,收到异步回调时用于判断是哪个事件发送成功
        GameData.events[result.sequence] = event;
        console.log("发起游戏开始的通知,等待回复");
    }

    private sendEventResponse(ev:egret.Event) {
        let rsp = ev.data;
        if (rsp.status !== 200) {
            return console.log('事件发送失败,status:' + status);
        }

        var event = GameData.events[rsp.sequence]

        if (event && event.action === GameData.gameStartEvent) {
            delete GameData.events[rsp.sequence];
            this.release();
            GameSceneView._gameScene.play();
        }
    }

    private sendEventNotify(ev:egret.Event) {
        let sdnotify = ev.data;
        if (sdnotify
            && sdnotify.cpProto
            && sdnotify.cpProto.indexOf(GameData.gameStartEvent) >= 0) {
            this._gameUserList = [];
            this._gameUserList .push(GameData.gameUser);
            // 通过游戏开始的玩家会把userIds传过来,这里找出所有除本玩家之外的用户ID,
            // 添加到全局变量playerUserIds中
            JSON.parse(sdnotify.cpProto).userIds.forEach((element)=> {
                let gUser:GameUser = new GameUser;
                gUser.avatar = element.avatar;
                gUser.name = element.name;
                gUser.id = element.id;
                if(gUser.id !==GameData.gameUser.id ){
                    this._gameUserList.push(gUser);
                }
            });
            GameData.playerUserIds = this._gameUserList;
            this.release();
            GameSceneView._gameScene.play();
        }
    }
}

游戏信息同步

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

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

//GamePlayerView.ts
private sendEventNotify(event:egret.Event) {
	let sdnotify = event.data;
    if (sdnotify && sdnotify.cpProto) {
        if (sdnotify.cpProto.indexOf(GameData.newStarEvent) >= 0) {
            if(sdnotify.srcUserId != GameData.gameUser.id) {
                let info = JSON.parse(sdnotify.cpProto);
                GameData.starPositionX = info.x;
                GameData.starPositionY = info.y;
                this.deleteStar();
                this.createStar();
            }
        } else if (sdnotify.cpProto.indexOf(GameData.playerPositionEvent) >= 0) {
            // 收到其他玩家的位置速度加速度信息,根据消息中的值更新状态
            this._receiveCountValue++;
            this._receiveMsgCountLabel.text = "receive msg count: " + this._receiveCountValue;
            let cpProto = JSON.parse(sdnotify.cpProto);
            if (sdnotify.srcUserId == GameData.gameUser.id) {
                let delayValue = new Date().getTime() - cpProto.ts;
                if (this._minDelayValue === undefined || delayValue < this._minDelayValue) {
                    this._minDelayValue = delayValue;
                }
                if (this._maxDelayValue === undefined || delayValue > this._maxDelayValue) {
                    this._maxDelayValue = delayValue;
                }
                this._delayLabel.text = "delay: " + delayValue + "\n" + "minDelay: " + this._minDelayValue + "\n" + "maxDelay: " + this._maxDelayValue; 
            } else {
                //console.log("cpProto=" + JSON.stringify(cpProto) + " name1=" + this._egretBird1.name + "name2=" + this._egretBird2.name);
                if (this._egretBird1.name == cpProto.uid) {
                    this._egretBird1.x = cpProto.x;
                    this._egretBird1.y = cpProto.y;
                } else if (this._egretBird2.name == cpProto.uid) {
                    this._egretBird2.x = cpProto.x;
                    this._egretBird2.y = cpProto.y;
                }
            }
        } else if (sdnotify.cpProto.indexOf(GameData.reconnectStartEvent) >= 0) {
            let info = JSON.parse(sdnotify.cpProto);
            if(info.userID === GameData.gameUser.id && GameData.starPositionX === 0) {
                GameData.starPositionX = info.x;
                GameData.starPositionY = info.y;
                GameData.playerUserIds = info.PlayerScoreInfos;
                GameData.playerUserIds.forEach((value)=>{
                    if(value.id === info.userID){
                        this._score = value.pValue;
                    }
                });
                this._countDownLabel.text = info.timeCount;
                this.deleteStar();
                this.createStar();
                this.setScoreLabel();
            }
        } else if (sdnotify.cpProto.indexOf(GameData.changeStarEvent) >= 0) {
            if(sdnotify.srcUserId != GameData.gameUser.id) {
                let info = JSON.parse(sdnotify.cpProto);
                this.changeStarPosition(info.x, info.y);
                this.setUserScore(sdnotify.srcUserId, info.score);
            }
        }else if(sdnotify.cpProto.indexOf(GameData.reconnectReadyEvent) >= 0){
            console.log("重新连接收到消息");
            let eventTemp = {
                action: GameData.reconnectStartEvent,
                userID: sdnotify.srcUserId,
                PlayerScoreInfos:GameData.playerUserIds,
                timeCount:Number(this._countDownLabel.text),
                x: this._star.x,
                y: GameData.defaultHeight
            }
            //发送游戏数据
            let result = mvs.MsEngine.getInstance.sendEvent(JSON.stringify(eventTemp));
            if (!result || result.result !== 0) {
                return console.log('重连创建足球事件发送失败');
            }
            console.log('重连创建足球事件发送成功');
        }
    }
}

小鸭子左右移动的时候会同步位置的信息给其他用户:

// GamePlayerView.ts
//左移动
private onButtonClickLeft(e: egret.TouchEvent) {
    //console.log("onButtonClickLeft");
    if(this._egretBird0.x <= 0){
        this._egretBird0.x = 0;
    }else{
        this._egretBird0.x -= 20;
    }
    this.processStar();
}
//右移动
private onButtonClickRight(e: egret.TouchEvent) {

    if(this._egretBird0.x >= GameData.width){
        this._egretBird0.x = GameData.width;
    }else{
        this._egretBird0.x += 20;
    }
    this.processStar();
}

private processStar() {
    let length:number = Math.abs(this._egretBird0.x - this._star.x);
    console.log("length:" + length);
    if (length <= (this._star.width + this._egretBird0.width)/2) {
        this._score++;
        this.setUserScore(GameData.gameUser.id, this._score);
        let newX:number = 0;
        newX = Math.random() * this.stage.width;
        this.changeStarPosition(newX, GameData.defaultHeight);
        let eventTemp = {
            action: GameData.changeStarEvent,
            x: this._star.x,
            y: GameData.defaultHeight,
            score: this._score,
        }
        let result = mvs.MsEngine.getInstance.sendEvent(JSON.stringify(eventTemp));
        if (!result || result.result !== 0)
            return console.log('足球位置变更事件发送失败:' + JSON.stringify(result));			
    }
}

最终效果如下:

img

修改房间属性

在创建房间界面可以选择不同的房间地图,房主在选择地图时,其他玩家会更新地图选项。调用修改房间属性的示例代码如下:

MsEngine.ts 和 MsResponse.ts 文件定义

// MsEngine.ts
public setRoomProperty(roomID:string, roomProperty:string):number{
    let res = this._engine.setRoomProperty(roomID, roomProperty);
    console.info("[MsEngine setRoomProperty ] resCode:", res);
    return res;
}
// MsResponse.ts 异步回调
private setRoomPropertyNotify(notify:MsRoomPropertyNotifyInfo){
    console.info("[setRoomPropertyNotify] info:");
    let data = {
        roomID:notify.roomID,
        userID:notify.userID,
        roomProperty:notify.roomProperty
    };
    this.dispatchEvent(new egret.Event(MsEvent.EVENT_SETROOMPROPERTY_NTFY, false, false, data));
}

/**
* 设置房间属性回调
*/
private setRoomPropertyResponse(rsp:MsSetRoomPropertyRspInfo){
    console.info("[setRoomPropertyResponse] info:", rsp.status);
    let data = {
        roomID:rsp.roomID,
        userID:rsp.userID,
        roomProperty:rsp.roomProperty,
        status:rsp.status
    };
    this.dispatchEvent(new egret.Event(MsEvent.EVENT_SETROOMPROPERTY_RSP, false, false, data));
}

使用示例

//CreateRoomView.ts
class CreateRoomView extends egret.DisplayObjectContainer{
    //首先要设置回调
    //设置房间属性
    mvs.MsResponse.getInstance.addEventListener(mvs.MsEvent.EVENT_SETROOMPROPERTY_RSP, this.setRoomPropertyResponse,this);
mvs.MsResponse.getInstance.addEventListener(mvs.MsEvent.EVENT_SETROOMPROPERTY_NTFY, this.setRoomPropertynotify,this);
    ....
    private radioChangeHandler(evt:eui.UIEvent):void {
        if(evt.target.value === 0){
            //地图A
            GameData.roomPropertyValue = GameData.roomPropertyType.mapA;
          mvs.MsEngine.getInstance.setRoomProperty(this._roomID,GameData.roomPropertyType.mapA);
        }else {
            //地图B
            GameData.roomPropertyValue = GameData.roomPropertyType.mapB;
          mvs.MsEngine.getInstance.setRoomProperty(this._roomID,GameData.roomPropertyType.mapB);
        }
    }
    /**
     * 他人设置房间属性回调事件
     */
    private setRoomPropertynotify(ev:egret.Event):void{
        let notify = ev.data;
        console.log("roomProperty = "+notify.roomProperty);
        if(notify.roomProperty === GameData.roomPropertyType.mapB){
            GameData.roomPropertyValue = GameData.roomPropertyType.mapB;
            this._gameMapB.selected = true;
        }else{
            GameData.roomPropertyValue = GameData.roomPropertyType.mapA;
            this._gameMapA.selected = true;
        }
    }
    /**
     * 自己设置房间数据回调事件
     */
    private setRoomPropertyResponse(ev:egret.Event):void{
        console.log("roomProperty = "+ev.data.roomProperty);
    }
}

设置属性界面如下:

img

断线重连

在游戏里面如果网络断开,可以调用 reconnect 函数重新连接。断线重连限制在断线后20秒内才能重新连接到房间,超过20秒服务会把该玩家踢出房间不能重新连接。

####MsEngine.ts 和 MsResponse.ts 文件定义

//MsEngine.ts
/**
* 断线重连
* @returns {number}
*/
public reconnect():number{
    let res = this._engine.reconnect();
    console.info("[MsEngine reconnect ]", res);
    return res;
}
//MsResponset.ts
/**
* 断线重新连接回调
*/
private reconnectResponse(status:number, roomUserInfoList:Array<MsRoomUserInfo>, roomInfo:MsRoomInfo){
    console.info("[reconnectResponse] info:", status);
    let data = {
        status : status,
        roomUserInfoList : roomUserInfoList,
        roomInfo : roomInfo
    };
    this.dispatchEvent(new egret.Event(MsEvent.EVENT_RECONNECT_RSP, false, false, data));
}

使用示例

断线重新连接分为两种情况:

  • 没有重新启动程序:在游戏进行时网络断开,直接调用 reconnect 重新连接到游戏。
//ErrorView.ts
private mbuttonReconnRoom(event:egret.TouchEvent){
    //重连
    GameSceneView._gameScene.reconnectView();
}

// ReconnectView.ts 断线重连
private timerFunc(event: egret.Event){
    this._msglabel.text = "正在重新连接......"+this._reconnctTimes+"/"+this._totalTimes;
    console.log(this._msglabel.text)
    /重连
    let res = mvs.MsEngine.getInstance.reconnect();
    this._reconnctTimes++;
    if(this._reconnctTimes > this._totalTimes){
        this._timer.stop();
        if(res === 0){
            mvs.MsEngine.getInstance.leaveRoom("");
            GameSceneView._gameScene.lobby();
        }else{
            mvs.MsEngine.getInstance.leaveRoom("");
            GameSceneView._gameScene.login();
        }
        this.release();
    }
}
// 重连回调
private reconnectResponse(envet:egret.Event){
    if(!data.status || data.status !== 200){
        console.log("重连失败"+this._reconnctTimes);
        this._msglabel.text = "重连失败......"+this._reconnctTimes+"/"+this._totalTimes;
        ......
    }else{
        console.log("重连成功status:"+data.status+" 重连次数:"+this._reconnctTimes);
        ......
    }
}

注意:reconnect 接口调用不能直接写在 errorResponse 回调里面,不然会出现无限循环调用的情况。建议使用按钮提示,或者限制重连次数。

  • 重新加载程序:先调用login 然后判断 loginResponse 中的参数 roomID 是否为0 如果不为 0 就调用reconnect 重连到房间。
//LoginView.ts
private loginResponse(login:MsLoginRsp) {
		console.log("loginResponse, status=" + login.status);
		if (login.status != 200) {
			console.log("登陆失败");
		} else {
			console.log("登陆成功 roomID="+login.roomID);
            if(login.roomID !== "0"){
                //重新连接
                GameSceneView._gameScene.reconnectView();
            }else{
                GameSceneView._gameScene.lobby();
            }
		}
	}

// ReconnectView.ts
private timerFunc(event: egret.Event){
    this._msglabel.text = "正在重新连接......"+this._reconnctTimes+"/"+this._totalTimes;
    console.log(this._msglabel.text)
    //重连
    let res = mvs.MsEngine.getInstance.reconnect();
    this._reconnctTimes++;
    if(this._reconnctTimes > this._totalTimes){
        this._timer.stop();
        if(res === 0){
            mvs.MsEngine.getInstance.leaveRoom("");
            GameSceneView._gameScene.lobby();
        }else{
            mvs.MsEngine.getInstance.leaveRoom("");
            GameSceneView._gameScene.login();
        }
        this.release();
    }
}

private reconnectResponse(status:number, roomUserInfoList:Array<MsRoomUserInfo>, roomInfo:MsRoomInfo){
        this._timer.stop();
        if(status !== 200){
            console.log("重连失败"+this._reconnctTimes);
        }else{
            console.log("重连成功status:"+status+" 重连次数:"+this._reconnctTimes);
        }
    ......
}

Demo 独立部署配置

上面介绍的是在Matchvs官网控制台创建游戏账号使用Demo,下面我们来介绍在Demo中配置独立部署参数。在使用Demo之前需要先独立部署Matchvs服务到你自己的服务器,并配置好游戏信息。在Demo的login页面右下角有一个独立部署按钮,如下图:

img

进入到独立部署参数配置界面。

img

这些参数来自你部署Matchvs服务的时候配置的信息。

服务地址:部署Matchvs 服务 (可以执行 ./matchvs_tool deploy info,可查看部署结果及运行服务) wss_proxy 中的value。

gameID,appKey,secretKey 在独立部署中执行 ./matchvs_tool game add 添加的信息例如:

[root@deployer bin]# ./matchvs_tool game add 1000 appkey123abc appsecret123abc

执行 ./matchvs_tool game list可以查看已添加的游戏信息列表。

注意:userID 和 token 不是使用 MatchvsSDK 中的 registerUser 接口获取的,而是你自己设置的用户。如果你没有设置 userID 和 token 你可以暂时 随便填写一个。具体的请阅读独立部署服务端配置文档。

填写好服务信息和用户信息就可以登录到Demo的大厅界面了。

Matchvs SDK 独立部署接入指引

独立部署与使用Matchvs云游戏接入SDK不同之处就是调用初始化接口不一样,还有不需要调用registerUser接口,userID 和 token是由开发者自己定义。独立部署使用的初始化接口是premiseInit 。初始化回调接口还是与之前一样使用 initResponse 接口

独立部署SDK初始化

MsEngine.ts 和 MsResponse.ts 文件定义

//MsEngine.ts 请求
public premiseInit(endPoint:string, gameID:number):number{
    this._response = MsResponse.getInstance.getResponse();
    let res = this._engine.premiseInit( MsResponse.getInstance.getResponse(),endPoint, gameID);
    if (res !== 0){
        console.info("[MsEngine premiseInit failed] resCode:",res);
        return res;
    }
    console.info("[MsEngine premiseInit seccess] resCode:",res);
    return res;
}

//MsResponse.ts 回调
private initResponse(status:number){
    console.info("initResponse status:",status);
    this.dispatchEvent(new egret.Event(MsEvent.EVENT_INIT_RSP,false,false,{status:status}));
}

使用示例

//PremiseLoginUI.ts
 button.addEventListener(egret.TouchEvent.TOUCH_TAP, e => {
     GameData.configEnvir(input.text, cbx.selected);
     console.log(" environment=" + GameData.DEFAULT_ENV + " gameid=" + GameData.gameID);
     //这里调用 MsEngine.ts 中的函数     
     let result = mvs.MsEngine.getInstance.init(GameData.CHANNEL, GameData.DEFAULT_ENV, GameData.gameID);
        }, this);
/**
 * 初始化
 */
private premiseInit(event:egret.TouchEvent){
    let endPoint:string = this.txt_endport.text;
    let gameID:number = Number(this.txt_gameID.text);
    mvs.MsEngine.getInstance.premiseInit(endPoint,gameID);
}

//LoginView.ts 在页面用户监听到的回调事件
private initResponse(ev:egret.Event) {
    console.log("initResponse,status:" + ev.data.status);
    ...
}

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

登录

登录以及其他操作都是使用与MatchvsSDK云游戏是一样的。