联机帧同步射击类游戏终于上线了。再次分享并请教各位。

我的帧同步射击类游戏2D版CS终于上线了,感谢论坛各位先前的指导gh_7c9e93894b2b_258
我的帧同步是每100ms一个逻辑帧
客户端每6帧上传一次玩家地理位置(100ms约等于6帧)
然后服务端在收集玩家位置信息后,每100ms广播给所有玩家客户端
客户端收到服务端帧同步包以后,将玩家走路的过程分为6帧渲染
比如人物在该逻辑帧行走了6像素,那每一个渲染帧(update)走1个像素
update代码如下:

         //判断人物位置是否变化
        if ((this.m_targetPos.x != this.m_NowPos.x || this.m_targetPos.y != this.m_NowPos.y)) {
            this.m_NowPos = this.m_targetPos;

            this.cha_x = this.m_targetPos.x - this.node.x;
            this.cha_y = this.m_targetPos.y - this.node.y;
            this.runStep = 6;
        }

        //变化了则分6帧移动人物
        if(this.runStep>0){
            let new_x = this.node.x + this.cha_x/6;
            let new_y = this.node.y + this.cha_y/6;

            if ((this.cha_x > 0 && new_x > this.m_targetPos.x)
                || (this.cha_x < 0 && new_x < this.m_targetPos.x)) {
                new_x = this.m_targetPos.x;
            }

            if ((this.cha_y > 0 && new_y > this.m_targetPos.y)
                || (this.cha_y < 0 && new_y < this.m_targetPos.y)) {
                new_y = this.m_targetPos.y;
            }
            this.node.setPosition(new_x, new_y);
            if (this.id == GameDataCenter.gameinfo.id && !GameDataCenter.moveJu) {
                //摄像机移动
                cc.Camera.main.node.position = new cc.Vec3(new_x, new_y, 0);
                //操控杠移动
                this.control.position = new cc.Vec3(new_x, new_y, 0);
            }
            this.runStep--;
        }

现在遇到的问题是,依然有部分手机,人物在行走时卡顿
就是走一秒钟,摄像头停顿下,然后继续往前,一秒后又停顿下,然后往前(一秒只是举例,时间也不是固定的)
不是所有的手机如此,安卓旗舰机甚至大部分弱机型基本不会,iPhone机型比较常见,比如7p的ios版本越高越卡。

视频不知道怎么传,麻烦各位大大了。

另外,由于帧同步每100ms一次,上下包消息很多,粗略算了下每秒钟每个游戏房间通讯总包体为0.024M,也就是说我10m带宽的服务器理论上最大只能支持40个房间、400人同时游戏,这支撑也太小了!有大大知道怎么优化吗?

你少算了除八吧。 :joy:

我是这样算的,10M带宽=1M/S除以0.024=41个房间,好像没错吧?-_-

请问下楼主用的什么联机对战引擎?如果是腾讯的mgobe,那么国外的怎么解决?

10Mb/s/8=1.25MB/s
0.024MB/s/room
1.25/0.024=52,差不多50个房间。
请问下楼主用的什么联机对战引擎?如果是腾讯的mgobe,那么国外的怎么解决?

试了一下,感觉还不错。不过你同步的过程是无法确保每100ms就可以收到逻辑帧的,有可能快,有可能慢,你渲染6帧的事件也不会是确定的100ms,所以需要你做一个预测或者缓存,这样此才不会出现卡顿。
顺便问一下,您游戏的不同设备的物理同步是这么做的,我做的网络帧同步,就是卡在物理同步上了,一旦发生碰撞,不同设备产生的效果会不一致。

是我错了。

自己写的服务端,java+netty+websocket。
之前考虑过第三方引擎,但是感觉自定义没那么方便
而且,好像费用可能并不便宜,我猜的。

我也在写帧同步射击类,目前移动很平滑。我奇怪的是,你的人物移动直接设置坐标为逻辑帧的坐标?不进行lerp肯定不会平滑。另外就是跟其它网友说的一样,移动可以进行预测几帧。

我的人物碰撞是在自己机器上计算好了,然后将最终的人物位置发送给服务器,广播给其他人。至于每台机器都要同步的那些,比如子弹和人物碰撞是在逻辑帧消息内计算的,用的定点数保证每台机器同步,有个定点数计算类和三角函数库,你需要留下邮箱我可以发你

直接设置为逻辑帧的位置?不是啊,是分了6帧去平滑移动,6帧中的最终帧才是逻辑帧位置

另外,碰撞需要自己写,不能用引擎自带的,因为引擎自带的不是定点数计算。
这个碰撞同步模块我还是花了不少时间研究,还是有不少心得,可以多交流交流。

每个客户端不是稳稳60帧渲染帧,有些人是40帧,有些人是30帧,你按固定6帧渲染帧来过度当然卡了。

1、这个不算是严格的帧同步,帧同步只同步玩家操作,转发数据量不会那么大
2、卡顿问题是因为玩家位置取决于服务器的下发玩家位置状态,这个受限于网速延迟

浏览器最小化,这时候update是停止的,会有问题吧?你的游戏我也玩了,最小化再回来,貌似游戏是暂停的。我是这么写的,渲染帧的帧数和逻辑帧无关,逻辑帧收到消息之后直接设置逻辑位置,然后根据移动速度和方向弧度设置一个逻辑帧移动所需耗费时间,然后渲染帧里面根据update的deltaTime,换算出渲染一次所需时间,然后进行lerp。我现在的表现是,浏览器最小化,逻辑帧继续跑,回复浏览器,渲染帧会直接设置人物位置到最新位置。以下是代码,不知道有没有帮到你:

逻辑帧update:

public update(delta: number) {
if (this.moveRadian != -9999) {
let oldPosition = this.position.clone();
let position = oldPosition.clone().add(this.getV3(this.moveRadian, delta));//目标位置
if (!this.battle.navmesh.isPointInMesh(position)) {//如果目标点是障碍
const dx = position.x - oldPosition.x;
const dy = position.y - oldPosition.y;
let findResult = this.battle.navmesh.findClosestMeshPoint(new Vector2(position.x, position.y), Math.sqrt(dx * dx + dy * dy));
if (!findResult.point) {
return;
}
let radian = Math.atan2(findResult.point.y - oldPosition.y, findResult.point.x - oldPosition.x);
position = oldPosition.clone().add(this.getV3(radian, delta));//重设目标位置
}
this.position = position;
this.direction = this.position.clone().subtract(oldPosition);
this.totalTime = this.direction.length() / this.moveSpeed;
this.walkTime = 0;
}
}

渲染帧update:

private update(delta: number) {
if (this.hero.walkTime >= this.hero.totalTime) {
return;
}
this.hero.walkTime += delta;
if (this.hero.walkTime >= this.hero.totalTime) {
this.hero.walkTime = this.hero.totalTime;
}
let ratio = Math.floor(this.hero.walkTime / this.hero.totalTime * 1000) / 1000;
this.node.position = this.node.position.lerp(this.hero.position, ratio);
}

谢谢:pray: 我的邮箱392883202@qq.com

十分感谢啦,我因为这个物理困扰了好久。有几个疑问向您请教:
1没用引擎自带的物理,碰撞是怎么写的,贴墙滑动是怎么实现的呢?
2玩家操作人物移动会先表现出来吗,我有点不太明白同步玩家的位置 是玩家已经发生位移后再上传到服务器的吗?
3同步玩家子弹是怎么做的呢?

你的update算法里面没有用上自带参数dt对吧,这样的话在不同的手机,每次执行update的时间都是不一致的,是不是就是因为这点导致有的手机会卡一下

严格一点说我的是状态同步,即同步玩家位置的xy坐标信息
1、就是在update中,你先计算人物本帧移动后的位置坐标xy,如果xy不在障碍物内,将人物坐标发送给服务端,如果在障碍物内,此时,可以2种操作方法,一种是人物停止不动,一种是让人物沿着障碍物贴墙滑动,一般来说2d游戏我们障碍物比较简单,都是长方形,贴墙滑动要不就是x=x+v,要不就y=y+v,v是人物移动速度,具体是x还是y要自己判断,另外,如果复杂点,还要继续判断x+v或y+v后是否还在障碍物内。
2、如第一点,玩家移动流程是这样的:玩家位置信息xy计算后将结果发给服务器(玩家的node此时并不移动),玩家真正的位置移动需要在收到帧消息并处理好帧逻辑后,在玩家node的update里渲染的。
3、子弹同步:玩家在客户端点击射击键后,此时客户端将这个开枪信号flag=1传送给服务端,服务端广播给所有客户端,客户端收到后判断玩家开枪信号flag是否等于1,决定玩家是否射出子弹,然后子弹飞行时间是1个逻辑帧还是多少个逻辑帧需要你自己处理,并且还要计算该逻辑帧下是否和玩家、障碍物碰撞了。flag只是举例,现实可能flag很有可能包含比如子弹射击的方向、速度等,具体看你情况。

我之所以用位置同步,是因为如果只同步玩家输入,那么每个玩家和障碍物的碰撞都要在每个客户端计算,我的10人游戏就要计算10遍,机器效率跟不上,所以改成了每个客户端只计算自己和障碍物的计算,这样效率提高了很多,不会这么卡,如果你也是多人游戏,建议你用这个方式。
另外,定点数在这个地址下载,我的跟他几乎完全一样的,我邮箱也不发你了。