📚

Godotのゲームにセーブ機能を実装する

2024/06/18に公開

既存の方法

Godotにおいて、セーブ・ロードを実装する方法は様々あります。

公式チュートリアルでは、Saveメソッドを各ノード(のスクリプト)で実装し、セーブ時に呼び出す方法が紹介されています。

既存の方法も十分よく動作しますが、もっと直感的で、見逃しなくデータを保存するような方法が無いか模索しました。

結論が出たとは言いませんが、一つのアイデアとして土台になりうるのではないかと思います。

今後、ブラッシュアップする度に記事を更新していこうと思います。

SaveDataクラスを作成する方法

クラスを作成する

下のクラスを定義します。

class_name SaveData

var _key: String
var _data_dict: Dictionary
var _signal_dict: Dictionary
signal set_signal
var v:
	get:
		if not (_key in _data_dict and is_instance_valid(_data_dict[_key])):
			_signal_dict[_key] = set_signal
			await set_signal
		return _data_dict[_key]
	set(value):
		_data_dict[_key] = value
		if _key in _signal_dict:
			_signal_dict[_key].emit()

func _init(key: String, dict:={}, signal_dict:={}):
	_key = key
	_data_dict = dict
	_signal_dict = signal_dict

func new_child(key: String) -> SaveData:
	var child = SaveData.new(_key + "/" + key, _data_dict, _signal_dict)
	return child

func save(path: String):
	var f = FileAccess.open(path, FileAccess.WRITE)
	f.store_string(var_to_str(_data_dict))
	f.close()

func load(path: String):
	var f = FileAccess.open(path, FileAccess.READ)
	_data_dict = str_to_var(f.get_as_text())
	f.close()

awaitを使って、nullや開放された値以外が代入されるまで待たせることができます。

使用例

GlobalシーンはAutoLoadに設定してあります。
GlobalシーンにアタッチしてあるGlobal.gdの中で、var sd = SaveData.new_child("root")を実行してあります。

var player :SaveData = Global.sd.new_child("player")
var speed := player.new_child("speed")
var gravity := player.new_child("gravity")
var HP := player.new_child("HP")
var velocity := Vector2()

func _process(delta):
	if Input.is_action_pressed("ui_right"):
		velocity.x = speed.v
	elif Input.is_action_pressed("ui_left"):
		velocity.x = -speed.v
	else:
		velocity.x = 0
	velocity.y += gravity.v * delta
	position += velocity * delta

func _hit(damage):
	HP.v -= damage

特徴

  • 変数の定義時にSaveData.new_child(key: String)の返り値を代入し
    変数の値の取得時に".v"をつける以外は、普通の変数のように振る舞います。
  • SaveDataクラスに階層構造を持たせて管理できます。

メリット・デメリット

メリット

  • 普通の変数に代入するように、セーブデータ用の辞書を更新することができます。

デメリット

  • 自分で作成したクラスなど、テキスト化に特別な操作が必要な値を保存できません。
  • 静的型付けが出来ません。

あとがき

自分用のメモとして、新たな発見がある度に更新していきます。

Discussion