気ままなタンス*プログラミングなどのノートブック

プログラミングやRPGツクール、DTM等について、学んだことや備忘録をアウトプットとして残し、情報を必要としている誰かにとって「かゆいところに手が届く」ブログとなることを願いながら記事を書いています。

【RPGツクールMV】[中級者向け]セーブ・ロードで利用されているプログラムの調査記録

スポンサーリンク

回想モードプラグインで、セーブデータ間のスイッチ情報を共有する機能の実装を検討していました。

その中で、セーブ・ロード時に利用するメソッドやゲームオブジェクトの変化といった詳細な情報が必要となり、調査したので記録に残します。

loadFromLocalFile(ローカル実行時のセーブファイル読み込み)

// rpg_managers.js 610行目
StorageManager.loadFromLocalFile = function(savefileId) {
    var data = null;
    var fs = require('fs');
    var filePath = this.localFilePath(savefileId);
    if (fs.existsSync(filePath)) {
        data = fs.readFileSync(filePath, { encoding: 'utf8' });
    }
    return LZString.decompressFromBase64(data);
};
  • this.localFilePathは、以下のような条件でパスを返す
    • savefileIdが0より小さい場合:"config.rpgsave"を返す
    • savefileIdが0と同じ場合:"global.rpgsave"を返す
    • savefileIdが1以上の場合:"file%1.rpgsave".format(savefileId)を返す

loadFromWebStorage(ブラウザ実行時のセーブファイル読み込み)

// rpg_managers.js 639行目
StorageManager.loadFromWebStorage = function(savefileId) {
    var key = this.webStorageKey(savefileId);
    var data = localStorage.getItem(key);
    return LZString.decompressFromBase64(data);
};
  • this.webStorageKeyは以下のような条件でキー名を返す
    • savefileIdが0より小さい場合:"RPG Config"
    • savefileIdが0と同じ場合:"RPG Global"
    • savefileIdが1以上の場合:"RPG File%1".format(savefileId);

saveToLocalFile(ローカル実行時のセーブファイル書き込み)

// rpg_managers.js 599行目
StorageManager.saveToLocalFile = function(savefileId, json) {
    var data = LZString.compressToBase64(json);
    var fs = require('fs');
    var dirPath = this.localFileDirectoryPath();
    var filePath = this.localFilePath(savefileId);
    if (!fs.existsSync(dirPath)) {
        fs.mkdirSync(dirPath);
    }
    fs.writeFileSync(filePath, data);
};
  • 引数で受け取ったjsonをBase64にエンコード

saveToWebStorage(ブラウザ実行時のセーブファイル書き込み)

// rpg_managers.js 633行目
StorageManager.saveToWebStorage = function(savefileId, json) {
    var key = this.webStorageKey(savefileId);
    var data = LZString.compressToBase64(json);
    localStorage.setItem(key, data);
};

ローカルか、ブラウザ実行かの判定

非常にわかりやすいメソッド名ですね。

// rpg_managers.js 595行目
StorageManager.isLocalMode = function() {
    return Utils.isNwjs();
};

セーブ(ローカル、ブラウザ)の判定

// rpg_managers.js 563行目
StorageManager.save = function(savefileId, json) {
    if (this.isLocalMode()) {
        this.saveToLocalFile(savefileId, json);
    } else {
        this.saveToWebStorage(savefileId, json);
    }
};

セーブの呼び出し

json文字列の長さが200,000以上だった場合はワーニングを出力するようですね。ただ、その条件に一致する場合に変数操作は行っておらず、エラー等にはならないようです。

// rpg_managers.js 362行目
DataManager.saveGameWithoutRescue = function(savefileId) {
    var json = JsonEx.stringify(this.makeSaveContents());
    if (json.length >= 200000) {
        console.warn('Save data too big!');
    }
    StorageManager.save(savefileId, json);
    this._lastAccessedId = savefileId;
    var globalInfo = this.loadGlobalInfo() || [];
    globalInfo[savefileId] = this.makeSavefileInfo();
    this.saveGlobalInfo(globalInfo);
    return true;
};
  • this.makeSaveContentsで、ゲーム変数($gameSystem等)をオブジェクトに格納
// rpg_managers.js 422行目
DataManager.makeSaveContents = function() {
    // A save data does not contain $gameTemp, $gameMessage, and $gameTroop.
    var contents = {};
    contents.system       = $gameSystem;
    contents.screen       = $gameScreen;
    contents.timer        = $gameTimer;
    contents.switches     = $gameSwitches;
    contents.variables    = $gameVariables;
    contents.selfSwitches = $gameSelfSwitches;
    contents.actors       = $gameActors;
    contents.party        = $gameParty;
    contents.map          = $gameMap;
    contents.player       = $gamePlayer;
    return contents;
};
  • ゲーム変数を格納したオブジェクトをjsonに変換し、563行目のsaveメソッドに渡す
  • makeSavefileInfoメソッドで、セーブ画面に表示するキャラクターやプレイ時間等の情報をオブジェクトにセットする
// game_managers.js 411行目
DataManager.makeSavefileInfo = function() {
    var info = {};
    info.globalId   = this._globalId;
    info.title      = $dataSystem.gameTitle;
    info.characters = $gameParty.charactersForSavefile();
    info.faces      = $gameParty.facesForSavefile();
    info.playtime   = $gameSystem.playtimeText();
    info.timestamp  = Date.now();
    return info;
};

ロード(ローカル、ブラウザ)の判定

StorageManager.load = function(savefileId) {
    if (this.isLocalMode()) {
        return this.loadFromLocalFile(savefileId);
    } else {
        return this.loadFromWebStorage(savefileId);
    }
};

ロードの呼び出し

DataManager.loadGameWithoutRescue = function(savefileId) {
    var globalInfo = this.loadGlobalInfo();
    if (this.isThisGameFile(savefileId)) {
        var json = StorageManager.load(savefileId);
        this.createGameObjects();
        this.extractSaveContents(JsonEx.parse(json));
        this._lastAccessedId = savefileId;
        return true;
    } else {
        return false;
    }
};
  • StorageManager.loadで、セーブファイルから文字列(Base64からデコード済み)を取得
  • this.createGameObjectsで、Game_XXXXオブジェクトを生成
// game_managers.js 194行目
DataManager.createGameObjects = function() {
    $gameTemp          = new Game_Temp();
    $gameSystem        = new Game_System();
    $gameScreen        = new Game_Screen();
    $gameTimer         = new Game_Timer();
    $gameMessage       = new Game_Message();
    $gameSwitches      = new Game_Switches();
    $gameVariables     = new Game_Variables();
    $gameSelfSwitches  = new Game_SelfSwitches();
    $gameActors        = new Game_Actors();
    $gameParty         = new Game_Party();
    $gameTroop         = new Game_Troop();
    $gameMap           = new Game_Map();
    $gamePlayer        = new Game_Player();
};
  • セーブファイルの文字列を、jsonパースし、ゲーム変数($gameSystem等)に格納
// game_managers.js 438行目
DataManager.extractSaveContents = function(contents) {
    $gameSystem        = contents.system;
    $gameScreen        = contents.screen;
    $gameTimer         = contents.timer;
    $gameSwitches      = contents.switches;
    $gameVariables     = contents.variables;
    $gameSelfSwitches  = contents.selfSwitches;
    $gameActors        = contents.actors;
    $gameParty         = contents.party;
    $gameMap           = contents.map;
    $gamePlayer        = contents.player;
};

それでは、皆様も楽しいツクールライフを!