【Godot】【入门】节点生命周期怎么用(避免帧循环乱写导致卡顿的范式)

许多新手把逻辑全塞进 _process,结果卡顿、逻辑乱序。本文拆解 Godot 节点生命周期:加载、就绪、物理帧、普通帧、输入、退出,并给出"放哪写什么"的范式与性能建议。

核心概念速览

  • _ready():节点进入场景树后调用一次,适合初始化、找子节点、连接信号。
  • _process(delta):每帧(受 fps 影响)执行,适合与显示相关、非物理逻辑。
  • _physics_process(delta):固定频率(默认 60Hz)执行,适合运动、碰撞计算。
  • _input(event):原始输入事件回调,UI 可能拦截。
  • _unhandled_input(event):UI 未消费的输入才会来到此处,适合游戏全局输入。
  • _exit_tree() / _notification:节点从树中移除/销毁时触发,用于解绑、保存。

心法:运动/碰撞放 _physics_process,视觉/插值放 _process,初始化放 _ready,输入优先 _unhandled_input

生命周期顺序(常见调用链)

  1. 构造脚本(_init 可选)。
  2. 进入场景树 → _enter_tree_ready
  3. 每帧: _process_physics_process 按引擎节奏交替。
  4. 输入事件 _input → UI → _unhandled_input
  5. 离开场景树:_exit_treequeue_free 清理资源。

各阶段写什么

_ready:初始化、连接、查找

gdscript 复制代码
@onready var anim := $AnimationPlayer
func _ready():
    anim.play("idle")
    $Area2D.body_entered.connect(_on_body_entered)
  • 不要在 _init 里找节点,场景尚未装配。
  • 导出变量在此安全使用。

_process:视觉/插值/计时

gdscript 复制代码
func _process(delta):
    sprite.modulate = sprite.modulate.lerp(Color.WHITE, 0.1)
    ui_time += delta
    label.text = format_time(ui_time)
  • 不要在这里做碰撞运算,避免 fps 变化导致穿透。

_physics_process:运动与碰撞

gdscript 复制代码
func _physics_process(delta):
    velocity.y += gravity * delta
    move_and_slide()
  • 使用 move_and_slide/move_and_collide 等物理 API,保证稳定性。
  • 粒度:只放必须的物理计算,其他逻辑分层。

输入处理:_input vs _unhandled_input

  • UI 项目:优先 _unhandled_input,避免与按钮焦点冲突。
gdscript 复制代码
func _unhandled_input(event):
    if event.is_action_pressed("pause"):
        toggle_pause()
        get_viewport().set_input_as_handled()
  • 需要原始鼠标移动(如相机)的放 _input

退出与清理:_exit_tree

gdscript 复制代码
func _exit_tree():
    SignalsBus.enemy_spawned.disconnect(_on_enemy_spawned)
  • 解绑全局信号/计时器,释放引用避免内存泄漏。

常见坑与规避

  • 把物理放在 _process :fps 波动导致速度不稳甚至穿透;改用 _physics_process
  • 在 _ready 前访问节点null instance 报错;改用 @onready 或放到 _ready
  • 重复 connect :在 _process 里连接信号会导致多次绑定;连接操作放 _ready,必要时先 disconnect
  • 忘记 queue_free :动态实例不释放导致泄漏;场景切换时用 queue_freecall_deferred("free")
  • 输入被 UI 吃掉 :把核心输入监听放 _unhandled_input,或在 _input 调用 accept_event()

性能小贴士

  • 减少每帧分配:在 _process 内避免频繁创建对象(如 new Vector2),可复用向量或在类成员预存。
  • 频繁逻辑分级:高频逻辑 _physics_process,中频逻辑用 Timer,低频逻辑用 yield/await 或状态机驱动。
  • 关闭未用的处理:set_process(false)set_physics_process(false) 关闭回调,UI 场景静态时尤为有效。

模板:角色控制器的分层

gdscript 复制代码
func _ready():
    _init_inputs()
    _init_signals()

func _physics_process(delta):
    _update_gravity(delta)
    _update_move(delta)
    _apply_motion()

func _process(delta):
    _update_animations(delta)
    _update_ui(delta)
  • 物理与表现分开,方便调试;动画跟随状态,不直接写在物理段。

调试生命周期顺序

  • 打印顺序:在各回调 print,运行一次即可确认调用次序。
  • 调试器 → Monitors → Frame Time 观察 _process_physics_process 是否被启用。
  • Remote SceneTree 查看节点是否已经进入场景树。

总结复盘

  • 分三类:初始化 _ready,物理 _physics_process,表现 _process,输入 _unhandled_input,清理 _exit_tree
  • 避免把所有逻辑塞进 _process;关闭不需要的处理;信号连接放 _ready,解绑放 _exit_tree
  • 掌握这些生命周期范式,能让脚本结构更清晰、性能更稳,不再因为帧循环乱写导致卡顿。
相关推荐
sindyra6 分钟前
Unity资源内存管理与释放
unity·游戏引擎·资源管理·资源释放·内存释放
CreasyChan15 分钟前
Unity FairyGUI高斯模糊实现方法
unity·游戏引擎·fgui
avi911123 分钟前
Unity半官方的AssetBundleBrowser插件说明+修复+Reporter插件
unity·游戏引擎·打包·assetbundle·游戏资源
一个笔记本11 小时前
godot log | 修改main scene
游戏引擎·godot
nnsix13 小时前
Unity PicoVR开发 实时预览Unity场景 在Pico设备中(串流)
unity·游戏引擎
一只一只18 小时前
Unity之UGUI Button按钮组件详细使用教程
unity·游戏引擎·ugui·button·ugui button
神米米21 小时前
Maya快速安装UE4 布料权重绘制插件PhysX导出apx
游戏引擎·ue4·maya
WarPigs21 小时前
Unity阴影
unity·游戏引擎
一只一只1 天前
Unity之Invoke
unity·游戏引擎·invoke
技术小甜甜1 天前
【Godot】【入门】信号系统从 0 到 1(UI/玩法彻底解耦的通用写法)
ui·游戏引擎·godot