我正在开发一个联机射击类对战游戏,带子弹飞行弹道,做的帧同步,但目前有些问题不懂。
我的逻辑帧是每秒同步20次
比如我控制的人物逻辑帧中速度为3像素/帧,
然后我在一个逻辑帧中算出该帧结束后人物所在位置(0,3)
那么我在渲染帧update中如何更新人物位置呢?
比如
update(dt){
this.node.x = speed*dt;
}
那算出来不是有小数吗?如何跟(0,3)同步呢?
另外,我一个逻辑层是需要拆分成多少个渲染帧呢?这个个数又如何确定才能和其他客户端同步呢。
敬请懂得指导一下,感谢。
取整就行了,向上或者向下
那一个逻辑帧我分多少个渲染帧去渲染完毕这一帧呢?人物走到了(0,3)位置就停止渲染帧等待下一个逻辑帧到达吗

当年做的一个帧同步的移动处理,getRealPosition拿到的是逻辑帧的位置,这个显示组件的update中,不断检测逻辑帧的新位置,然后插值平滑移动。
我们的逻辑帧大概是10帧每秒,按照60的FPS,一逻辑帧会被拆成5-6次渲染帧平滑地表现
感谢,不懂得再请教 -_-
你这个做法画面会不平滑,会一顿一顿的。每执行一帧都要判断有没有超出真实位置距离,超出即等于,所以会在这里卡一下,等下一帧逻辑帧过来了,渲染帧再起步追上,所以画面会一卡一卡的
https://v.youku.com/v_show/id_XMjY4NTI0MTIyNA==.html?spm=a2h0c.8166622.PhoneSokuUgc_1.dtitle
给你看看这个一卡一卡的效果
我目前测试了下他的方法,效果还行。请问下你有什么更好的方法吗?请指教下
先用宝爷的代码实现出来,再想更好的效果吧。
假设服务器现在发过来的帧id为1,第一帧。
帧数据是这样的
玩家1按了右键,玩家2按了左键
{
id: 1,
player1: {
up: false,
down: false,
left: false,
right: true,
},
player2: {
up: false,
down: false,
left: true,
right: false,
},
}
根据楼主的一秒同步20次,那么每一帧将占用50ms。
这个时候,不用管什么游戏帧数,按照每次同步的帧数来当基础,不然就同步就毫无意义了。
node1.runAciton(cc.moveTo(0.05, cc.v2(node1.x+3,node1.y))); //以同步的帧时长为标准
node2.runAciton(cc.moveTo(0.05, cc.v2(node1.x-3,node1.y))); //以同步的帧时长为标准
这就是第一帧的人物坐标同步
那么有小伙子就问了,马老师,那么之后的网络有波动呢,或者我大意了,没有闪跑着跑着被枪打了一枪,减速了怎么办。
假设子弹在这一帧数据**(50ms)**的20ms击中,减速了50%,那么就是这样
node1.runAciton(cc.sequence(
cc.moveTo(0.02, cc.v2(node1.x + 3 * 0.5, node.y)),
cc.callFunc(() => {
this.byHit(); //击中动画调用
}),
cc.moveTo(0.03, cc.v2(node1.x + 3, node.y))
));
那么网络波动延时了呢,根据一下子发过来的帧数据,直接算出当前帧的上一帧的数据,将人物顺移到当前位置,再执行这一帧的数据。或者将数据提取出来,把移动的坐标点进行插值优化,尽量让人少瞬移。
别问为啥是20ms过后击中,因为我们得提前算出子弹击中我们的可能。这就是帧同步重要的一步,预测。
其实小游戏做到预测就够了,这就是为啥都用runaction来跑。不用纠结快照和回滚,我们咸鱼要有咸鱼的觉悟。
http://www.manew.com/thread-140259-1-1.html
多看看这个链接,应该也能有更多领悟吧
有点疑惑,你的帧同步逻辑是用node1.runAciton(cc.moveTo(0.05, cc.v2(node1.x+3,node1.y)));这个做移动的吗?这个能保证每个客户端都同步?还有碰撞检测你怎么判断?
感谢,先马再看
大神,经常论坛看到你,指导一下有没有更优化的办法,
很明显,左右按钮按下,是一个事件,应当发送的是事件数据,而不是状态数据;然后一个 action 却只执行一帧的 0.02 秒,逻辑上就是错误。
时间是检验真理的标准。
影子跟随算法
你影子跟随算法就是4楼宝爷那个算法吧,我开始误会了,以为他是按一定的增量去让渲染位置跟随真实位置,会导致卡顿,现在仔细看了下,是插值算法,会平滑到真实位置,不会卡。不过插值算法在游戏快速运转中,渲染帧和逻辑帧会有一定的误差,比如一颗子弹逻辑帧已经击中敌人了,但是渲染帧还离敌人有100像素,视觉上会发生子弹提前击中敌人
这只是一个简单的模型例子,帧同步眼里,没有什么事件数据和状态数据,只有玩家的输入。如果非要硬杠,一个帧数据都要包括一个按键的 按下 松开 这2个状态的数据,不然fps游戏就无法实现点射了。
至于为啥没有鼠标点击,那是因为别人的子弹可能飞了0.1秒了,不是当前帧数据发送过来的,这个本意强调,我可以根据以前+现在的帧数据,我就能预测后面50ms的每一ms的游戏进度
我想知道你用runAction做逻辑移动,怎么能保证客户端同步,子弹和玩家的碰撞逻辑怎么运算,不会是用引擎自带的碰撞系统吧。
抛开能不能同步不说,1秒20帧的情况,网络传输可不是平稳的,有时前500ms收到5帧,后500ms收到剩下的15帧,你用固定的0.05秒做每帧逻辑帧的逻辑过度画面能平滑吗。每帧逻辑帧每个对象都要创建一个runAction,如果有100个对象,每一秒内就要创建 20 * 100 个runAction,这运算消耗也太疯狂了吧。你要做提前判断预测,一个对象已经被判断死亡了,后面要回滚,角色又原地复活了,这体验…?
同步是因为我在确定收到了数据的情况下,玩家的操作输入已经被锁定了,你可以理解为 50ms的回合游戏,当操作完,这50ms的结果就固定了,至于碰撞逻辑,这是你自己的事情,这是你自己逻辑帧该跑的事情,属于你的游戏逻辑,你该问你自己。
至于网络延时,你可以这么理解,老师早上8点收作业,你虽然写了,但是早上堵车了,对于老师来说,你就是没交作业,其他学生也是一样,在他们眼里,你写没写不重要,重要的是交没交。
老师 = 服务器 其他学生 = 其他玩家 写 = 玩家操作 交 = 将自己的帧数据提交到服务器
这么理解下来就懂了吧。假如把剧烈的网络波动形容成因伤退学,然后我想重新追回来。
剩下的功课 = 一起发过来的帧数据 ,只要功课在,你肯定追上他们。
还有runaction的问题,我们看问题应该看峰值,你100个对象,也只会有100个act,因为执行完了他就没了,所以最高只会存在100个act。按照你的说法换成update,那我还能说我1秒钟就要进行100*60的update调用呢。
提前预判,还是最上面说的,我只能根据服务器发给我的最新的帧数据,预测下面50ms,因为这一切都已经固定了,无法被改变。
还是直接跟解释你的游戏做不到同步的原因吧。
还是这条
node.runAciton(cc.moveTo(0.05, cc.v2(node.x+3,node.y)));
假设 c1 玩家位置在(0,0)位置
c1 客户端 显示c1玩家的位置
第0.05秒
收到第一帧时 c1 的位置 cc.v2(node.x+3,node.y) 变成了 (3,0);
第0.1秒 runAction刚好跑完 node.x 变成了3
收到第二帧时 c1 的位置 cc.v2(node.x+3,node.y) 变成了 (6,0);
c2客户端显示c1玩家的位置
第0.05秒
收到第一帧时 c1 的位置 cc.v2(node.x+3,node.y) 变成了 (3,0);
第0.07秒 (因为网络波动提前收到),runAction只跑了0.02秒,node.x的位置3 * 0.02 / 0.05 = 1.2
收到第二帧时
c1 的位置 cc.v2(node.x+3,node.y) 变成了 (4.2,0);
这时相同的一帧逻辑帧,不同客户端同一个玩家的位置已经不一样了,最后发生一个客户端显示这个c1玩家被子弹击中了,另一个客户端子弹miss