with one click
godot-gdscript
写 Godot(GDScript/C#)时使用。节点树、signal、场景、性能规范。
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
写 Godot(GDScript/C#)时使用。节点树、signal、场景、性能规范。
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
做 Cocos Creator 多机型/多分辨率适配时使用。Canvas、Widget、安全区。
Cocos Creator 用 AssetBundle 做分包/远程资源时使用。加载、释放、依赖、缓存。
优化 Cocos Creator 渲染性能时使用。合批、图集、动静分离、Label。
给 Cocos Creator 原生包做热更新时使用。version manifest、增量、校验、回滚。
写 Cocos Creator 动效/动画时使用。tween、Animation、Spine、性能与清理。
做 Cocos Creator 大量条目列表时使用。虚拟列表、节点复用。
| name | godot-gdscript |
| description | 写 Godot(GDScript/C#)时使用。节点树、signal、场景、性能规范。 |
| category | gamedev |
| tags | ["godot","gdscript","节点"] |
_process 和 _physics_process 里的逻辑越写越多、帧率下降时。规则: 节点引用用 @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。规则: 节点间跨层通信用 signal;子节点不直接持有父节点引用;场景卸载或节点销毁前断开信号连接,防止内存泄漏和空引用回调。
为什么——真实会犯的错:
Enemy 脚本里写了 get_parent().get_node("HUD").show_damage(damage),这条路径硬绑了节点树结构,一旦 HUD 改了层级或名称,运行时立刻报 null,且错误信息只提示"尝试调用 null 上的方法",找来找去才发现是节点路径变了。另一个事故:动态生成的子弹连接了 GameManager 的信号,子弹 queue_free() 后没有断开,GameManager 还保持对已销毁节点的引用,触发回调时崩溃或静默错误。
怎么做:
signal,父节点监听,子节点不引用父节点。_exit_tree() 或 queue_free() 前调用 signal.disconnect(callback) 断开。connect(..., CONNECT_ONE_SHOT) 处理只触发一次的事件,自动断开更安全。规则: 涉及物理体、碰撞、速度的逻辑一律放 _physics_process(delta);纯表现层更新可放 _process(delta);能用信号/事件触发的逻辑不轮询。
为什么——真实会犯的错:
在 _process 里移动 CharacterBody2D,与物理引擎以不同频率运行,出现抖动和碰撞穿透,在低帧机器上更明显。另一个方向:把大量 UI 状态检测("玩家血量是否低于 20%")放在 _physics_process 里每帧判断,不必要地占用物理线程。还有把"按下攻击键"的检测放在 _process 里,输入延迟和物理帧不对齐,打击感差。
怎么做:
move_and_slide()、碰撞查询、刚体速度赋值 → 全进 _physics_process。_process。_unhandled_input() 或 _input() 回调,不在 _process 里 poll Input.is_action_pressed()(除非需要持续检测,如长按移动)。_process 里每帧比对旧值。规则: 所有变量、函数参数、返回值都加类型注解;避免用 Variant 做中间传递;禁止用字符串做类型名传给 get_node()、is_class() 等。
为什么——真实会犯的错:
var speed = 5 写成动态类型,Godot 运行时每次访问都要推断类型,100 个敌人 × 每帧访问数十次,性能损耗可测量。更常见的问题是可读性:func deal_damage(target, amount) 两个参数都是 Variant,调用方传错类型(把 Node 传给了期望 float 的 amount),运行时才报错,且错误提示不友好。用了字符串类 get_node("Player").has_method("take_hit") 这种动态查找,重构时改了方法名但字符串没改,静默失效。
怎么做:
var speed: float = 5.0,var health: int = 100。func deal_damage(target: CharacterBody2D, amount: float) -> void:。@onready var player: CharacterBody2D = $Player,有类型后能享受 IDE 补全和静态检查。Variant 兜底。规则: 动态生成的节点用 queue_free() 释放,不用 free();preload 预加载静态资源,load 按需加载动态资源;Resource 之间不能循环引用。
为什么——真实会犯的错:
在回调函数中直接调 node.free(),如果此帧还有其他代码持有该节点的引用并继续调用其方法,立刻崩溃报 freed object。用 queue_free() 则会等本帧所有逻辑跑完再安全删除。另一个常见问题:两个 Resource(如 WeaponData 和 AbilityData)互相持有对方引用,GDScript 的引用计数无法回收,内存随关卡切换不断增长。
怎么做:
queue_free(),不用 free()(除非你清楚知道当前帧没有其他引用)。preload("res://...") 编译期绑定。ResourceLoader.load_threaded_request() 异步加载,避免卡主线程。# ❌ 反例 — 每帧解析路径;硬引用父节点;无类型注解
extends Node
func _process(delta):
# 每帧都在节点树里查找路径字符串,性能差
$AnimationPlayer.play("idle")
# 子节点直接拿父节点的子节点,耦合严重
get_parent().get_node("HUD").update_hp(get_parent().get_node("Player").hp)
func take_damage(amount): # 参数无类型,调用方传错不报错
get_parent().get_node("Player").hp -= amount
# ✅ 正例 — onready 缓存;signal 向上通信;全类型注解
extends CharacterBody2D
signal health_changed(new_hp: int) # 信号携带类型
@onready var anim: AnimationPlayer = $AnimationPlayer # 一次缓存
var hp: int = 100
func _ready() -> void:
anim.play("idle") # 直接用缓存变量,无路径查找
func take_damage(amount: float) -> void:
hp -= int(amount)
health_changed.emit(hp) # 发信号,不直接引用 HUD
func _on_died() -> void:
queue_free() # 安全释放,不用 free()
@onready 缓存,_process / _physics_process 里没有 get_node() 调用。_physics_process,纯表现逻辑在 _process,状态变化用 signal 通知而非每帧轮询。Variant。queue_free(),信号连接在节点退出树前断开。