【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
  • 掌握这些生命周期范式,能让脚本结构更清晰、性能更稳,不再因为帧循环乱写导致卡顿。
相关推荐
小李也疯狂20 小时前
Unity 中的立方体贴图(Cubemaps)
unity·游戏引擎·贴图·cubemap
呆呆敲代码的小Y20 小时前
【Unity工具篇】| 超实用工具LuBan,快速上手使用
游戏·unity·游戏引擎·unity插件·luban·免费游戏·游戏配置表
EQ-雪梨蛋花汤20 小时前
【Unity优化】Unity多场景加载优化与资源释放完整指南:解决Additive加载卡顿、预热、卸载与内存释放问题
unity·游戏引擎
我的offer在哪里21 小时前
用 Unity 从 0 做一个「可以玩的」游戏,需要哪些步骤和流程
游戏·unity·游戏引擎
泡泡茶壶ᐇ21 小时前
Unity游戏开发入门指南:从零开始理解游戏引擎核心概念
unity·游戏引擎
Var_al1 天前
抖小Unity WebGL分包命令行工具实践指南
unity·游戏引擎·webgl
天人合一peng1 天前
unity 通过代码修改button及其名字字体的属性
unity·游戏引擎
GLDbalala1 天前
Unity基于自定义管线实现经典经验光照模型
unity·游戏引擎
心疼你的一切1 天前
Unity异步编程神器:Unitask库深度解析(功能+实战案例+API全指南)
深度学习·unity·c#·游戏引擎·unitask
呆呆敲代码的小Y1 天前
【Unity 实用工具篇】 | Book Page Curl 快速实现翻书效果
游戏·unity·游戏引擎·u3d·免费游戏·翻书插件