godot-master by thedivergentai/gd-agentic-skills
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-master每个章节都通过聚焦于 知识差异 来赢得其令牌——即 Claude 已有的知识与一位资深 Godot 工程师从实际产品开发中获得的经验之间的差距。
在编写任何系统之前,为每一份状态数据回答以下三个问题:
StatsComponent 拥有生命值,而不是 CombatSystem)apply_damage() 这样的公共方法)health_changed 信号的对象)如果你不能为每个状态变量回答所有三个问题,那么你的架构就存在耦合问题。这不是 OOP 封装——这是 Godot 特有的,因为信号系统就是执行机制,而不是访问修饰符。
将每个功能组织成四层。信号向上传递,绝不向下:
┌──────────────────────────────┐
│ 表现层 (UI / VFX) │ ← 监听信号,从不拥有数据
├──────────────────────────────┤
│ 逻辑层 (状态机) │ ← 编排状态转换,查询数据
├──────────────────────────────┤
│ 数据层 (资源 / .tres) │ ← 单一事实来源,可序列化
├──────────────────────────────┤
│ 基础设施层 (自动加载项) │ ← 信号总线、SaveManager、AudioBus
└──────────────────────────────┘
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
关键规则:表现层绝不能直接修改数据层。基础设施层只能通过信号进行通信。如果一个 Label 节点正在调用 player.health -= 1,那么架构就出问题了。
match_started、player_died、settings_changed)。调试混乱是其代价——将事件限制在 < 15 个。CombatBus 仅用于战斗节点)。这是可扩展的折中方案。| 场景 | 策略 | 必读 技能链 | 权衡 |
|---|---|---|---|
| 快速原型 | 事件驱动的单场景 | 阅读:基础 → 自动加载项架构。不要加载 类型或平台参考。 | 启动快,有代码混乱风险 |
| 复杂 RPG | 组件驱动 | 阅读:组合模式 → 状态机 → RPG 属性。不要加载 多人游戏或平台参考。 | 设置复杂,无限扩展 |
| 大型开放世界 | 资源流式加载 | 阅读:开放世界 → 保存/加载。同时加载性能优化。 | 复杂的 I/O,超过 1 万单位后浮点精度抖动 |
| 服务器验证多人游戏 | 确定性 | 阅读:服务器架构 → 多人游戏。不要加载 单人游戏类型参考。 | 高延迟,反作弊安全 |
| 移动端/网页端移植 | 自适应响应式 | 阅读:UI 容器 → 桌面→移动端适配 → 移动平台。 | UI 复杂度高,覆盖面广 |
| 应用程序 / 工具 | 应用组合 | 阅读:应用组合 → 主题化。不要加载 游戏特定参考。 | 与游戏不同的范式 |
| 恋爱 / 约会模拟 | 好感度经济 | 阅读:恋爱系统 → 对话系统 → UI 富文本。 | UI/叙事密度高 |
| 秘密 / 彩蛋 | 故意混淆 | 阅读:秘密机制 → 持久化。 | 社区参与度高,调试风险 |
| 收集任务 | 寻宝逻辑 | 阅读:收集系统 → Marker3D 放置。 | 玩家留存率高,探索驱动力强 |
| 季节性活动 | 运行时注入 | 阅读:节日主题 → 材质替换。 | 品牌更新快,无资源污染 |
| 类魂游戏死亡机制 | 风险回报复活 | 阅读:复活/捡尸跑 → 3D 物理。 | 紧张感强,玩家挫败感风险 |
| 波次制动作游戏 | 战斗节奏循环 | 阅读:波次系统 → 战斗系统。 | 紧张感逐步升级,遭遇战设计 |
| 生存经济 | 采集循环 | 阅读:采集系统 → 库存系统。 | 资源稀缺,循环持久 |
| 竞速 / 速通 | 验证循环 | 阅读:计时赛 → 输入缓冲。 | 精度要求高,幽灵记录驱动力 |
这是最具影响力的专家级决策之一。Godot 文档明确指出"避免对所有东西都使用节点":
| 类型 | 何时使用 | 成本 | 专家用例 |
|---|---|---|---|
Object | 自定义数据结构,手动内存管理 | 最轻量。必须手动调用 .free()。 | 自定义空间哈希映射,类似 ECS 的数据存储 |
RefCounted | 临时数据包,自动删除的逻辑对象 | 当没有引用时自动删除。 | DamageRequest、PathQuery、AbilityEffect —— 不需要场景树的逻辑数据包 |
Resource | 支持检查器序列化的数据 | 比 RefCounted 稍重。处理 .tres I/O。 | ItemData、EnemyStats、DialogueLine —— 任何设计师应在检查器中编辑的数据 |
Node | 需要 _process/_physics_process,需要存在于场景树中 | 最重——每个节点都有场景树开销。 | 仅用于需要每帧更新或空间变换的实体 |
专家模式:对所有逻辑数据包和数据容器使用 RefCounted 子类。将 Node 保留给必须存在于空间树中的对象。这可以将复杂系统的场景树开销减半。
从空项目到生产就绪的容器。
必读——阅读整个文件:基础
/features/player/、/features/combat/),而不是按类类型。一个 player/ 文件夹包含玩家的场景、脚本、资源和测试。GlobalSignalBus 自动加载项。untyped_declaration 警告。.gitignore、导出预设和输入映射。构建模块化、可测试的角色。
必读链——全部阅读:组合模式 → 状态机 → CharacterBody2D 或 3D 物理 → 动画树 在实体工作中不要加载 UI、音频或保存/加载参考。
InputComponent,绝不直接处理输入。这使得 AI/玩家切换无需重构。MoveState 告诉 MoveComponent 执行动作,而不是反过来。通过资源连接战斗、库存、属性。
Resource 的 ItemData.gd。将其实例化为 100 个 .tres 文件,而不是 100 个脚本。player_health_changed 信号。@export Resource 变量上启用"场景本地化",或在 _ready() 中调用 resource.duplicate()。不这样做是第 8 部分中的 Bug #1。.get("key", default_value)。诊断优先方法(绝不盲目优化):
_process 是否在数百个节点上被调用。迁移到单管理器模式或服务器 API(见第 6 部分)。MultiMeshInstance。使用 ORM 纹理批量处理材质。_process 而不是 _physics_process 进行移动。load() 或 NavigationServer 重新计算。使用 ResourceLoader.load_threaded_request()。InputManager 自动加载项,将所有输入类型转换为规范化的动作。绝不直接读取 Input.is_key_pressed() —— 这会阻止控制器和触摸支持。MarginContainer 来处理刘海屏/挖孔屏设备。AudioServer 在首次播放前需要用户交互(浏览器策略)。通过"点击开始"屏幕来处理。必读:程序化生成 → Tilemap 精通 或 3D 世界构建 → 导航
seed 的 FastNoiseLite 资源进行确定性生成。NavigationServer3D.parse_source_geometry_data() + NavigationServer3D.bake_from_source_geometry_data_async()。WorkerThreadPool 进行。在树外构建场景区块,然后在主线程上使用 add_child.call_deferred()。MultiplayerSpawner 在 Godot 4 中处理复制。按场景配置,而不是全局配置。.theme 资源作为全局外观的单一事实来源。Tween 缩放脉冲,伤害数字上的 RichTextEffect,面板过渡上的 AnimationPlayer。Control.FOCUS_MODE_ALL 并测试完整的键盘/手柄导航。不可访问的 UI 会阻碍主机认证。GPUParticles3D。仅当脚本需要读取/写入单个粒子位置时才使用 CPUParticles。visibility_aabb。自动计算的 AABB 通常是错误的,导致发射器离屏时粒子消失。screen_texture 的 CanvasItem 着色器进行后期处理。在 3D 中,使用带有自定义 Environment 资源的 WorldEnvironment。ReflectionProbe 对比 VoxelGI 对比 SDFGI:探针便宜/静态,VoxelGI 中等/烘焙,SDFGI 昂贵/动态。根据你的平台预算选择(见第 5 部分)。每条规则都包含非显而易见的原因——只有实际产品开发经验才能教会的东西。
get_tree().root.get_node("...") —— 当任何祖先节点被重命名或重新设置父节点时,绝对路径会失效。使用 %UniqueNames、@export NodePath 或基于信号的发现。_process 内部使用 load() —— 同步磁盘读取会阻塞整个主线程。对于小资源在脚本顶部使用 preload(),对于大资源使用 ResourceLoader.load_threaded_request()。queue_free() —— 持有引用的父节点或数组将出现"已删除对象"错误。在 _exit_tree() 中清理引用,并在释放前将它们设为 null。_draw() 中 —— _draw() 在渲染线程上调用。改变游戏状态会导致与 _physics_process 的竞态条件。Area2D 用于 1000+ 个重叠对象 —— 每个重叠检查都有 O(n²) 的粗检测成本。对于弹幕模式,使用 ShapeCast2D、PhysicsDirectSpaceState2D.intersect_shape() 或服务器 API。HealthComponent 调用 $HUD.update_bar(),删除 HUD 会导致游戏崩溃。组件发出信号;监听器决定如何响应。_physics_process 中使用 await —— await 会暂停执行,意味着物理步会跳过帧。将异步操作移到由信号触发的单独方法中。String 键 —— 字符串哈希是 O(n)。使用 StringName(&"key")进行 O(1) 的指针比较,或使用整数枚举。Callable 引用 —— 会静默崩溃或抛出错误。在 _exit_tree() 中断开信号连接,或使用 CONNECT_ONE_SHOT。_process 用于 1000+ 个实体 —— 每个 _process 调用都有每个节点的场景树开销。使用一个遍历数据结构数组的单一 Manager._process(面向数据模式),或直接使用服务器 API。Tween —— 如果一个节点在 Tween 运行时被 queue_free(),会导致错误。在 _exit_tree() 中停止补间动画,或绑定到场景树:get_tree().create_tween()。_process 中向 RenderingServer 或 PhysicsServer 请求数据 —— 这些服务器异步运行。调用 getter 函数会强制同步停顿,严重影响性能。这些 API 在设计上故意在热路径中只允许写入。call_deferred() 作为初始化顺序错误的临时解决方案 —— 它会掩盖架构问题(对树顺序的依赖)。使用显式的初始化信号或 @onready 来修复实际的依赖关系。super() 链会是一场噩梦。使用组合(Node 子节点)来添加行为。| 指标 | 移动端目标 | 桌面端目标 | 专家备注 |
|---|---|---|---|
| 绘制调用 | < 100 (2D), < 200 (3D) | < 500 | 对植被/碎片使用 MultiMeshInstance |
| 三角形数量 | < 10 万可见 | < 100 万可见 | 超过 50 万必须使用 LOD 系统 |
| 纹理 VRAM | < 512MB | < 2GB | VRAM 压缩:ETC2(移动端),BPTC(桌面端) |
| 脚本时间 | < 4ms 每帧 | < 8ms 每帧 | 将热循环移到服务器 API |
| 物理刚体 | < 200 个活动 | < 1000 个活动 | 对大规模模拟使用 PhysicsServer 直接 API |
| 粒子数量 | < 2000 总计 | < 10000 总计 | GPU 粒子,手动设置 visibility_aabb |
| 音频总线 | < 8 个同时 | < 32 个同时 | 使用音频系统 总线路由 |
| 存档文件大小 | < 1MB | < 50MB | 种子 + 增量模式用于程序化世界 |
| 场景加载时间 | < 500ms | < 2s | ResourceLoader.load_threaded_request() |
这是大多数 Godot 开发者从未学到的知识。当场景树成为瓶颈时,使用 Godot 的低级服务器 API 完全绕过它。
RenderingServer 而不是 Sprite2D/MeshInstance3D 节点。PhysicsServer2D 创建刚体而不是 Area2D 节点。PhysicsServer3D 进行布娃娃场、碎片或类流体模拟。服务器 API 通过 RID(资源 ID)进行通信——服务器端对象的不透明句柄。关键规则:
# 创建服务器端画布项(无节点开销)
var ci_rid := RenderingServer.canvas_item_create()
RenderingServer.canvas_item_set_parent(ci_rid, get_canvas_item())
# 关键:保持资源引用存活。RID 不计为引用。
# 如果 Texture 资源被 GC 回收,RID 会静默失效。
var texture: Texture2D = preload("res://sprite.png")
RenderingServer.canvas_item_add_texture_rect(ci_rid, Rect2(-texture.get_size() / 2, texture.get_size()), texture)
add_child.call_deferred() 将它们附加到活动树。Mutex。Resource —— 使用一个加载线程。 class_name Entity extends CharacterBody2D
var _components: Dictionary = {}
func _ready() -> void:
for child in get_children():
if child.has_method("get_component_name"):
_components[child.get_component_name()] = child
func get_component(component_name: StringName) -> Node:
return _components.get(component_name)
func _on_damage_dealt(target: Node, amount: int) -> void:
if not is_instance_valid(target): return
if target.is_queued_for_deletion(): return
target.get_component(&"health").apply_damage(amount)
func _load_level_async(path: String) -> void:
ResourceLoader.load_threaded_request(path)
while ResourceLoader.load_threaded_get_status(path) == ResourceLoader.THREAD_LOAD_IN_PROGRESS:
await get_tree().process_frame
var scene: PackedScene = ResourceLoader.load_threaded_get(path)
add_child(scene.instantiate())
func can_transition_to(new_state: StringName) -> bool:
match name:
&"Dead": return false # 终止状态
&"Stunned": return new_state == &"Idle" # 只能恢复到 Idle
_: return true
func _load_chunk_threaded(chunk_pos: Vector2i) -> void:
# 在活动树外构建场景区块(线程安全)
var chunk := _generate_chunk(chunk_pos)
# 仅从主线程附加到活动树
_world_root.add_child.call_deferred(chunk)
@export 资源默认是共享的:多个场景实例都共享同一个 Resource。在 _ready() 中使用 resource.duplicate() 或启用"场景本地化"复选框。这是新手报告最多的 Godot 4 Bug #1。connect("signal_name", target, "method")(Godot 3 语法)可以编译但在 Godot 4 中不起作用。必须使用 signal_name.connect(callable)。Tween 不再是节点:通过 create_tween() 创建,绑定到创建节点的生命周期。如果该节点被释放,Tween 也会死亡。使用 get_tree().create_tween() 来创建持久的补间动画。PhysicsBody 层与掩码:collision_layer = "我是什么"。collision_mask = "我检测什么"。将两者设置为相同的值会导致自碰撞或检测失败。StringName 与 String:StringName(&"key")使用指针比较(O(1))。String 使用字符比较(O(n))。在 _process 中始终使用 StringName 作为字典键。@onready 时机:在 _init() 之后但在 _ready() 期间运行。如果你需要构造函数时设置,使用 _init()。如果你需要访问树,使用 @onready 或 _ready()。混合使用会导致空值。_process 中调用 RenderingServer 或 PhysicsServer 的 getter 函数会强制同步管道刷新。这些服务器异步运行——向它们请求数据会阻塞整个管道,直到服务器赶上。move_and_slide() API 变更:返回 bool(是否发生碰撞)。速度现在是一个属性,而不是参数。在调用 move_and_slide() 之前先设置 velocity = dir * speed。Every section earns its tokens by focusing on Knowledge Delta — the gap between what Claude already knows and what a senior Godot engineer knows from shipping real products.
Before writing any system, answer these three questions for EVERY piece of state:
StatsComponent owns health, NOT the CombatSystem)apply_damage())health_changed signal)If you can't answer all three for every state variable, your architecture has a coupling problem. This is not OOP encapsulation — this is Godot-specific because the signal system IS the enforcement mechanism , not access modifiers.
Organize every feature into four layers. Signals travel UP, never down:
┌──────────────────────────────┐
│ PRESENTATION (UI / VFX) │ ← Listens to signals, never owns data
├──────────────────────────────┤
│ LOGIC (State Machines) │ ← Orchestrates transitions, queries data
├──────────────────────────────┤
│ DATA (Resources / .tres) │ ← Single source of truth, serializable
├──────────────────────────────┤
│ INFRASTRUCTURE (Autoloads) │ ← Signal Bus, SaveManager, AudioBus
└──────────────────────────────┘
Critical rule : Presentation MUST NOT modify Data directly. Infrastructure speaks exclusively through signals. If a Label node is calling player.health -= 1, the architecture is broken.
match_started, player_died, settings_changed). Debugging sprawl is the cost — limit events to < 15.CombatBus only for combat nodes). This is the compromise that scales.| Scenario | Strategy | MANDATORY Skill Chain | Trade-off |
|---|---|---|---|
| Rapid Prototype | Event-Driven Mono | READ : Foundations → Autoloads. Do NOT load genre or platform refs. | Fast start, spaghetti risk |
| Complex RPG | Component-Driven | READ : Composition → States → RPG Stats. Do NOT load multiplayer or platform refs. | Heavy setup, infinite scaling |
| Massive Open World |
One of the most impactful expert-only decisions. The Godot docs explicitly say "avoid using nodes for everything":
| Type | When to Use | Cost | Expert Use Case |
|---|---|---|---|
Object | Custom data structures, manual memory management | Lightest. Must call .free() manually. | Custom spatial hash maps, ECS-like data stores |
RefCounted | Transient data packets, logic objects that auto-delete | Auto-deleted when no refs remain. | DamageRequest, PathQuery, AbilityEffect — logic packets that don't need the scene tree |
The expert pattern : Use RefCounted subclasses for all logic packets and data containers. Reserve Node for things that must exist in the spatial tree. This halves scene tree overhead for complex systems.
From empty project to production-ready container.
MANDATORY — READ ENTIRE FILE : Foundations
/features/player/, /features/combat/), not by class type. A player/ folder contains the scene, script, resources, and tests for the player.GlobalSignalBus autoload with < 15 events.untyped_declaration warning in Project Settings → GDScript → Debugging..gitignore, export presets, and input map.Building modular, testable characters.
MANDATORY Chain — READ ALL : Composition → State Machine → CharacterBody2D or Physics 3D → Animation Tree Do NOT load UI, Audio, or Save/Load references for entity work.
InputComponent, never handles input directly. This allows AI/Player swap with zero refactoring.MoveState tells MoveComponent to act, not the other way around.Connecting Combat, Inventory, Stats through Resources.
MANDATORY Chain — READ ALL : Resource Patterns → RPG Stats → Combat → Inventory
ItemData.gd extending Resource. Instantiate it as 100 .tres files instead of 100 scripts.player_health_changed on the Signal Bus.@export Resource variables, or call resource.duplicate() in _ready(). Failure to do this is Bug #1 in Part 8.MANDATORY : Autoload Architecture → Save/Load → Scene Management
.get("key", default_value).MANDATORY : Debugging/Profiling → Performance Optimization
Diagnosis-first approach (NEVER optimize blindly):
_process is being called on hundreds of nodes. Move to single-manager pattern or Server APIs (see Part 6).MultiMeshInstance for repetitive geometry. Batch materials with ORM textures._process is used instead of _physics_process for movement.load(), or NavigationServer recalculation. Use ResourceLoader.load_threaded_request().MANDATORY : Input Handling → Adapt Desktop→Mobile → Platform Mobile Also read : Platform Desktop, Platform Web, Platform Console, Platform VR as needed.
InputManager autoload that translates all input types into normalized actions. NEVER read Input.is_key_pressed() directly — it blocks controller and touch support.MarginContainer with Safe Area logic for notch/cutout devices.AudioServer requires user interaction before first play (browser policy). Handle this with a "Click to Start" screen.MANDATORY : Procedural Gen → Tilemap Mastery or 3D World Building → Navigation
FastNoiseLite resource with a fixed seed for deterministic generation.NavigationServer3D.parse_source_geometry_data() + NavigationServer3D.bake_from_source_geometry_data_async().WorkerThreadPool. Build the scene chunk off-tree, then add_child.call_deferred() on the main thread.MANDATORY — READ ALL : Multiplayer Networking → Server Architecture → Adapt Single→Multi Do NOT load single-player genre blueprints.
MultiplayerSpawner handles replication in Godot 4. Configure it per scene, not globally.MANDATORY : UI Theming → UI Containers → Tweening → Rich Text
.theme resource as the single source of truth for global skinning.Tween scale pulse on buttons, RichTextEffect on damage numbers, AnimationPlayer on panel transitions.Control.FOCUS_MODE_ALL and test full keyboard/gamepad navigation. Inaccessible UI blocks console certification.MANDATORY : 3D Lighting → 3D Materials → Shader Basics → Particles
GPUParticles3D for environment effects (rain, fog, fire). Use CPUParticles ONLY when script must read/write individual particle positions.visibility_aabb manually on GPU particles. The auto-calculated AABB is often wrong, causing particles to disappear when the emitter is off-screen.CanvasItem shaders with screen_texture for post-processing in 2D. In 3D, use a WorldEnvironment with custom Environment resource.ReflectionProbe vs VoxelGI vs SDFGI: Probes are cheap/static, VoxelGI is medium/baked, SDFGI is expensive/dynamic. Choose based on your platform budget (see Part 5).Each rule includes the non-obvious reason — the thing only shipping experience teaches.
get_tree().root.get_node("...") — Absolute paths break when ANY ancestor is renamed or reparented. Use %UniqueNames, @export NodePath, or signal-based discovery.load() inside a loop or _process — Synchronous disk read blocks the ENTIRE main thread. Use preload() at script top for small assets, ResourceLoader.load_threaded_request() for large ones.queue_free() while external references exist — Parent nodes or arrays holding refs will get "Deleted Object" errors. Clean up refs in _exit_tree() and set them to before freeing.| Metric | Mobile Target | Desktop Target | Expert Note |
|---|---|---|---|
| Draw Calls | < 100 (2D), < 200 (3D) | < 500 | MultiMeshInstance for foliage/debris |
| Triangle Count | < 100K visible | < 1M visible | LOD system mandatory above 500K |
| Texture VRAM | < 512MB | < 2GB | VRAM Compression: ETC2 (mobile), BPTC (desktop) |
| Script Time | < 4ms per frame | < 8ms per frame | Move hot loops to Server APIs |
| Physics Bodies | < 200 active | < 1000 active | Use PhysicsServer direct API for mass sim |
This is knowledge most Godot developers never learn. When the scene tree becomes a bottleneck, bypass it entirely using Godot's low-level Server APIs.
RenderingServer with RIDs instead of Sprite2D/MeshInstance3D nodes.PhysicsServer2D body creation instead of Area2D nodes.PhysicsServer3D directly for ragdoll fields, debris, or fluid-like simulations.Server APIs communicate through RID (Resource ID) — opaque handles to server-side objects. Critical rules:
# Create server-side canvas item (NO node overhead)
var ci_rid := RenderingServer.canvas_item_create()
RenderingServer.canvas_item_set_parent(ci_rid, get_canvas_item())
# CRITICAL: Keep resource references alive. RIDs don't count as references.
# If the Texture resource is GC'd, the RID becomes invalid silently.
var texture: Texture2D = preload("res://sprite.png")
RenderingServer.canvas_item_add_texture_rect(ci_rid, Rect2(-texture.get_size() / 2, texture.get_size()), texture)
add_child.call_deferred() to attach them to the live tree.Mutex.Resource from multiple threads simultaneously — use one loading thread.class_name Entity extends CharacterBody2D
var _components: Dictionary = {}
func _ready() -> void:
for child in get_children():
if child.has_method("get_component_name"):
_components[child.get_component_name()] = child
func get_component(component_name: StringName) -> Node:
return _components.get(component_name)
func _on_damage_dealt(target: Node, amount: int) -> void:
if not is_instance_valid(target): return
if target.is_queued_for_deletion(): return
target.get_component(&"health").apply_damage(amount)
func _load_level_async(path: String) -> void:
ResourceLoader.load_threaded_request(path)
while ResourceLoader.load_threaded_get_status(path) == ResourceLoader.THREAD_LOAD_IN_PROGRESS:
await get_tree().process_frame
var scene: PackedScene = ResourceLoader.load_threaded_get(path)
add_child(scene.instantiate())
func can_transition_to(new_state: StringName) -> bool:
match name:
&"Dead": return false # Terminal state
&"Stunned": return new_state == &"Idle" # Can only recover to Idle
_: return true
func _load_chunk_threaded(chunk_pos: Vector2i) -> void:
# Build scene chunk OFF the active tree (thread-safe)
var chunk := _generate_chunk(chunk_pos)
# Attach to live tree from main thread ONLY
_world_root.add_child.call_deferred(chunk)
@export Resources are shared by default: Multiple scene instances ALL share the same Resource. Use resource.duplicate() in _ready() or enable "Local to Scene" checkbox. This is the #1 most reported Godot 4 bug by newcomers.connect("signal_name", target, "method") (Godot 3 syntax) compiles but does nothing in Godot 4. Must use signal_name.connect(callable).Tween is no longer a Node: Created via create_tween(), bound to the creating node's lifetime. If that node is freed, the Tween dies. Use get_tree().create_tween() for persistent tweens.[!IMPORTANT] Load ONLY the modules needed for your current workflow. Use the Decision Matrix in Part 2 to determine which chain to follow.
Foundations | Composition | App Composition | Signals | Autoloads | States | Resources | Templates | MCP Setup | Skill Discovery | Skill Judge
GDScript Mastery | Testing Patterns | Debugging/Profiling | Performance Optimization
2D Animation | 2D Physics | Tilemaps | Animation Player | Animation Tree | CharacterBody2D | Particles | Tweening | Shader Basics | Camera Systems
3D Lighting | 3D Materials | 3D World Building | Physics 3D | Navigation/Pathfinding | Procedural Generation
Abilities | Combat | Dialogue | Economy | Inventory | Questing | RPG Stats | Turn System | Audio | Scene Transitions | Save/Load | Secrets | Collections | Waves | Harvesting | |
UI Containers | Rich Text | Theming | Input Handling | Seasonal Theming
Multiplayer | Server Logic | Export Builds | Desktop | Mobile | Web | Console | VR
2D→3D | 3D→2D | Desktop→Mobile | Mobile→Desktop | Single→Multi
Action RPG | Shooter | RTS | MOBA | Rogue-like | Survival | Open World | Metroidvania | Platformer | Fighting | Stealth | Sandbox | Horror | Puzzle | Racing | | | | | | | | | | |
Symptom : Node exists in tree but isn't rendering. Expert diagnosis chain : visible property → z_index → parent CanvasLayer wrong layer → modulate.a == 0 → behind camera's near clip (3D) → SubViewport.render_target_update_mode not set → CanvasItem not in any CanvasLayer (renders behind everything).
Symptom : Clicks or key presses ignored intermittently. Expert diagnosis : Another Control node with mouse_filter = STOP overlapping the target. Or, modal PopupMenu consuming unhandled input. Or, _unhandled_input() in another script calling get_viewport().set_input_as_handled().
Symptom : Character vibrates at surface contacts. Expert diagnosis : Safe Margin too large. Or, _process used for movement instead of _physics_process (interpolation mismatch). Or, collision shapes overlap at spawn (push each other apart permanently).
Symptom : RAM grows steadily during play. Expert diagnosis : queue_free() called but reference held in Array/Dictionary. Or, signals connected with CONNECT_REFERENCE_COUNTED without cleanup. Use Profiler "Objects" tab to find orphaned instances. Search for Node instances without a parent.
Symptom : Smooth FPS but periodic drops. Expert diagnosis : GDScript GC pass. Or, synchronous load() for a large resource. Or, NavigationServer rebaking. Or, Server API query stall (requesting data from RenderingServer in _process). Profile with built-in Profiler → look for function-level spikes.
Weekly Installs
166
Repository
GitHub Stars
59
First Seen
Feb 10, 2026
Security Audits
Gen Agent Trust HubWarnSocketPassSnykPass
Installed on
opencode160
codex159
gemini-cli158
amp155
kimi-cli155
github-copilot155
Flutter状态管理教程:MVVM与Provider实现单向数据流和架构模式
1,100 周安装
| Resource-Streaming |
| READ : Open World → Save/Load. Also load Performance. |
| Complex I/O, float precision jitter past 10K units |
| Server-Auth Multi | Deterministic | READ : Server Arch → Multiplayer. Do NOT load single-player genre refs. | High latency, anti-cheat secure |
| Mobile/Web Port | Adaptive-Responsive | READ : UI Containers → Adapt Desk→Mobile → Platform Mobile. | UI complexity, broad reach |
| Application / Tool | App-Composition | READ : App Composition → Theming. Do NOT load game-specific refs. | Different paradigm than games |
| Romance / Dating Sim | Affection Economy | READ : Romance → Dialogue → UI Rich Text. | High UI/Narrative density |
| Secrets / Easter Eggs | Intentional Obfuscation | READ : Secrets → Persistence. | Community engagement, debug risk |
| Collection Quest | Scavenger Logic | READ : Collections → Marker3D Placement. | Player retention, exploration drive |
| Seasonal Event | Runtime Injection | READ : Easter Theming → Material Swapping. | Fast branding, no asset pollution |
| Souls-like Mortality | Risk-Reward Revival | READ : Revival/Corpse Run → Physics 3D. | High tension, player frustration risk |
| Wave-based Action | Combat Pacing Loop | READ : Waves → Combat. | Escalating tension, encounter design |
| Survival Economy | Harvesting Loop | READ : Harvesting → Inventory. | Resource scarcity, loop persistence |
| Racing / Speedrun | Validation Loop | READ : Time Trials → Input Buffer. | High precision, ghost record drive |
Resource | Serializable data with Inspector support | Slightly heavier than RefCounted. Handles .tres I/O. | ItemData, EnemyStats, DialogueLine — any data a designer should edit in Inspector |
Node | Needs _process/_physics_process, needs to live in the scene tree | Heaviest — SceneTree overhead per node. | Only for entities that need per-frame updates or spatial transforms |
null_draw() — _draw() is called on the rendering thread. Mutating game state causes race conditions with _physics_process.Area2D for 1000+ overlapping objects — Each overlap check has O(n²) broadphase cost. Use ShapeCast2D, PhysicsDirectSpaceState2D.intersect_shape(), or Server APIs for bullet-hell patterns.HealthComponent calls $HUD.update_bar(), deleting the HUD crashes the game. Components emit signals; listeners decide how to respond.await in _physics_process — await yields execution, meaning the physics step skips frames. Move async operations to a separate method triggered by a signal.String keys in hot-path dictionary lookups — String hashing is O(n). Use StringName (&"key") for O(1) pointer comparisons, or integer enums.Callable references to freed objects — Crashes silently or throws errors. Disconnect signals in _exit_tree() or use CONNECT_ONE_SHOT._process for 1000+ entities — Each _process call has per-node SceneTree overhead. Use a single Manager._process that iterates an array of data structs (Data-Oriented pattern), or use Server APIs directly.Tween on a node that may be freed — If a node is queue_free()'d while a Tween runs, it errors. Kill tweens in _exit_tree() or bind to SceneTree: get_tree().create_tween().RenderingServer or PhysicsServer in _process — These servers run asynchronously. Calling getter functions forces a synchronous stall that kills performance. The APIs are intentionally designed to be write-only in hot paths.call_deferred() as a band-aid for initialization order bugs — It masks architectural problems (dependency on tree order). Fix the actual dependency with explicit initialization signals or @onready.super() chains is a nightmare. Use composition (Node children) to add behaviors instead.| Particles | < 2000 total | < 10000 total | GPU particles, set visibility_aabb manually |
| Audio Buses | < 8 simultaneous | < 32 simultaneous | Use Audio Systems bus routing |
| Save File Size | < 1MB | < 50MB | Seed + Delta pattern for procedural worlds |
| Scene Load Time | < 500ms | < 2s | ResourceLoader.load_threaded_request() |
PhysicsBody layers vs masks: collision_layer = "what I am". collision_mask = "what I scan for". Setting both to the same value causes self-collision or missed detections.StringName vs String in hot paths: StringName (&"key") uses pointer comparison (O(1)). String uses character comparison (O(n)). Always use StringName for dictionary keys in _process.@onready timing: Runs AFTER _init() but DURING _ready(). If you need constructor-time setup, use _init(). If you need tree access, use @onready or _ready(). Mixing them causes nulls.RenderingServer or PhysicsServer getter functions in _process forces a synchronous pipeline flush. These servers run async — requesting data from them stalls the entire pipeline until the server catches up.move_and_slide() API change: Returns bool (whether collision occurred). Velocity is now a property, not a parameter. velocity = dir * speed before calling move_and_slide().