resource-pattern について
Godot 4.3以降の開発者向けデータ設計スキルです。Resourceで編集・再利用できる設定やゲームデータを扱う方法と、共有状態やインスタンスごとの複製を解説します。
主な機能
- 01カスタムResourceとInspector編集
- 02データ駆動型のゲーム設定
- 03Resourceコレクションと読み込み
- 04共有リソースとインスタンス複製の管理
- 05Resourceの保存とシリアライズ
活用シーン
- 01アイテム、敵、能力のデータ定義
- 02Inspectorでの設定編集
- 03シーン間でのデータリソース再利用
- 04型安全なゲームデータの保存
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jame581/ godotprompter --skill resource-patternインストール後、Claude Code で/resource-patternを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/collections.mdconfiguration-pattern.mdeditor-integration.mdsaving-resources.mdsharing-vs-unique.md詳細
Godot 4.3+ のリソースパターン
リソースは、Godot に組み込まれているデータコンテナです。シーンツリーの外部に存在する設定、アイテム定義、キャラクターのステータスなどのデータに使用します。すべての例は、非推奨 API を使用せず Godot 4.3+ を対象としています。
関連スキル: リソースベースのアイテム定義については inventory-system、リソースのシリアライズについては save-load、データ駆動型のコンポーネント設定については component-system、このパターンを基盤とするリソースベースのアビリティ定義については ability-system を参照してください。
1. リソースとは
Resource は、参照カウント方式のデータオブジェクトです。次の特徴があります。
.tres(テキスト)または.res(バイナリ)ファイルとしてディスクに保存されます- Godot のインスペクターで直接編集できます
- 一度だけ読み込まれ、デフォルトでは共有されます — 同じパスを読み込むすべてのノードが、同じメモリ上のオブジェクトを取得します
- 他のリソースや PackedScene の中にネストできます
- シーン変更後も、何か(オートロードや static var など)が参照している間だけ存続します —
RefCountedであるため、最後の参照がなくなると解放されます
リソースはデフォルトで共有されるため、読み取り専用データ(アイテム定義、オーディオ設定、アビリティの設計図など)に最適です。インスタンスごとに変更可能な状態を持たせるには、duplicate() を呼び出す(または resource_local_to_scene を設定する)必要があります。セクション 8 を参照してください。
2. リソースを使用する場面
| 用途 | リソースの例 | 代替手段 |
|---|---|---|
| アイテム定義 | 名前、アイコン、価値を持つ ItemData | Dictionary(型安全性が失われます) |
| 敵の設定 | 体力、速度、ダメージを持つ EnemyStats | Node のエクスポート変数(再利用できません) |
| キャラクターのステータス | 基本値を持つ CharacterStats | オートロード(グローバル状態となり、テストが困難です) |
| アビリティ定義 | クールダウン、コスト、効果を持つ AbilityData | ハードコードされた定数 |
| レベルメタデータ | 音楽、制限時間、目標を持つ LevelConfig | JSON(エディターとの連携がありません) |
| オーディオ/ビジュアルのテーマ | カラーパレット、フォントを持つ UIThemeData | Theme リソース(同じ考え方で、組み込みのものです) |
| 会話ツリー | 次の行を参照する DialogueLine | JSON(型チェックがありません) |
インスペクターでの編集+型付きデータ+シーン間での共有が必要な場合は、カスタムリソースを使用してください。
3. 基本的なカスタムリソース
GDScript
# item_data.gd
class_name ItemData
extends Resource
enum ItemType { WEAPON, ARMOUR, CONSUMABLE, QUEST }
@export var name: String = ""
@export var description: String = ""
@export var icon: Texture2D
@export var value: int = 0
@export var item_type: ItemType = ItemType.CONSUMABLE
エディターでインスタンスを作成します。FileSystem パネルを右クリック → New Resource → ItemData を選択します。インスペクターのフィールドに入力し、res://data/items/health_potion.tres として保存します。
実行時に読み込みます。
var potion: ItemData = load("res://data/items/health_potion.tres")
print(potion.name) # "Health Potion"
print(potion.value) # 50
C#
// ItemData.cs
using Godot;
[GlobalClass]
public partial class ItemData : Resource
{
public enum ItemType { Weapon, Armour, Consumable, Quest }
[Export] public string Name { get; set; } = "";
[Export] public string Description { get; set; } = "";
[Export] public Texture2D Icon { get; set; }
[Export] public int Value { get; set; } = 0;
[Export] public ItemType Type { get; set; } = ItemType.Consumable;
}
エディターがクラスを認識し、New Resource に表示するには、C# では
[GlobalClass]が必要です。
var potion = GD.Load<ItemData>("res://data/items/health_potion.tres");
GD.Print(potion.Name); // "Health Potion"
GD.Print(potion.Value); // 50
4. エディターとの連携
class_name と @icon を使用すると、カスタムリソースをインスペクターで第一級の要素として扱えます(セッターをエディターで実行する必要がある場合のみ @tool を追加します)。リソースピッカーに表示され、右クリックして「New Resource」から作成でき、カスタムアイコンも表示されます。@export_group と @export_subgroup でプロパティを整理できます。
class_name、@icon、@export_groupを使った GDScript + C# の完全なパターンについては、references/editor-integration.md を参照してください。
5. 設定としてのリソース
最も効果を発揮する用途は、データ駆動型のゲームコンテンツです。戦利品テーブル、敵のステータス、アビリティ定義、アイテムカタログをすべてカスタムリソースにできます。デザイナーはインスペクターで .tres ファイルを調整し、プログラマーはローダーを組み込みます。JSON の緩いスキーマや、文字列に依存したパースを避けられます。
LootTable + DropEntry の具体例(GDScript + C#)については、references/configuration-pattern.md を参照してください。
6. リソースのコレクション
@export var entries: Array[Entry] = [] を使うと、型付き配列をインスペクターに表示できます。複数のリソースファイルをドラッグ&ドロップできます。起動時に読み込むセットには ResourcePreloader を使用します。実行時にアセットフォルダーを検索するには、DirAccess を使って走査します。
型付き配列のエクスポート(v1.6.0 の C# 対応を維持)、
ResourcePreloaderの設定、ディレクトリー走査によるローダーパターンについては、references/collections.md を参照してください。
7. リソースとノード
| 項目 | リソース | ノード |
|---|---|---|
| 目的 | データの保存と設定 | 動作、レンダリング、物理演算、入力 |
| シーンツリー | ツリーには含まれません | シーンツリー内に存在します |
| ライフサイクルフック | ツリーのコールバックはありません(_init、_setup_local_to_scene) | _ready、_process、_physics_process など |
| 共有 | デフォルトで共有されます(同じパスなら同じオブジェクト) | 各インスタンスは独立しています |
| シリアライズ | .tres / .res として保存され、インスペクターで編集できます | .tscn 内に保存されます |
| シグナル | 対応しています | 対応しています |
| 用途 | アイテムデータ、ステータス、設定、アビリティの設計図 | プレイヤー、敵、UI ウィジェット、カメラ |
| 避ける用途 | フレームごとの更新やシーンのクエリが必要なもの | 実行時に変化しない静的データ |
目安: 動作がなく、シーンツリー内に存在する必要もない場合は、リソースにします。移動、レンダリング、入力の受け取り、フレームごとのロジック実行が必要な場合は、ノードにします。
8. 共有と固有
デフォルトでは、リソースは参照によって共有されます。res://items/sword.tres を参照する2つのシーンは、同じインスタンスを参照します。一方を変更すると、もう一方にも反映されます。インスタンスごとの状態には、.duplicate()(浅い複製)または .duplicate(true)(深い複製。バージョン固有の制限についてはリファレンスを参照)を使用してください。
パターンの全容については、references/sharing-vs-unique.md を参照してください(v1.6.0 の C# との同等性を維持しています)。
9. カスタムリソースの保存
ResourceSaver.save(resource, path) は .tres(テキスト)または .res(バイナリ)ファイルに書き込みます。スキーマがリソースサブクラスとしてコード内にあるセーブデータには、強い型付けを使いたい場合、手作業で作成した JSON の代わりに使用してください。
保存と読み込みのパターンの全容(GDScript + C#)とセキュリティ上の注意(埋め込み GDScript が実行されるため、信頼できないソースから
.tresを読み込まないでください)については、references/saving-resources.md を参照してください。
10. アンチパターン
duplicate() を使わない変更可能な共有リソース — 意図しない状態の共有
# BAD — all enemies share the same EnemyStats object.
# Damaging one enemy damages all of them.
class_name Enemy
extends CharacterBody2D
@export var stats: EnemyStats # loaded from .tres, shared
func take_damage(amount: int) -> void:
stats.health -= amount # mutates the shared Resource!
# GOOD — each enemy owns its own copy.
func _ready() -> void:
stats = stats.duplicate() # now safe to mutate
リソース内のゲームロジック
# BAD — Resources have no scene tree access, no _process, no signals from nodes.
class_name EnemyStats
extends Resource
func update_health_regen(delta: float) -> void:
# Can't call get_tree(), can't read Input, can't access nodes.
# This logic belongs in a Node.
health = min(health + regen_rate * delta, max_health)
# GOOD — keep logic in Nodes, data in Resources.
# enemy.gd
func _process(delta: float) -> void:
_current_health = minf(_current_health + stats.regen_rate * delta, stats.max_health)
巨大なモノリシックリソース
# BAD — one Resource holds everything; impossible to reuse parts.
class_name GameConfig
extends Resource
@export var player_health: int
@export var player_speed: float
@export var enemy_goblin_health: int
@export var enemy_goblin_speed: float
@export var enemy_troll_health: int
# ... 200 more properties
# GOOD — small focused Resources, composed together.
class_name PlayerConfig
extends Resource
@export var health: int = 100
@export var speed: float = 200.0
class_name EnemyConfig
extends Resource
@export var health: int = 50
@export var speed: float = 80.0
// ❌ Anti-pattern: mutating a shared Resource — accidental shared state
// All enemies share the same EnemyStats object loaded from the .tres file.
// Damaging one enemy damages all of them.
[GlobalClass]
public partial class Enemy : CharacterBody2D
{
[Export] public EnemyStats Stats; // loaded from .tres, shared by default
public void TakeDamage(int amount)
{
Stats.Health -= amount; // mutates the shared Resource — affects every Enemy!
}
}
// ✅ Correct: duplicate before mutating so each instance owns its own copy.
public partial class EnemyGood : CharacterBody2D
{
[Export] public EnemyStats Stats;
public override void _Ready()
{
Stats = (EnemyStats)Stats.Duplicate(); // now safe to mutate independently
}
public void TakeDamage(int amount)
{
Stats.Health -= amount; // only affects this instance
}
}
// ❌ Anti-pattern: game logic inside a Resource
// Resources have no scene-tree access, no _Process, and cannot call GetTree() or read Input.
[GlobalClass]
public partial class EnemyStatsBad : Resource
{
[Export] public float Health { get; set; }
[Export] public float MaxHealth { get; set; }
[Export] public float RegenRate { get; set; }
// This logic belongs in a Node, not a Resource.
public void UpdateHealthRegen(double delta)
{
// Cannot call GetTree(), cannot access nodes, cannot read Input.
Health = Mathf.Min(Health + RegenRate * (float)delta, MaxHealth);
}
}
// ✅ Correct: keep logic in Nodes, data in Resources.
public partial class EnemyCorrect : CharacterBody2D
{
[Export] public EnemyStats Stats;
private float _currentHealth;
public override void _Ready()
{
Stats = (EnemyStats)Stats.Duplicate();
_currentHealth = Stats.MaxHealth;
}
public override void _Process(double delta)
{
_currentHealth = Mathf.Min(_currentHealth + Stats.RegenRate * (float)delta, Stats.MaxHealth);
}
}
// ❌ Anti-pattern: giant monolithic Resource
// One Resource holds everything; impossible to reuse individual pieces.
[GlobalClass]
public partial class GameConfigBad : Resource
{
[Export] public int PlayerHealth { get; set; }
[Export] public float PlayerSpeed { get; set; }
[Export] public int EnemyGoblinHealth { get; set; }
[Export] public float EnemyGoblinSpeed { get; set; }
[Export] public int EnemyTrollHealth { get; set; }
// ... 200 more properties
}
// ✅ Correct: small focused Resources, composed together.
[GlobalClass]
public partial class PlayerConfig : Resource
{
[Export] public int Health { get; set; } = 100;
[Export] public float Speed { get; set; } = 200.0f;
}
[GlobalClass]
public partial class EnemyConfig : Resource
{
[Export] public int Health { get; set; } = 50;
[Export] public float Speed { get; set; } = 80.0f;
}
11. チェックリスト
- リソースのサブクラスに
class_nameを指定し、エディターの 新規リソース から見つけられるようにする - C# クラスに
[GlobalClass]属性を付ける - インスペクターで編集可能なフィールドすべてに
@export/[Export]を使用する - デザイナーが調整可能な範囲を設ける数値フィールドに
@export_rangeを使用する - 多数のフィールドを持つリソースでは、インスペクターのレイアウトを整理するために
@export_groupと@export_categoryを使用する - インスタンスごとに変更するリソースを
_ready()でduplicate()する - 読み取り専用の共有リソース(定義、設計図)は複製しない
- ゲームロジック(フレームごとの更新、シーンのクエリ)はリソースではなくノードに置く
- 大規模なデータセットを、単一の責務に特化したリソースに分割する
- 開発中は
.tresを使用し、リリース用の本番データには.resの使用を検討する -
ResourceSaver.save()の戻り値を確認し、エラーをpush_error()で報告する - 信頼できない外部ソースから
.tres/.resファイルを読み込まない