📚
Godotのゲームにセーブ機能を実装する
既存の方法
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