当前位置: 首页 > article >正文

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呢?可以自行推演一下,是否能够解决不同步现象


http://www.kler.cn/news/343484.html

相关文章:

  • 证明算法(参数估计)满足大样本性质
  • Spring Boot集成encache快速入门Demo
  • 提示词格式化
  • 卡码网C++基础课 |20. 排队取奶茶
  • xmltodict 处理 XML 数据案例解析
  • 无人机在矿业领域的应用!
  • 探秘纯前端Excel表格:构建现金流量表的完整指南
  • 【大数据】数据采集工具sqoop介绍
  • 春意融融:Spring Boot技术在“衣依”服装销售平台的应用
  • swagger2.9.2 和 springboot3.3.4版本冲突问腿
  • 线控底盘技术介绍
  • Selenium WebDriver和Chrome对照表
  • 用AI构建小程序需要多久?效果如何?
  • Redis:set类型
  • 命令 首选项:打开用户设置(json) 导致错误 文件似乎是二进制文件,不能作为文本打开
  • Qt Qml Map-地图绘制点与圆的切线
  • 火山引擎边缘智能×扣子,拓展AI Agent物理边界
  • javascript 自定义多选框实现 ag-grid中没有原生多选框
  • 物理学基础精解【67】
  • 通知系统的设计方案