godot-best-practices
Godot 4.x の GDScript コーディングベストプラクティスに沿ったコード生成・設計を AI エージェントに行わせるスキルです。シーン構成、シグナル、リソース、ステートマシン、オブジェクトプーリング、セーブ/ロードシステムなど、ゲームアーキテクチャ全般の実装時に活用できます。GDScript の型ヒントや Autoload、ノード構造のパターンについて質問する際にも適しています。
description の原文を見る
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.
SKILL.md 本文
Godot 4.x GDScript ベストプラクティス
Godot 4.x の高品質な GDScript コード執筆を AI エージェントがガイドします。このスキルはコーディング標準、アーキテクチャパターン、ゲーム開発用テンプレートを提供します。
このスキルを使用する場合
以下の場合に使用してください:
- 新しい GDScript コードを生成する
- Godot シーンを作成または整理する
- ゲームアーキテクチャとノードの階層を設計する
- ステートマシン、オブジェクトプール、セーブシステムを実装する
- GDScript パターンまたは Godot の規約に関する質問に答える
- GDScript コードの品質問題をレビューする
以下の場合は使用しないでください:
- Godot で C# を使用する(C# パターンを使用)
- Godot 3.x で作業する(構文が大きく異なる)
- GDExtension/C++ を使用する(異なるパラダイム)
- Godot のビジュアルスクリプティングを使用する
コア原則
1. 命名規則
GDScript の命名標準に一貫して従います:
# クラス: PascalCase
class_name PlayerController
extends CharacterBody2D
# シグナル: past_tense_snake_case(発生したことを説明)
signal health_changed(new_health: int)
signal player_died
signal item_collected(item: Item)
# 定数: SCREAMING_SNAKE_CASE
const MAX_SPEED: float = 200.0
const JUMP_FORCE: int = -400
# 変数と関数: snake_case
var current_health: int = 100
var _private_variable: float = 0.0 # 先頭アンダースコアはプライベート用
func calculate_damage(base: int, multiplier: float) -> int:
return int(base * multiplier)
func _private_helper() -> void: # 先頭アンダースコアはプライベート用
pass
2. 型ヒント(静的型付け)
自動補完とエラー検出のため、すべての場所で明示的な型ヒントを使用します:
# 変数宣言
var speed: float = 100.0
var player: CharacterBody2D
var items: Array[Item] = []
var stats: Dictionary = {}
# 戻り値型を含む関数シグネチャ
func get_damage() -> int:
return _base_damage * _multiplier
func find_nearest_enemy(position: Vector2) -> Enemy:
# 実装
return null
# 型付きシグナル(Godot 4.x)
signal score_updated(new_score: int, old_score: int)
signal target_acquired(target: Node2D, distance: float)
# 型を持つノード参照
@onready var sprite: Sprite2D = $Sprite2D
@onready var collision: CollisionShape2D = $CollisionShape2D
@onready var animation_player: AnimationPlayer = %AnimationPlayer
3. ノード参照
リファクタリングに強い、安定した参照パターンを使用します:
# 推奨: @onready と型ヒント付き
@onready var health_bar: ProgressBar = $UI/HealthBar
@onready var weapon: Weapon = $WeaponMount/Weapon
# 推奨: 重要なノードに % で一意の名前を使用
@onready var player: Player = %Player
@onready var game_manager: GameManager = %GameManager
# 避ける: _ready() 内での get_node()
func _ready() -> void:
# これはしないこと
var sprite = get_node("Sprite2D")
# 避ける: 深く脆いパス
@onready var thing = $Parent/Child/GrandChild/GreatGrandChild # 脆い
4. シグナル駆動アーキテクチャ
デカップリングされた通信のためシグナルを使用します。「信号は上へ、呼び出しは下へ」に従います:
# 子ノードはシグナルを出力(親について知らない)
class_name HealthComponent
extends Node
signal health_changed(current: int, maximum: int)
signal died
var _health: int = 100
var _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()
# 親は子シグナルに接続(子について知っている)
class_name Player
extends 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:
# UI 更新、エフェクト再生など
pass
func _on_died() -> void:
sprite.modulate = Color.RED
queue_free()
5. リソース読み込み
適切な読み込み戦略を選択します:
# preload(): コンパイル時読み込み(重要/小さいアセット向け)
const 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(): 実行時読み込み(オプション/大きいアセット向け)
func 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 による非同期読み込み(スタッターを防止)
func _load_level_async(path: String) -> void:
ResourceLoader.load_threaded_request(path)
# ステータス確認: ResourceLoader.load_threaded_get_status(path)
# 取得: ResourceLoader.load_threaded_get(path)
クイックリファレンス
| カテゴリ | 推奨 | 避ける |
|---|---|---|
| ノード参照 | @onready var x: Type = $Path | _ready() での get_node() |
| 一意のノード | %UniqueName | 深いパス $A/B/C/D |
| リソース読み込み | 小さい/重要なものは preload() | すべてで load() |
| シグナル | 型付き: signal x(val: int) | 文字列: emit_signal("x") |
| 型安全性 | 明示的な型ヒント | 型なし変数 |
| 定数 | const または @export | マジックナンバー/文字列 |
| null チェック | is_instance_valid(node) | 解放ノードで node != null |
| コルーチン | await | yield(非推奨) |
| グループ | シーン固有のグループ | すべてにグローバルグループ |
| オートロード | サービス/マネージャーのみ | オートロード内のゲームロジック |
| プロパティ | セッター/ゲッター | 直接変更 |
| 通信 | 信号は上へ、呼び出しは下へ | 子が親メソッドを呼び出し |
コード生成ガイドライン
スクリプト構造
セクションを一貫性を保って順序付けします:
class_name MyClass
extends Node2D
## このクラスの簡潔な説明。
##
## 必要に応じて長い説明を追加し、目的と使用法を説明します。
# === シグナル ===
signal state_changed(new_state: State)
# === 列挙型 ===
enum State { IDLE, RUNNING, JUMPING }
# === エクスポート ===
@export var speed: float = 100.0
@export_group("Combat")
@export var damage: int = 10
@export var attack_range: float = 50.0
# === 定数 ===
const MAX_HEALTH: int = 100
# === パブリック変数 ===
var current_state: State = State.IDLE
# === プライベート変数 ===
var _internal_counter: int = 0
# === Onready ===
@onready var sprite: Sprite2D = $Sprite2D
@onready var collision: CollisionShape2D = $CollisionShape2D
# === ライフサイクルメソッド ===
func _ready() -> void:
pass
func _process(delta: float) -> void:
pass
func _physics_process(delta: float) -> void:
pass
# === パブリックメソッド ===
func take_damage(amount: int) -> void:
pass
# === プライベートメソッド ===
func _calculate_knockback() -> Vector2:
return Vector2.ZERO
エクスポートアノテーション
エディタで設定可能な値にはエクスポートを使用します:
# 基本的なエクスポート
@export var health: int = 100
@export var speed: float = 200.0
@export var player_name: String = "Player"
# 範囲制約
@export_range(0, 100) var percentage: int = 50
@export_range(0.0, 1.0, 0.1) var volume: float = 0.8
# リソースエクスポート
@export var texture: Texture2D
@export var scene: PackedScene
@export var audio: AudioStream
# グループ化されたエクスポート
@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
# 列挙型エクスポート
@export var difficulty: Difficulty = Difficulty.NORMAL
enum Difficulty { EASY, NORMAL, HARD }
# フラグ(複数選択)
@export_flags("Fire", "Water", "Earth", "Air") var elements: int = 0
一般的なゲームパターン
ステートマシン(概要)
簡単な場合は列挙型ベースのステートマシンを使用します:
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)
詳細な実装は references/patterns/state-machine.md を参照してください。
オブジェクトプーリング(概要)
インスタンス化のコストを避けるためにオブジェクトを再利用します:
class_name ObjectPool
extends 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)
完全な実装は references/patterns/object-pooling.md を参照してください。
セーブ/ロード(概要)
セーブデータに Resource または JSON を使用します:
# セーブデータ用カスタムリソース
class_name SaveData
extends Resource
@export var player_position: Vector2
@export var player_health: int
@export var inventory: Array[String]
@export var level_name: String
# セーブ
func save_game(data: SaveData) -> void:
ResourceSaver.save(data, "user://save.tres")
# ロード
func load_game() -> SaveData:
if ResourceLoader.exists("user://save.tres"):
return load("user://save.tres") as SaveData
return SaveData.new()
包括的なガイドは references/patterns/save-load-system.md を参照してください。
一般的なアンチパターン
| アンチパターン | 問題 | 解決策 |
|---|---|---|
_process でのポーリング | 未変更状態で CPU を無駄にする | 状態変更にはシグナルを使用 |
get_parent().get_parent() | 緊密な結合、脆い | 信号は上へ、またはグループを使用 |
深いノードパス $A/B/C/D | リファクタリングで破損 | %UniqueName を使用 |
_process での load() | スタッター、メモリ変動 | preload() またはキャッシュ参照 |
文字列シグナル emit_signal("x") | タイプミス、自動補完なし | 型付き: signal_name.emit() |
型なし @onready var x = $Node | 自動補完が失われる | 常に型ヒントを追加 |
| オートロード内のロジック | テスト困難、結合度高い | オートロードは薄く保つ |
| マジックナンバー | 意味が不明確 | const または @export を使用 |
解放ノードで node != null | 解放済みでも true を返す | is_instance_valid() を使用 |
| 循環依存 | 読み込みエラー、フロー不明確 | 依存性注入またはシグナル |
追加リソース
パターンガイド
references/patterns/state-machine.md- 完全なステートマシン実装references/patterns/object-pooling.md- 完全なプーリングシステムreferences/patterns/save-load-system.md- 包括的なセーブ/ロードガイドreferences/patterns/input-handling.md- 入力バッファリングと再バインド
アーキテクチャ
references/architecture/project-structure.md- ディレクトリ構成references/architecture/scene-composition.md- シーン設計パターンreferences/architecture/node-communication.md- シグナルと直接呼び出し
GDScript 深掘り
references/gdscript/type-system.md- 静的型付けの詳細references/gdscript/coroutines-await.md- await による非同期パターン
テンプレート
assets/templates/base-script.gd.md- 標準スクリプトテンプレートassets/templates/state-machine.gd.md- ステートマシンテンプレートassets/templates/autoload-manager.gd.md- オートロードシングルトンテンプレート
制限事項
- GDScript のみ(C#、GDExtension、VisualScript ではない)
- Godot 4.x 構文(3.x では一部パターンが異なる)
- ゲーム向けパターン(エディタプラグイン開発ではない)
- ランタイム検証スクリプトなし(GDScript は Godot ランタイムが必須)
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- jwynia
- リポジトリ
- jwynia/agent-skills
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/jwynia/agent-skills / ライセンス: MIT
関連スキル
agent-browser
AI エージェント向けのブラウザ自動化 CLI です。ウェブサイトとの対話が必要な場合に使用します。ページ遷移、フォーム入力、ボタンクリック、スクリーンショット取得、データ抽出、ウェブアプリのテスト、ブラウザ操作の自動化など、あらゆるブラウザタスクに対応できます。「ウェブサイトを開く」「フォームに記入する」「ボタンをクリックする」「スクリーンショットを取得する」「ページからデータを抽出する」「このウェブアプリをテストする」「サイトにログインする」「ブラウザ操作を自動化する」といった要求や、プログラマティックなウェブ操作が必要なタスクで起動します。
anyskill
AnySkill — あなたのプライベート・スキルクラウド。GitHubを基盤としたリポジトリからエージェントスキルを管理、同期、動的にロードできます。自然言語でクラウドスキルを検索し、オンデマンドでプロンプトを自動ロード、カスタムスキルのアップロードと共有、スキルバンドルの一括インストールが可能です。OpenClaw、Antigravity、Claude Code、Cursorに対応しています。
engram
AIエージェント向けの永続的なメモリシステムです。バグ修正、意思決定、発見、設定変更の後はmem_saveを使用してください。ユーザーが「覚えている」「記憶している」と言及した場合、または以前のセッションと重複する作業を開始する際はmem_searchを使用します。セッション終了前にmem_session_summaryを使用して、コンテキストを保持してください。
skyvern
AI駆動のブラウザ自動化により、任意のウェブサイトを自動化できます。フォーム入力、データ抽出、ファイルダウンロード、ログイン、複数ステップのワークフロー実行など、ユーザーがウェブサイトと連携する必要があるときに使用します。Skyvernは、LLMとコンピュータビジョンを活用して、未知のサイトも自動操作可能です。Python SDK、TypeScript SDK、REST API、MCPサーバー、またはCLIを通じて統合できます。
pinchbench
PinchBenchベンチマークを実行して、OpenClawエージェントの実世界タスクにおけるパフォーマンスを評価できます。モデルの機能テスト、モデル間の比較、ベンチマーク結果のリーダーボード提出、またはOpenClawのセットアップがカレンダー、メール、リサーチ、コーディング、複数ステップのワークフローにどの程度対応しているかを確認する際に使用します。
openui
OpenUIとOpenUI Langを使用してジェネレーティブUIアプリを構築できます。これらはLLM生成インターフェースのためのトークン効率的なオープン標準です。OpenUI、@openuidev、ジェネレーティブUI、LLMからのストリーミングUI、AI向けコンポーネントライブラリ、またはjson-render/A2UIの置き換えについて述べる際に使用します。スキャフォルディング、defineComponent、システムプロンプト、Renderer、およびOpenUI Lang出力のデバッグに対応しています。