Godot Best Practices

jwynia/agent-skills/skills/tech/game-development/godot/godot-best-practices

作者 jwyniae02ec7e226a6MIT168 个星标收录于 2026年10月8日更新于 2026年10月8日仓库7个月前更新

Guide AI agents through Godot 4.x GDScript coding best practices including scene organization, signals, resources, state machines, and performance optimization. This skill should be used when generating GDScript code, creating Godot scenes, designing game architecture, implementing state machines, object pooling, save/load systems, or when the user asks about Godot patterns, node structure, or GDScript standards. Keywords: godot, gdscript, game development, signals, resources, scenes, nodes, state machine, object pooling, save system, autoload, export, type hints.

AI 生成的概览

指导 AI 代理按照命名、类型、场景与架构最佳实践编写 Godot 4.x GDScript 代码。

功能
该技能为 Godot 4.x GDScript 游戏开发提供编码规范、架构模式和模板。内容涵盖命名约定、类型提示、节点引用、信号驱动通信、资源加载、导出注解,以及状态机、对象池和存档/读档等常见模式。它还列出反模式,并指向随技能附带的参考文档和脚本模板。
适用场景
适用于生成或审查 GDScript 代码、创建或组织 Godot 场景、设计节点层级与游戏架构,或实现状态机、对象池和存档系统时。不适用于 C#、GDExtension/C++、Godot 3.x 或可视化脚本相关工作。
运行要求
需要一个使用 GDScript 的 Godot 4.x 项目;不附带脚本,只有说明、参考文档和模板文件。

Godot 4.x GDScript Best Practices

Guide AI agents in writing high-quality GDScript code for Godot 4.x. This skill provides coding standards, architecture patterns, and templates for game development.

When to Use This Skill

Use this skill when:

  • Generating new GDScript code
  • Creating or organizing Godot scenes
  • Designing game architecture and node hierarchies
  • Implementing state machines, object pools, or save systems
  • Answering questions about GDScript patterns or Godot conventions
  • Reviewing GDScript code for quality issues

Do NOT use this skill when:

  • Working with C# in Godot (use C# patterns)
  • Working with Godot 3.x (syntax differs significantly)
  • Using GDExtension/C++ (different paradigm)
  • Working with Godot's visual scripting

Core Principles

1. Naming Conventions

Follow GDScript naming standards consistently:

gdscript
# Classes: PascalCaseclass_name PlayerControllerextends CharacterBody2D
# Signals: past_tense_snake_case (describe what happened)signal health_changed(new_health: int)signal player_diedsignal item_collected(item: Item)
# Constants: SCREAMING_SNAKE_CASEconst MAX_SPEED: float = 200.0const JUMP_FORCE: int = -400
# Variables and functions: snake_casevar current_health: int = 100var _private_variable: float = 0.0  # Leading underscore for private
func calculate_damage(base: int, multiplier: float) -> int:    return int(base * multiplier)
func _private_helper() -> void:  # Leading underscore for private    pass

2. Type Hints (Static Typing)

Use explicit type hints everywhere for autocomplete and error detection:

gdscript
# Variable declarationsvar speed: float = 100.0var player: CharacterBody2Dvar items: Array[Item] = []var stats: Dictionary = {}
# Function signatures with return typesfunc get_damage() -> int:    return _base_damage * _multiplier
func find_nearest_enemy(position: Vector2) -> Enemy:    # Implementation    return null
# Typed signals (Godot 4.x)signal score_updated(new_score: int, old_score: int)signal target_acquired(target: Node2D, distance: float)
# Node references with types@onready var sprite: Sprite2D = $Sprite2D@onready var collision: CollisionShape2D = $CollisionShape2D@onready var animation_player: AnimationPlayer = %AnimationPlayer

3. Node References

Use modern patterns for stable, refactor-friendly references:

gdscript
# PREFER: @onready with type hints@onready var health_bar: ProgressBar = $UI/HealthBar@onready var weapon: Weapon = $WeaponMount/Weapon
# PREFER: Unique names with % for critical nodes@onready var player: Player = %Player@onready var game_manager: GameManager = %GameManager
# AVOID: get_node() in _ready()func _ready() -> void:    # Don't do this    var sprite = get_node("Sprite2D")
# AVOID: Deep fragile paths@onready var thing = $Parent/Child/GrandChild/GreatGrandChild  # Fragile

4. Signal-Driven Architecture

Use signals for decoupled communication. Follow "signal up, call down":

gdscript
# Child node emits signals (doesn't know about parent)class_name HealthComponentextends Node
signal health_changed(current: int, maximum: int)signal died
var _health: int = 100var _max_health: int = 100
func take_damage(amount: int) -> void:    _health = max(0, _health - amount)    health_changed.emit(_health, _max_health)    if _health <= 0:        died.emit()
gdscript
# Parent connects to child signals (knows about children)class_name Playerextends CharacterBody2D
@onready var health: HealthComponent = $HealthComponent@onready var sprite: Sprite2D = $Sprite2D
func _ready() -> void:    health.health_changed.connect(_on_health_changed)    health.died.connect(_on_died)
func _on_health_changed(current: int, maximum: int) -> void:    # Update UI, play effects, etc.    pass
func _on_died() -> void:    sprite.modulate = Color.RED    queue_free()

5. Resource Loading

Choose the right loading strategy:

gdscript
# preload(): Compile-time loading for critical/small assetsconst BULLET_SCENE: PackedScene = preload("res://scenes/bullet.tscn")const PLAYER_SPRITE: Texture2D = preload("res://sprites/player.png")const DAMAGE_SOUND: AudioStream = preload("res://audio/damage.wav")
# load(): Runtime loading for optional/large assetsfunc load_level(level_name: String) -> void:    var path := "res://levels/%s.tscn" % level_name    var level_scene: PackedScene = load(path)    var level := level_scene.instantiate()    add_child(level)
# ResourceLoader for async loading (prevents stuttering)func _load_level_async(path: String) -> void:    ResourceLoader.load_threaded_request(path)    # Check with: ResourceLoader.load_threaded_get_status(path)    # Get with: ResourceLoader.load_threaded_get(path)

Quick Reference

CategoryPreferAvoid
Node references@onready var x: Type = $Pathget_node() in _ready()
Unique nodes%UniqueNameDeep paths $A/B/C/D
Resource loadingpreload() for small/criticalload() everywhere
SignalsTyped: signal x(val: int)String: emit_signal("x")
Type safetyExplicit type hintsUntyped variables
Constantsconst or @exportMagic numbers/strings
Null checksis_instance_valid(node)node != null for freed nodes
Coroutinesawaityield (deprecated)
GroupsScene-specific groupsGlobal groups for everything
AutoloadsServices/managers onlyGame logic in autoloads
PropertiesSetters/gettersDirect mutation
CommunicationSignal up, call downChild calling parent methods

Code Generation Guidelines

Script Structure

Order sections consistently:

gdscript
class_name MyClassextends Node2D## Brief description of this class.#### Longer description if needed, explaining purpose and usage.
# === Signals ===signal state_changed(new_state: State)
# === Enums ===enum State { IDLE, RUNNING, JUMPING }
# === Exports ===@export var speed: float = 100.0@export_group("Combat")@export var damage: int = 10@export var attack_range: float = 50.0
# === Constants ===const MAX_HEALTH: int = 100
# === Public Variables ===var current_state: State = State.IDLE
# === Private Variables ===var _internal_counter: int = 0
# === Onready ===@onready var sprite: Sprite2D = $Sprite2D@onready var collision: CollisionShape2D = $CollisionShape2D
# === Lifecycle Methods ===func _ready() -> void:    pass
func _process(delta: float) -> void:    pass
func _physics_process(delta: float) -> void:    pass
# === Public Methods ===func take_damage(amount: int) -> void:    pass
# === Private Methods ===func _calculate_knockback() -> Vector2:    return Vector2.ZERO

Export Annotations

Use exports for editor-configurable values:

gdscript
# Basic exports@export var health: int = 100@export var speed: float = 200.0@export var player_name: String = "Player"
# Range constraints@export_range(0, 100) var percentage: int = 50@export_range(0.0, 1.0, 0.1) var volume: float = 0.8
# Resource exports@export var texture: Texture2D@export var scene: PackedScene@export var audio: AudioStream
# Grouped exports@export_group("Movement")@export var walk_speed: float = 100.0@export var run_speed: float = 200.0
@export_group("Combat")@export var attack_damage: int = 10
# Enum exports@export var difficulty: Difficulty = Difficulty.NORMALenum Difficulty { EASY, NORMAL, HARD }
# Flags (multiselect)@export_flags("Fire", "Water", "Earth", "Air") var elements: int = 0

Common Game Patterns

State Machine (Overview)

Use enum-based state machines for simple cases:

gdscript
enum State { IDLE, WALK, JUMP, ATTACK }
var current_state: State = State.IDLE
func _physics_process(delta: float) -> void:    match current_state:        State.IDLE:            _process_idle(delta)        State.WALK:            _process_walk(delta)        State.JUMP:            _process_jump(delta)        State.ATTACK:            _process_attack(delta)
func change_state(new_state: State) -> void:    if current_state == new_state:        return    _exit_state(current_state)    current_state = new_state    _enter_state(new_state)

See references/patterns/state-machine.md for advanced implementations.

Object Pooling (Overview)

Reuse objects to avoid instantiation cost:

gdscript
class_name ObjectPoolextends Node
var _pool: Array[Node] = []var _scene: PackedScene
func _init(scene: PackedScene, initial_size: int = 10) -> void:    _scene = scene    for i in initial_size:        var obj := _scene.instantiate()        obj.set_process(false)        _pool.append(obj)
func acquire() -> Node:    if _pool.is_empty():        return _scene.instantiate()    var obj := _pool.pop_back()    obj.set_process(true)    return obj
func release(obj: Node) -> void:    obj.set_process(false)    _pool.append(obj)

See references/patterns/object-pooling.md for complete implementation.

Save/Load (Overview)

Use Resources or JSON for save data:

gdscript
# Custom Resource for save dataclass_name SaveDataextends Resource
@export var player_position: Vector2@export var player_health: int@export var inventory: Array[String]@export var level_name: String
# Savefunc save_game(data: SaveData) -> void:    ResourceSaver.save(data, "user://save.tres")
# Loadfunc load_game() -> SaveData:    if ResourceLoader.exists("user://save.tres"):        return load("user://save.tres") as SaveData    return SaveData.new()

See references/patterns/save-load-system.md for comprehensive guide.

Common Anti-Patterns

Anti-PatternProblemSolution
Polling in _processWastes CPU on unchanged stateUse signals for state changes
get_parent().get_parent()Tight coupling, fragileSignal up, or use groups
Deep node paths $A/B/C/DBreaks on refactorUse %UniqueName
load() in _processStuttering, memory churnpreload() or cache reference
String signals emit_signal("x")Typos, no autocompleteTyped: signal_name.emit()
Untyped @onready var x = $NodeLoses autocompleteAlways add type hint
Logic in autoloadsTesting difficulty, couplingKeep autoloads thin
Magic numbersUnclear meaningUse const or @export
node != null for freed nodesReturns true for freedUse is_instance_valid()
Circular dependenciesLoad errors, unclear flowDependency injection or signals

Additional Resources

Pattern Guides

  • references/patterns/state-machine.md - Full state machine implementations
  • references/patterns/object-pooling.md - Complete pooling system
  • references/patterns/save-load-system.md - Comprehensive save/load guide
  • references/patterns/input-handling.md - Input buffering and rebinding

Architecture

  • references/architecture/project-structure.md - Directory organization
  • references/architecture/scene-composition.md - Scene design patterns
  • references/architecture/node-communication.md - Signals vs direct calls

GDScript Deep Dives

  • references/gdscript/type-system.md - Static typing in depth
  • references/gdscript/coroutines-await.md - Async patterns with await

Templates

  • assets/templates/base-script.gd.md - Standard script template
  • assets/templates/state-machine.gd.md - State machine template
  • assets/templates/autoload-manager.gd.md - Autoload singleton template

Limitations

  • GDScript only (not C#, GDExtension, or VisualScript)
  • Godot 4.x syntax (some patterns differ from 3.x)
  • Game-focused patterns (not editor plugin development)
  • No runtime validation scripts (GDScript requires Godot runtime)

来源与署名

来源:jwynia/agent-skills位于skills/tech/game-development/godot/godot-best-practices提交e02ec7e

许可证: MIT

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架