ゲーム開発において、プレイヤーの進行状況や設定を永続的に保存し、次回起動時に復元できる セーブ/ロードシステム は、ゲーム体験の根幹をなす重要な要素です。
Godot Engineでは、データを永続化するためのいくつかの強力な組み込み機能が提供されています。本記事では、特に初心者から中級者の開発者に向けて、最も一般的に使用される3つの方法、すなわち JSON、ConfigFile、そして カスタムリソース(Custom Resource) を徹底的に比較し、それぞれの特徴と具体的な実装方法を解説します。
データの永続化:なぜこのトピックが重要なのか
Godot Engineは、Vector2やColorといった独自のデータ型を多用します。これらのGodot固有のデータ型を、ファイルシステム上で安全かつ効率的に保存・復元する仕組み(シリアライズ と デシリアライズ)を理解することが、堅牢なセーブシステム構築の鍵となります。
1. ConfigFile:設定ファイルに最適なシンプルな構造
ConfigFileクラスは、WindowsのINIファイルやUnixの設定ファイルに似た、セクションとキー・値のペアでデータを管理する形式です。
ConfigFileのメリット・デメリット
| メリット | デメリット |
|---|---|
| シンプルさ | 複雑な階層構造や配列の保存には不向き |
| 人間が読みやすい | 大量のゲームデータには非効率 |
| Godotネイティブ |
ConfigFileを使った実装例
extends Node
const SAVE_PATH = "user://settings.cfg"
func save_settings(volume: float, fullscreen: bool) -> void:
var config = ConfigFile.new()
config.set_value("audio", "master_volume", volume)
config.set_value("video", "fullscreen", fullscreen)
var error = config.save(SAVE_PATH)
if error != OK:
print("設定の保存に失敗しました: ", error)
func load_settings() -> Dictionary:
var config = ConfigFile.new()
var error = config.load(SAVE_PATH)
if error != OK:
return {"volume": 1.0, "fullscreen": false}
var volume = config.get_value("audio", "master_volume", 1.0)
var fullscreen = config.get_value("video", "fullscreen", false)
return {"volume": volume, "fullscreen": fullscreen}
2. JSON:汎用性と複雑なデータ構造への対応
JSON(JavaScript Object Notation)は、Webの世界で広く使われている軽量なデータ交換フォーマットです。
JSONのメリット・デメリット
| メリット | デメリット |
|---|---|
| 汎用性 | Godot固有の型を直接保存できない |
| 複雑な構造 | 読み書きに手動変換が必要 |
| デバッグの容易さ |
JSONを使った実装例
extends Node
const SAVE_PATH = "user://game_save.json"
func save_game_data(player_position: Vector2, inventory: Array) -> void:
var save_data = {
"player_pos": [player_position.x, player_position.y],
"inventory": inventory,
"timestamp": Time.get_unix_time_from_system()
}
var json_string = JSON.stringify(save_data, "\t")
var file = FileAccess.open(SAVE_PATH, FileAccess.WRITE)
if file:
file.store_string(json_string)
file.close()
func load_game_data() -> Dictionary:
if not FileAccess.file_exists(SAVE_PATH):
return {}
var file = FileAccess.open(SAVE_PATH, FileAccess.READ)
if file:
var json_string = file.get_as_text()
file.close()
var parse_result = JSON.parse_string(json_string)
if parse_result is Dictionary:
var loaded_data = parse_result
var pos_array = loaded_data.get("player_pos", [0.0, 0.0])
loaded_data["player_pos"] = Vector2(pos_array[0], pos_array[1])
return loaded_data
return {}
3. カスタムリソース(Custom Resource):Godotネイティブなセーブの決定版
Godot Engineにおいて、最も推奨され、強力なセーブ方法が カスタムリソース の利用です。
カスタムリソースのメリット・デメリット
| メリット | デメリット |
|---|---|
| 型安全性 | 外部からの改ざんリスクがある |
| Godot固有型を直接サポート | 外部アプリケーションとの連携に不向き |
| 少ないコード量 |
カスタムリソースを使った実装例
# SaveGame.gd
class_name SaveGame
extends Resource
@export var coins := 0
@export var player_global_position := Vector2(0, 0)
@export var unlocked_levels := []
# SaveManager.gd
extends Node
const SAVE_PATH = "user://game_save.tres"
var current_save: SaveGame = null
func load_game() -> void:
if ResourceLoader.exists(SAVE_PATH):
current_save = ResourceLoader.load(SAVE_PATH, "", ResourceLoader.CACHE_MODE_IGNORE)
else:
current_save = SaveGame.new()
func save_game() -> void:
var error = ResourceSaver.save(current_save, SAVE_PATH)
if error != OK:
print("セーブに失敗しました: ", error)
徹底比較:どの方法を選ぶべきか
| 特徴 | ConfigFile | JSON | カスタムリソース |
|---|---|---|---|
| 主な用途 | 設定ファイル | 複雑なデータ、外部連携 | ゲームのセーブデータ全体 |
| データ構造 | シンプルなキー/値 | 複雑な階層、配列、辞書 | 複雑な階層、Godot固有型 |
| Godot固有型 | サポート | 非サポート(手動変換必要) | 完全サポート |
| コード量 | 少ない | 多い(変換処理が必要) | 最も少ない |
実践的な活用:使い分けの指針
- ConfigFile: ゲーム設定(音量、グラフィック設定、キーバインド)に使用
- JSON: 外部ツールとのデータ連携、大量の静的データ(アイテムリスト)に使用
- カスタムリソース: ゲームのセーブデータ(プレイヤーの状態、インベントリ、マップ情報など)に最も推奨
まとめ
Godot Engineでセーブ/ロードシステムを実装する際、ConfigFile は設定ファイルに、JSON は汎用的なデータ交換に、そして カスタムリソース はゲームのセーブデータ本体に、それぞれ最適な選択肢となります。
特にカスタムリソースは、Godotの設計思想に最も合致しており、@export変数とResourceSaver/ResourceLoaderを使うだけで、複 雑なゲームデータを驚くほど簡単に永続化できます。