【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
  • 掌握这些生命周期范式,能让脚本结构更清晰、性能更稳,不再因为帧循环乱写导致卡顿。
相关推荐
Howrun77718 小时前
虚幻引擎_核心框架
游戏引擎·虚幻
GLDbalala1 天前
Unity 实现一个简单的构建机
unity·游戏引擎
JIes__2 天前
Unity(二)——Resources资源动态加载
unity·游戏引擎
地狱为王2 天前
Unity使用NovaSR将沉闷的16kHz音频升频成清晰的48kHz音频
unity·游戏引擎·音视频·novasr
孟无岐2 天前
【Laya】Socket 使用指南
websocket·typescript·游戏引擎·游戏程序·laya
tealcwu3 天前
【Unity资源】Unity MCP 介绍
unity·游戏引擎
晚霞的不甘3 天前
Flutter for OpenHarmony 引力弹球游戏开发全解析:从零构建一个交互式物理小游戏
前端·flutter·云原生·前端框架·游戏引擎·harmonyos·骨骼绑定
Thomas_YXQ3 天前
Unity3D中提升AssetBundle加载速度的详细指南
java·spring boot·spring·unity·性能优化·游戏引擎·游戏开发
浪客川3 天前
1972 GODOT 入门案例
android·java·godot
晚霞的不甘3 天前
Flutter 方块迷阵游戏开发全解析:构建可扩展的关卡式益智游戏
前端·flutter·游戏·游戏引擎·游戏程序·harmonyos