agentleFS
MindsMasterMindsMaster/godot-claude-code-skills/skills/godot-gdscript/SKILL.md

godot-gdscript

Skill3 starsChanged 19 days agoMITView on GitHub

写或改 GDScript 时用。静态类型 := 与 Array[T] Dictionary[K, V]、class_name、@export @onready、信号 connect emit lambda、await 协程、match、setter getter、call_deferred、警告设置。GDScript typing, signals, coroutine, warnings.

4 other projects use a version of this.

Adopted by

Most recently taken up by

The file

Files
SKILL.md

GDScript 写法

GDScript 语言层的写法与陷阱:类型、注解、信号、协程、对象有效性、警告。断言与日志见 references/syntax.md。目标 4.7,最低 4.4,更新的特性标版本号。

范围

不含去哪
节点选型、场景组织、节点通信godot-scene-architecture
数据资源godot-resources-data
C# 写法godot-csharp
卡顿与每帧分配godot-performance
无头加载脚本、抓警告godot-headless-testing

先查内置

需求用这个别自己写
延时一次await get_tree().create_timer(t).timeout 或 Timer 节点_process 里累加 delta
周期执行Timer 节点的 timeout 信号_process 里 if elapsed > 0.5
输入轴Input.get_vector()、Input.get_axis()手写正负键相减
数值靠近目标move_toward()、lerp()、Tween手写 SmoothDamp
取子节点%Name、@export var x: Node、@onready长硬路径 get_node("../../A/B")
枚举名字互转State.keys()、State.find_key(v)、State["RUN"]match 字符串表
数组变换filter map reduce any all pick_random sort_customfor 加临时数组
随机randi_range()、randf_range()、RandomNumberGenerator自写 LCG
浮点比较is_equal_approx()、is_zero_approx()== 比较浮点
帧末调用callable.call_deferred()、set_deferred()用 Timer 凑一帧
数据放 Inspector 里改Resource 子类加 @exportJSON 手写解析

做法

静态类型

<!-- check: gdonly -->
extends Node

var speed: float = 200.0
var hp := 10
var names: Array[String] = []
var scores: Dictionary[StringName, int] = {}
var target: Node2D

func take_damage(amount: int) -> bool:
	hp -= amount
	return hp <= 0

func _ready() -> void:
	var raw: Dictionary = {"k": 1}
	var value: int = raw["k"]
	var sprite := get_node_or_null("Sprite") as Sprite2D
	print(value, sprite)
写法结果改法
:= 右值是 Variant,如无类型 Dictionary 取值、call()错误 inference_on_variantvar v: int = raw["k"]
var x := max(a, b)同上,clamp min max lerp abs sign floor ceil round snapped 都返回 Variant用 maxf clampi 这类带类型版本
load(path)返回 Resourcevar tex: Texture2D = load(path),常量路径用 preload()
$Node、get_node()返回 Node@onready var sprite: Sprite2D = $Sprite
x as Sprite2D 类型不符静默得 null只在就要这个结果时用
map() 结果赋给 Array[T]报错,map() 返回无类型 Arraytyped.assign(untyped)
整数传给枚举参数int_as_enum_without_cast写成员名或显式转换
  • Array[T] 运行时强制元素类型,filter() 保留元素类型
  • Dictionary[K, V] 4.4+,不支持再嵌套类型
  • 循环变量写类型:for n: Node in nodes:
  • 大量数值用 PackedFloat32Array,Array[float] 仍是 Variant 数组
  • 对象类型可为 null,数值与字符串类型不可

class_name 与 preload

  • class_name Foo 注册全局类,任何脚本可直接当类型和构造
  • 类名不能与 autoload 同名,报 Class "Foo" hides an autoload singleton
  • 类名不能与引擎类同名
  • 无 class_name 的脚本用常量引用,preload 参数须是常量字符串
  • 路径运行时才定用 load()
<!-- check: skip; gdonly -->
const Rifle := preload("res://weapons/rifle.gd")

var main_weapon: Rifle = Rifle.new()
  • 两个脚本互相 preload 在 4.7 可加载,仍建议用 class_name 打断环
  • @export 类型只能是内置类型、Resource、Node、枚举
  • 3.x 的 export var onready var yield connect("sig", obj, "method") 在 4.x 都没了

注解

<!-- check: gdonly -->
extends Node2D

@export var speed: float = 200.0
@export_range(0.0, 1.0, 0.01) var ratio := 0.5
@export_enum("Idle", "Run") var mode: int = 0
@export_file("*.tscn") var next_scene: String
@export var bullet_scene: PackedScene
@export var spawn_point: Marker2D
@export_flags_2d_physics var hit_mask: int

@export_group("Loot")
@export var drops: Array[Resource] = []

@onready var label: Label = %Label
@onready var timer: Timer = $Timer

func _ready() -> void:
	timer.start()
	label.text = str(speed)

全表见 references/annotations.md。

时刻发生
声明初值脚本默认值
_init()读到的是默认值
场景保存的值赋入导出变量的 setter 此时调用,@onready 变量仍是 null
@onready 赋值紧接着 _ready()
  • @export 的类型来自标注或默认值,都没有是解析错误,要 Variant 写 : Variant
  • @export var n: Node 只在 Node 派生脚本里可用
  • setter 里用节点前判断 is_node_ready()
  • @onready 与 @export 同用是错误 onready_with_export
  • 成员默认值里用 $ 不带 @onready 是错误 get_node_default_without_onready
  • 代码 add_child() 的节点直接赋成员,不需要 @onready
  • 场景里已存的属性值覆盖脚本的新默认值
  • 导出 Resource 随场景一起加载,大资源用 @export_file 存路径,用时 load()
  • 4.5+ @export_file 存 UID,要原始路径用 @export_file_path
  • @export_tool_button("名字") 4.4+,须配 @tool

信号

<!-- check: gdonly -->
extends Node

signal died
signal damaged(amount: int, source: Node)

@export var button: Button

func _ready() -> void:
	button.pressed.connect(_on_pressed)
	damaged.connect(_log_damage.bind("player"))
	died.connect(queue_free, CONNECT_ONE_SHOT)

func _on_pressed() -> void:
	damaged.emit(3, self)

func _log_damage(amount: int, _source: Node, tag: String) -> void:
	print(tag, amount)
  • bind() 的参数追加在发射参数之后,unbind(n) 丢掉末尾 n 个发射参数
  • 同一 Callable 连两次返回 ERR_INVALID_PARAMETER 并打印错误,先 is_connected()
  • CONNECT_ONE_SHOT 触发一次后断开,CONNECT_DEFERRED 推迟到帧末
  • 接收者释放时连接自动断开,lambda 捕获 self 成员时也一样
  • 只由外部发射的信号加 @warning_ignore("unused_signal")
  • emit() 不检查参数类型,接收方法参数类型不符时调用报错
<!-- check: gdonly -->
extends Node

func _ready() -> void:
	var stats: Dictionary = {"count": 0}
	var bump := func() -> void:
		stats["count"] += 1
	bump.call()
	bump.call()
	print(stats["count"])
  • lambda 创建时按值捕获局部变量一次
  • lambda 里 count += 1 只改副本,触发 confusable_capture_reassignment
  • 要回写就捕获 Array、Dictionary 或对象,改其内容

await 与协程

<!-- check: gdonly -->
extends Node

signal answered(choice: int)

func open_door(door: Node3D) -> void:
	await get_tree().create_timer(1.0).timeout
	if not is_inside_tree() or not is_instance_valid(door):
		return
	door.hide()

func ask() -> int:
	var choice: int = await answered
	return choice
情况结果
调用协程不写 await跑到第一个 await 就返回,missing_await 默认关
信号带 1 个参数await 得到该值
信号带多个参数得到 Array
信号无参数得到 null
await 期间自己离开场景树get_tree() 打印错误并返回 null
await 期间自己被 free()协程不再恢复,后面的清理不执行
  • await 之后先检查 is_inside_tree() 和目标 is_instance_valid()
  • 要清理的状态放 _exit_tree() 或 tree_exiting 里复位
  • create_timer 默认暂停时也走,要受暂停影响第二参数传 false
  • 等一帧 await get_tree().process_frame,等物理帧 physics_frame
  • 没有内置超时,用 Timer 的 timeout 和业务信号谁先到谁处理

对象有效性

对象规则
已 free() 的对象变量不自动变 null,访问报 previously freed,用 is_instance_valid() 判断
queue_free() 后帧末前is_instance_valid() 仍为真,用 is_queued_for_deletion()
RefCounted、Resource引用计数,不悬空,不用 free()
长期持有可能被释放的节点存 weakref(node),用时 get_ref()
  • 物理回调、信号回调、遍历子节点时释放用 queue_free()
  • _init() 里节点不在树内,get_node() 失败

Callable 与延迟调用

<!-- check: gdonly -->
extends Node

func _ready() -> void:
	var child := Node.new()
	add_child.call_deferred(child)
	set_deferred(&"process_mode", Node.PROCESS_MODE_DISABLED)
	var sorted: Array[int] = [3, 1, 2]
	sorted.sort_custom(func(a: int, b: int) -> bool: return a > b)
  • call_deferred 在当前帧空闲阶段末尾执行,返回值永远是 null
  • 物理回调里改碰撞形状、Area2D.monitoring、CollisionShape2D.disabled 用 set_deferred
  • 线程里碰场景树只能 call_deferred
  • 方法引用写 _on_pressed,不写 Callable(self, "_on_pressed")
  • callable.is_valid() 判断绑定对象是否还在

警告设置

项目设置 debug/gdscript/warnings/*,0 忽略、1 警告、2 错误,高级设置里可见。全表见 references/warnings.md。全类型化项目建议:

[debug]

gdscript/warnings/untyped_declaration=2
gdscript/warnings/missing_await=2
gdscript/warnings/narrowing_conversion=2
gdscript/warnings/int_as_enum_without_cast=2
gdscript/warnings/unassigned_variable=2
  • 单句忽略 @warning_ignore("unused_signal")
  • 区域忽略 @warning_ignore_start 与 @warning_ignore_restore
  • directory_rules 默认忽略 res://addons

陷阱

match、静态成员、setter、super 的完整写法见 references/syntax.md。

现象做法
5 / 2 得 2,触发 integer_division5 / 2.0 或 float(a) / b
var b = a 后改 b,a 也变Array Dictionary 按引用传递,duplicate(),嵌套 duplicate(true)
const 数组 append 报 Array is in read-only stateconst 集合只读,改用 var
父类 _init() _ready() 没执行子类覆盖后要显式 super()
声明初值没触发 setter初值不调 setter,类内其他赋值才调
unused_parameter参数名加下划线前缀 _delta
narrowing_conversion浮点传整数参数时 int() roundi() floori()
固定种子不生效运行时已自动 randomize(),用 seed(),之后别再 randomize()
遍历数组时增删元素跳项遍历 arr.duplicate() 或倒序下标
call("do_it") 拼写错到运行时才暴露用 Callable 引用与信号对象
native_method_override 报错方法与引擎原生方法同名,改名
shadowed_variable局部变量与成员同名,改名
带 static var 的脚本持有大资源不释放脚本不会卸载,手动清空

参考

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.