自学内容网 自学内容网

godot帧同步-关于“显示与逻辑分离”

很多教程说帧同步的关键是“显示与逻辑分离”,但是又没有具体讲解,我起初也没有搞懂这句话的意思,就直接上手开发帧同步了。在开发的过程中,一下子就悟了,所以分享一下。

显示与逻辑未分离(单机)

func _physics_process(delta):

    # 一些处理(如伤害判断)

func _on_animated_sprite_2d_animation_finished() -> void:
if $AnimatedSprite2D.animation=="斩击":
set_process(false)
$AnimatedSprite2D.play("后摇")
if $AnimatedSprite2D.animation=="后摇":
queue_free()

看上面这个代码,在“斩击”动画过程中,进行伤害判断。“斩击”动画结束后,播放“后摇”动画,并停止process处理。在“后摇”动画结束后,删除节点。

这就是“显示与逻辑未分离”,每一步操作都依赖于动画的播放进程。

显示与逻辑分离(多人游戏帧同步)

1.帧同步的前置要求

简单提一下,看不懂就算了。

在帧同步中,每一个涉及到物理操作(如移动)的节点(怪物节点,子弹节点),都不应该执行自己的_physics_process()函数,而是要和核心场景的clientFrame同步的。若网络慢,核心场景clientFrame没有累加,那么该节点也不应该执行物理操作。

下面是例子,所有的process中的操作,都应该由Muti_game节点控制

func update_bullet():
var bullet_arr = get_child(2).get_children()
for _item in bullet_arr:
_item.do() # 手动执行一次物理
pass

func update_player():
    ···

func update_monster():
var monster_arr = get_child(1).get_node("Land/Monster").get_children()
for _item in monster_arr:
_item.get_node("FSM").do_() # 手动执行一次物理

func updateClient():

if logicFrame==lastSyncFrame:
···
if lastSyncFrame>logicFrame+1:
···
if lastSyncFrame==logicFrame+1:
        clientFrame+=1 # 客户端帧

update_monster() # 更新怪物
update_bullet() # 更新子弹
update_player() # 更新玩家

if clientFrame==(logicFrame+1)*GameControl.ClientServerFrameRate:
logicFrame+=1

func _physics_process(delta):
updateClient()
sendInput()

2. 分离

多人模式下,我们禁用了节点的_physics_process(),并将其中的内容提取为do函数,供核心场景调用。

我们还把基于动画的操作步骤,替换为了基于参数atk_num和houyao_num。

var atk_num = 60 # 斩击持续的帧数
var houyao_num = 30 # 后摇持续的帧数
func do():
    if houyao_num<=0:
        # 切换到待机状态
        pass
if atk_num<=0: # 斩击结束
        $AnimatedSprite2D.play("后摇")
        houyao_num-=1
return
    if atk_num>0:
        # 一些处理(如伤害判断)
        pass
atk_num-=1 # 计数更新

func _physics_process(delta):
    if 处于多人模式:
        return
    do()
 

# func _on_animated_sprite_2d_animation_finished() -> void:
# if $AnimatedSprite2D.animation=="斩击":
# set_process(false)
# $AnimatedSprite2D.play("后摇")
# if $AnimatedSprite2D.animation=="后摇":
# queue_free()

3. 分析 

为什么要分离。下面举反例。

假如有两个客户端A与B。

客户端A在 0s 时刻收到了来自第n帧的“斩击”指令,创建了斩击节点,并开始播放斩击动画,持续1s。

客户端B在 0.5s 时刻收到了来自第n帧的“斩击”指令,创建了斩击节点,并开始播放斩击动画,持续1s。

然后客户端A和B都在 1.2s 时刻收到了来自第n+1帧的“do”指令

因为客户端A的斩击动画在1s 时刻播放完毕,并开始播放了后摇动画,所以do操作没有进行伤害判断。

而客户端B的斩击动画将在 1.5s 时刻结束,此时仍在播放,故do操作进行了伤害判断。

以上过程,造成了不同步现象

如果我将斩击的持续时间,量化为atk_num呢?可以自行推演一下,是否能够解决不同步现象


原文地址:https://blog.csdn.net/atregret/article/details/142858255

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!