敵の ステータスをスクリプトに直書きしていると、敵の種類を増やすたびにコードをコピペし、バランス調整のたびにコードを開いて数値を書き換える——そんな消耗を感じたことはないでしょうか。しかもプランナーやデザイナーは、コードを触れないと数値ひとつ変えられません。
この悩みを解くのが カスタムリソース(Custom Resource) です。Godotの Resource システムを拡張して独自のデータ型を作り、 ゲームのロジック(コード)とデータ(値)を分離 します。Unityの ScriptableObject に相当する、 データ駆動設計 の要です。
この記事でわかること
- データ駆動設計 とは(ロジックとデータの分離)
Resourceを継承した カスタムリソースの定義 とインスペクタ編集- 継承 で派生リソースを作る(
ItemData→WeaponData)duplicate()で 実行時の変更を安全 に行う- JSON / CSV / SQLite との 使い分け
データ駆動設計とは
データ駆動設計とは、 「処理(ロジック)」と「値(データ)」を切り離す 考え方です。1つの汎用的なコードに、差し替え可能なデータを与えて、多彩なバリエーションを作ります。

例えば「敵」を動かすコードは1つ。そこに「スライムのデータ」「ゴブリンのデータ」「ドラゴンのデータ」を差し替えて渡せば、同じコードが別々の敵になります。 データを直書きしない ことで、
- 敵を増やすとき、コードでなく データファイルを増やすだけ で済む
- バランス調整 が、コードを開かず インスペクタで数値を触るだけ になる
- プランナーやデザイナーも、コードを知らずにデータを編集できる
——と、開発がぐっと楽になります。この「データ」を担うのがカスタムリソースです。
カスタムリソースを定義して使う
カスタムリソースは、Resource を継承したスクリプトで 型を定義 し、その 値をインスペクタで編集 して、 .tres ファイルに保存 します。

まず、Resource を継承して @export でプロパティを並べます。class_name を付けると、グローバルな型として使えます。
# character_data.gd
class_name CharacterData
extends Resource
@export var character_name: String = "New Character"
@export var max_health: int = 100
@export var attack_power: float = 15.0
enum Job { WARRIOR, MAGE, ROGUE }
@export var job: Job = Job.WARRIOR # enumはインスペクタで選択式になる
# データに紐づくロジックをメソッドで持たせることもできる
func get_description() -> String:
return "%s / HP:%d / ATK:%.1f / %s" % [
character_name, max_health, attack_power, Job.find_key(job)]
保存したら、FileSystemドックで右クリック →「新規 作成」→「リソース」→ CharacterData を選び、player_data.tres などの名前で保存します。あとは、ノード側で 型として直接 受け取れます。
extends CharacterBody2D
@export var data: CharacterData # インスペクタで.tresをドラッグして割り当てる
func _ready() -> void:
if data:
print(data.get_description()) # データに応じた振る舞い
@export var data: CharacterData のように 型を明示 できるので、インスペクタには CharacterData の .tres しかドロップできず、型安全です。
実践:継承でアイテムデータベースを作る
カスタムリソースの強みが最も出るのが、 アイテムや敵のデータベース です。RPGの装備品、ローグライクの消費アイテム、カードゲームのカード——「共通の土台+種類ごとの違い」を、 継承 できれいに表現できます。

まず全アイテム共通の基底 ItemData を作ります。
# item_data.gd
class_name ItemData
extends Resource
@export var item_id: int = 0
@export var item_name: String = "Unknown Item"
@export_multiline var description: String = ""
@export var icon: Texture2D
@export var stackable: bool = true
武器やポーションには固有のデータが要ります。ItemData を 継承 して、足りない分だけ追加します。
# weapon_data.gd
class_name WeaponData
extends ItemData # ItemDataの全プロパティを引き継ぐ
@export var attack_bonus: float = 5.0 # 武器だけの追加プロパティ
@export var weapon_type: String = "Sword"
こうすると、WeaponData は item_name などの共通項目を 持ったまま、武器固有の項目が増えます。読み込むときは、型で判別できます。
func load_item(path: String) -> ItemData:
var res := ResourceLoader.load(path)
if res is WeaponData:
print("武器: 攻撃+%.1f" % res.attack_bonus)
return res if res is ItemData else null
継承の考え方そのものは OOPデザインパターン の記事も参考になります。 共通は基底に、固有は派生に ——この整理で、アイテムが何種類増えても破綻しません。
duplicate()で実行時の変更を安全にする
1つ注意点があります。 リソースは共有されます 。同じ .tres を10体の敵に割り当てると、10体は 同じデータの実体 を見ています。ここで1体のHPを直接書き換えると——全員のHPが変わってしまいます。

実行時に個体ごとの値を変えたいときは、duplicate() で 複製を作り、その複製を変更 します。
func _ready() -> void:
# 共有の元データではなく、自分専用の複製を持つ
data = data.duplicate()
# これ以降 data をいくら書き換えても、他の個体や元の.tresは無事
data.max_health += 20
「敵ごとにHPを少しばらけさせたい」「拾ったポーションの残量を減らしたい」といった 個体差のある変更 は、必ず duplicate() してから行うのが鉄則です。逆に、全個体で共有したい設定(マスターデータ)は、複製せず共有のまま読むだけにします。
代替手段との使い分け
ゲームデータの持ち方は、カスタムリソース以外にもあります。規模と用途で選びます。
