【Godot】カスタムリソース(Custom Resource)の作成と活用 - Godot Engineで実現するデータ駆動設計

作成: 2025-12-08最終更新: 2025-12-16

Godot Engineのカスタムリソースを使ったデータ駆動設計の実践方法。パフォーマンスの最適化、よくある間違いとベストプラクティスまで解説します。

なぜカスタムリソースが重要なのか:データ駆動設計への第一歩

Godot Engineでゲーム開発を進める際、ゲーム内のデータ管理は避けて通れない課題です。キャラクターのステータス、アイテムの定義、敵のAI設定など、多くのデータがゲームの挙動を決定します。これらのデータをノードやシーンに直接記述してしまうと、データの再利用が難しくなり、プロジェクトの規模が大きくなるにつれて管理が複雑化し、変更が困難になります。

ここで カスタムリソース(Custom Resource) の出番です。カスタムリソースは、Godotの強力な リソース(Resource) システムを拡張し、独自のデータ型を作成・保存するための仕組みです。UnityにおけるScriptableObjectに近い概念であり、ゲームロジックからデータを分離し、データ駆動設計(Data-Driven Design) を実現するための鍵となります。


カスタムリソースの基本概念

Godotにおけるリソースとは、ディスクに保存できるデータコンテナです。シーン、スクリプト、テクスチャ、オーディオなど、プロジェクト内のほとんどの非ノードデータはリソースとして扱われます。カスタムリソースは、このResourceクラスを継承したスクリプトを作成することで定義されます。

1. カスタムリソースの定義

カスタムリソースは、Resourceクラスを継承したGDScriptファイルとして作成します。

# res://resources/character_data.gd
# class_nameでグローバルな型として登録する
class_name CharacterData extends Resource

# @exportでインスペクタに表示・保存するプロパティを定義
@export var character_name: String = "New Character"
@export var max_health: int = 100
@export var attack_power: float = 15.0

# Enumを使って職業を定義すると、インスペクタで選択可能になる
enum Job { WARRIOR, MAGE, ROGUE }
@export var job: Job = Job.WARRIOR

@export var skills: Array[String] = []

# データに紐づくロジックをメソッドとして実装することも可能
func get_description() -> String:
    # find_key()でenum値から対応する名前文字列を取得(より明示的)
    return "Name: %s, HP: %d, ATK: %.1f, Job: %s" % [character_name, max_health, attack_power, Job.find_key(job)]

class_nameキーワードは、このスクリプトをCharacterDataという名前のグローバルな型として登録します。

2. リソースファイルの作成と利用

スクリプトを保存したら、ファイルシステムドックで右クリックし、「新規作成」 -> 「リソース」を選択します。検索ボックスにCharacterDataと入力し、作成したカスタムリソース型を選び、player_data.tresのような名前で保存します。

このリソースをゲーム内で使うには、ノードのスクリプトで変数をエクスポートします。

# player.gd
extends CharacterBody2D

# 型として直接CharacterDataを指定できる
@export var data: CharacterData

func _ready():
    if data:
        print("プレイヤー情報をロードしました。")
        print(data.get_description())
    else:
        print("プレイヤーデータが設定されていません。")

実践的な活用例:アイテムデータベースの構築

カスタムリソースの最も強力な活用法の一つは、ゲーム内のアイテムやエンティティのデータベース構築です。

アイテムリソースの定義

# res://resources/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 = "A mysterious item."
@export var icon_texture: Texture2D
@export var stackable: bool = true
@export var max_stack: int = 99

派生リソースの作成

特定の種類のアイテム(例: 武器、ポーション)には、さらに固有のプロパティが必要になる場合があります。その場合、ItemDataを継承したカスタムリソースを作成します。

# res://resources/weapon_data.gd
class_name WeaponData extends ItemData

@export var attack_bonus: float = 5.0
@export var weapon_type: String = "Sword"
@export var two_handed: bool = false

ゲーム内での利用

extends Node

@export var equipped_weapon: WeaponData

func _ready():
    if equipped_weapon:
        print("装備中の武器: %s" % equipped_weapon.item_name)
        print("攻撃力ボーナス: %s" % equipped_weapon.attack_bonus)

func load_item_data(path: String) -> ItemData:
    var item_resource = ResourceLoader.load(path)
    if item_resource is ItemData:
        return item_resource
    return null

よくある間違いとベストプラクティス

よくある間違いベストプラクティス
共有リソースを直接変更する実行時にデータを変更する場合は、resource.duplicate()で複製を作成し、そのインスタンスを変更する。これにより、他の場所で使われている共有データへの影響を防ぎます。
_init()でプロパティを初期化するカスタムリソースのプロパティは、@export var health: int = 100のように、宣言と同時にデフォルト値を設定するのが基本です。_init()での初期化は、インスペクタの値で上書きされるため、意図通りに動作しません。
巨大なリソースを一つにまとめるアイテム図鑑など、大量のデータを一つのリソースに詰め込むと、ロード時間やメモリ使用量が増大します。データごとに個別のリソースファイルを作成し、必要なものだけをResourceLoader.load()で動的に読み込む設計を検討しましょう。
ファイルパスを文字列でハードコードする@export var item: Resourceのようにリソース型でエクスポートし、インスペクタから設定することで、パスの変更に強い構造になります。
リソースの変更を検知できないリソース内のデータが変更されたことを他のオブジェクトに通知したい場合、リソース内にシグナルを定義します。

パフォーマンスと代替パターンとの比較

パフォーマンスに関する注意点

  • .tres vs .res: .tresはテキスト形式で、人間が読めてバージョン管理に適しています。一方、.resはバイナリ形式で、ロードが高速かつファイルサイズが小さいですが、内容は直接編集できません。開発中は.tresを使い、リリース時に.resに変換するのが一般的です。
  • 遅延読み込み: ゲーム開始時にすべてのリソースを読み込むと、起動時間が長くなる可能性があります。ResourceLoader.load_threaded_request()を使って非同期に読み込み、ゲームの応答性を保つことが重要です。

代替パターンとの比較

データ管理方法メリットデメリット適した用途
カスタムリソースエディタとの統合が強力。型安全。再利用性が高い。Godotエンジンへの依存。キャラクター、アイテム、スキルなど、構造化されたゲームデータの管理。
JSONファイル人間が読み書きしやすい。言語やエンジンに依存しない。型情報がない。パース処理が別途必要。設定ファイル、外部ツールとのデータ連携。
CSVファイル表計算ソフトで簡単に編集できる。複雑なデータ構造には不向き。大量の単純なレコード(例:セリフ集)。
SQLiteデータベース大量のデータを効率的にクエリ・操作できる。セットアップがやや複雑。アドオンが必要。数千を超えるアイテムやユーザーデータ。

カスタムリソースのメリットとデータ駆動設計

メリット説明データ駆動設計との関連
データの分離ゲームロジック(GDScript)とデータ(.tresファイル)が完全に分離されるため、コードの可読性と保守性が向上します。データがコードから独立し、変更が容易になる。
再利用性一度作成したリソースファイルは、複数のシーンやノードで共有・再利用できます。データの定義が一箇所に集約される。
エディタ統合@exportプロパティはGodotのインスペクタに表示され、プログラミング知識がなくてもデザイナーやプランナーがデータを直感的に編集できます。開発チーム全体がデータにアクセスしやすくなる。
インスタンス化リソースはノードとは異なり、シーンツリーに属しません。Resource.duplicate()メソッドを使えば、簡単にデータのコピーを作成できます。実行時にデータを動的に生成・変更する基盤となる。

まとめ

Godot Engineのカスタムリソースは、単なるデータ保存の仕組みではなく、プロジェクトの設計思想を データ駆動型 へと進化させるための強力なツールです。

初心者の方は、まずキャラクターの基本ステータスやシンプルなアイテム定義からカスタムリソースの導入を試みてください。これにより、ゲームのデータ管理が劇的に改善され、より大規模で複雑なプロジェクトにも対応できるスケーラブルな設計基盤を築くことができます。