【Godot4.x】セーブデータの保存方法

このページでは Godot Engine 4.x系で、ゲームデータをセーブ・ロードする方法について説明をします。

セーブデータの保存方法

セーブデータのファイル操作の基礎知識

ファイルの読み込み

ファイルの書き込みは以下の記述で行います。

extends Node2D

func _ready() -> void:
    # ファイルを書き込みモードで開く.
    var f = FileAccess.open("user://savedata.txt", FileAccess.WRITE)
    
    # "test save" という文字を書き込む.
    f.store_string("test save")
    
    # ファイルを閉じる.
    f.close()

FileAccess.open() がファイルを開く関数で、戻り値がファイルオブジェクトとなります。

ここで気になるのがファイルパスの謎の文字です。"user://" というのはそのアプリケーションが使用するフォルダで、環境によって以下のパスに保存されます。

 

  • Windows: %APPDATA%\Godot\[プロジェクト名]
  • macOS: ~/Library/Application Support/Godot/app_userdata/[プロジェクト名]

ただこれらのフォルダを開くのは少々手間なので、Godot には直接開くための機能が用意されています。

メニューから「プロジェクト > ユーザーデータフォルダを開く」を選びます。

するとユーザーデータフォルダが自動的に開いて、先ほどのファイル書き込み処理が正しく行われたことが確認できます。

ファイルの読み込み

保存したファイルを読み込むには以下のように記述します。

extends Node2D

## セーブデータのファイルパス.
const PATH_SAVEDATA = "user://savedata.txt"

func _ready() -> void:
    
    if FileAccess.file_exists(PATH_SAVEDATA) == false:
        print("セーブデータが存在しません: %s"%PATH_SAVEDATA)
        return
        
    var f = FileAccess.open(PATH_SAVEDATA, FileAccess.READ)
    print(f.get_as_text())
    
    # ファイルを閉じる.
    f.close()

読み取りモードで開くだけなのですが、ファイルが存在しない可能性があるため FileAccess.file_exists() でファイルの存在チェックをしてから読み込みを行います。

複雑なデータ構造で保存する

セーブデータに保存する情報はゲームによって異なりますが、RPGSLGを作るときなどはセーブする情報が複雑になります。

そこでセーブするデータの形式を何にするべきかですが、個人的には一般的によく使われている "JSON" 形式 はおすすめしません

というのも Godot Engine には固有のシリアライズ機能が用意されているため、データの互換性として一番安定すると考えているためです。

具体的には var_to_str() / str_to_var() という関数を使用してデータを文字列に変換(または逆変換)します。

extends Node2D

## セーブデータのファイルパス.
const PATH_SAVEDATA = "user://savedata.txt"

func _ready() -> void:
    # セーブ処理を呼び出す
    _save()

## セーブを行う.
func _save() -> void:
    
    # セーブデータを辞書型で定義.
    var savedata = {}

    # キャラデータ.   
    var hero = {} # 勇者のデータ.
    hero["lv"] = 10
    hero["name"] = "hero"
    hero["hp"] = 100
    hero["mp"] = 20
    var magician = {} # 魔法使いのデータ.
    magician["lv"] = 8
    magician["name"] = "magician"
    magician["hp"] = 50
    magician["mp"] = 120
    
    # キャラリスト
    var chara_list = [hero, magician]
    
    savedata["chara_list"] = chara_list
    
    # セーブした座標.
    savedata["pos"] = Vector2i(60, 89)
    
    # ファイルを書き込みモードで開く.
    var f = FileAccess.open(PATH_SAVEDATA, FileAccess.WRITE)
    
    # セーブデータを文字列に変換.
    var s = var_to_str(savedata)
    f.store_string(s)
    
    # ファイルを閉じる.
    f.close()

 

これを実行すると以下のようなテキストデータとして保存されます。

 

savedata.txt
{
"chara_list": [{
"hp": 100,
"lv": 10,
"mp": 20,
"name": "hero"
}, {
"hp": 50,
"lv": 8,
"mp": 120,
"name": "magician"
}],
"pos": Vector2i(60, 89)
}

 

読み込み方法のサンプルは以下のとおりです。

 

extends Node2D

## セーブデータのファイルパス.
const PATH_SAVEDATA = "user://savedata.txt"

func _ready() -> void:
    # ロードを処理を呼び出す.
    _load()

## ロードを行う.
func _load() -> void:
    if FileAccess.file_exists(PATH_SAVEDATA) == false:
        print("セーブデータが存在しません: %s"%PATH_SAVEDATA)
        return
        
    var f = FileAccess.open(PATH_SAVEDATA, FileAccess.READ)
    var s = f.get_as_text()
    # 文字列をセーブデータに変換.
    var savedata = str_to_var(s)
    print("セーブした座標:", savedata["pos"])
    for chara in savedata["chara_list"]:
        print("-------------")
        print("lv: %d"%chara["lv"])
        print("name: %s"%chara["name"])
        print("hp: %s"%chara["hp"])
        print("mp: %s"%chara["mp"])
    
    # ファイルを閉じる.
    f.close()

 

実行結果
セーブした座標:(60, 89)
-------------
lv: 10
name: hero
hp: 100
mp: 20
-------------
lv: 8
name: magician
hp: 50
mp: 120
📌デリアライズ可能なのはGodotネイティブ型のみ

str_to_var() の注意点として、変換可能なデータはGodotネイティブ型のみ (int, String, float, Vector2など)となります。

例えば、classキーワードで定義した独自データ型はデシリアライズできませんので注意が必要です。(シリアライズは可能ですが、デシリアライズ時に辞書型などに変換されます)

参考