godot-gdscriptlisted
Install: claude install-skill Wade-DevCode/awesome-coding-skills-cn
# Godot
## 何时用
- 新建或修改任何 GDScript 脚本或 C# 组件脚本时。
- 节点间通信方式有疑问(是用 get_node、直接引用还是 signal)时。
- `_process` 和 `_physics_process` 里的逻辑越写越多、帧率下降时。
- 场景实例化、资源加载/释放方式不确定时。
- 遇到内存泄漏、孤立节点、信号未断开等问题时。
## 核心规则
### 1. 节点与场景:@onready 缓存,场景实例化代替继承
**规则:** 节点引用用 `@onready` 在 `_ready()` 阶段一次性缓存;不在 `_process` 里反复 `get_node()`;复用结构用场景实例化,而不是多层继承。
**为什么——真实会犯的错:**
在 `_process(delta)` 里每帧写 `$AnimationPlayer.play("run")`,GDScript 每次都要解析节点路径字符串、在节点树里做查找,场景节点数量一多(几百个)帧率肉眼可见地跌。另一个常见错误:用继承堆叠 Enemy → FlyingEnemy → BossEnemy → FinalBoss,四层下来 `_ready` 调用顺序、信号连接变得极难追踪,改一层上面全乱。
**怎么做:**
- 节点引用用 `@onready var anim: AnimationPlayer = $AnimationPlayer`,只在 `_ready()` 时解析一次路径,后续直接用变量。
- 逻辑上独立、可复用的结构拆成独立场景(`.tscn`),用 `instantiate()` 生成,而不是靠继承叠加。
- `get_node()` 只在 `_ready()`、事件回调里调用,**不放进 `_process` 或 `_physics_process`**。
---
### 2. signal 解耦:通信走信号,连接必须断开
**规则:** 节点间跨层通信用 `signal`;子节点不直接持有父节点引用;场景卸载或节点销毁前断开信号连接,防止内存泄漏和空引用回调。
**为什么——真实会犯的错:**
Enemy 脚本里写了 `get_parent().get_node("HUD").show_damage(damage)`,这条路径硬绑了节点树结构,一旦 HUD 改了层级或名称,运行时立刻报 `null`,且错误信息只提示"尝试调用 null 上的方法",找来找去才发现是节点路径变了。另一个事故:动态生成的子弹连接了 GameManager 的信号,子弹 `queue_free()` 后没有断开,GameManager 还保持对已销毁节点的引用,触发回调时崩溃或静默错误。
**怎么做:**
- 子节点向上通信 → 发射 `signal`,父节点监听,子节点不引用父节点。
- 跨系统通信 → 用 Autoload(单例)中转,或通过 signal bus。
- 动态节点连接信号时,在 `_exit_tree()` 或 `queue_free()` 前调用 `signal.disconnect(callback)` 断开。
- 用 `connect(..., CONNECT_ONE_SHOT)` 处理只触发一次的事件,自动断开更安全。
---
### 3. _process vs _physics_process:物理归物理,能事件驱动就不轮询
**规则:** 涉及物理体、碰撞、速度的逻辑一律放 `_physics_p