许久不见,本帧为帧同步版本,之前有一篇状态同步的:
还有人问,为什么官方有MGOBE还需要自己写联机方案呢,简单的告诉你,mgobe最便宜的收费是0.0025元/人/天,而实际上小游戏之边的收益大概在0.02-0.04分之前,这样的话,相当于游戏收益的10%需要交服务器费用,而且这是按天收费。很明白了吧。
本工程的写法部分参照于unity的UNet
大致的同步方案为:框架只同步主角信息(角度,位置),由主角而产生的消息(比如说发子弹,换枪)等操作,均由自己发消息和分发。
本框架代码并没有对消息进行压缩加密操作,如果需要通过protobuf加密等操作,请参考商店插件
客户端逻辑跟本篇差不多,只是加入了protobuf,服务器改为了golang,为了更加极致的性能。
https://store.cocos.com/app/detail/2881
框架外代码简单易懂,下面是简单解读
RoomControl
房间控制类,主要是功能就是登陆和进入,进行指定房间
onLoad(){
UNetMgr.model = UNetMgr.SyncModel.SelfNoDelay;
UNetMgr.event.on('join',(v)=>{
console.log('加入房间:',v);
this.node.active = false;
cc.find('Canvas/Game').active = true;
},null)
UNetMgr.event.on('loginSuccess',function(v){
console.log('登陆完成:',v);
UNetMgr.JoinRoom('1','0')
},null)
}
start(){
}
onClickEnter(e,d){
var loginData = {id:LOCAL_USER[d]}
UNetMgr.StartConnect({
isSSL : false, // wss 时参数为true,并且提供证书的cert路径
cert:'', //相对于resource的路径
host:'localhost',
port : '8001',
autoConnect:true //非人为断开时自动重连
},loginData)
//角色预制件托管 默认添加在Canvas
UNetMgr.room.rolePrefab = this.rolePrefab;
}
Game.ts
进入房间成功后就进入了游戏场景
游戏主代码也很简单,默认直接准备开始,然后只监听帧事件,然后做事件分发,除了帧事件外的一些事件监听都放在框架内的UNetMgr里
onLoad(){
Game.s_instance = this;
UNetMgr.SendRoomMsg('ready',{})
UNetMgr.event.on('frameUpdate',this.OnFrameUpdate,this)
UNetMgr.event.on('close',()=>{
console.log('重连失败,退出游戏')
this.node.destroy();
},this)
UNetMgr.event.on('reconnect',()=>{
console.log('断线重连中')
},this)
}
removeBullet(fs:FrameStep){
var idx = this.frameUnits.findIndex(v=>{return v._uuid == fs._uuid});
if(idx >= 0){
this.frameUnits.splice(idx,1);
fs.node.destroy();
}
}
addBullet(uuid:string,type:number,x:number,y:number){
var node = cc.instantiate(this.bulletPrefab);
this.node.getChildByName('Bullet').addChild(node);
var fs = node.getComponent(FrameStep)
fs.initFrame(uuid,UNetMgr.room.frame,{type:type,x:x,y:y})
this.frameUnits.push(fs)
}
OnFrameUpdate(frame:number){
for(var i=0; i<this.frameUnits.length; i++){
var fs = this.frameUnits[i];
fs.OnSyncFrame(UNetMgr.room.frame);
}
}
onDestroy(){
UNetMgr.event.off(this);
}
CharacterControllers
第一人称控制器,这个没啥好说,也不用帖代码
Player.ts
重点是这个角色类,继承UNetIdentity(框架角色类)
其主要就是继承了一些接口,自己翻一下代码就能看懂,
FrameStep.ts
除主角外其他需要进去帧同步的基类,比如子弹,地上的物品,场景上的元件,由于这个只是一个接口,怕不太会用,于是还写了一个实现了一个子弹类
/** 创建帧数 */
/** 创建帧数 */
createFrame : number = 0;
/** 创建时的数据 */
createData : any = null;
/** 上次更新的帧值 */
lastUpdateFrame : number = 0;
lockStepTween : cc.Tween = null
_uuid : string = '';
initFrame(id:string,frame:number,data:any){
this._uuid = id;
this.createFrame = frame;
this.createData = data;
this.lastUpdateFrame = frame
}
/** 帧同步主要函数 */
abstract OnSyncFrame(frame:number);
Bullet.ts
最关键的帧同步代码,根据服务器下发的当前的帧数与创建时的帧相对比,计数当前的位置,如果相关在2帧以类,可暂时不管,否则说明有网络延时。就需要修改其位置
var dframe = frame - this.lastUpdateFrame
if(dframe <= 0){
return
}
var time = (frame - this.createFrame) * UNetMgr.delay
if(time >= this.life){
Game.Instance.removeBullet(this)
return;
}
var stepx = UNetMgr.delay * this.speed.x
var stepy = UNetMgr.delay * this.speed.y
var x = this.createData.x + (frame - this.createFrame)*stepx
var y = this.createData.y + (frame - this.createFrame)*stepy
if((this.node.x - x) / stepx > 2 || (this.node.y - y) / stepy > 2){
this.node.x = x;
this.node.y = y;
}
话说了这么,还不如直接上代码 开源工程地址:
https://gitee.com/pabble_561/CreatorUNet_Nodejs.git