アクターに新規メンバーを追加しつつ、セーブデータを正しく読めるようにする

プラグインを追加すると「新規セーブデータでは動作するが、既存のセーブデータでは落ちる」という問題が発生することがあります。
原因ですが、アクターがnewで生成された時のみメンバ変数が追加され、既存セーブデータの読み込み時に追加処理を忘れると発生します。
実行中に該当変数が存在しない状態(undefined)になり、参照しようとしたところで落ちます。
いわゆる「ぬるぽ」です。

もし追加する変数が2個以上であれば、それらの変数をすべて含んだ新規のクラスを作成して、それをメンバに持たせることで対応しましょう。
if(!this._新規変数){this._新規変数=...}という処理を大量に作るのは手間ですが、自分のクラスに持たせれば手間は1回で済みます。
また、メンテナンス性も大幅に向上します。

以下は、セーブデータの新規作成時のデータ作成・読み込み時にデータがない場合の追加作成を行うスクリプトです。
「ActorNewMember」という記述が2連続になっている場所があって気持ち悪いですが、これはコードスニペットを使ったためです（後述）。

window[ ActorNewMember.name]=ActorNewMemberについては、[RPGツクールMVでclass記法によって書いたクラスをセーブデータに含める方法](https://qiita.com/Sigureya/items/405c32c6f2780a1fee95)を参照。

```javascript
:ActorNewMember.js

class ActorNewMember{
    constructer(){

    }
}
//クラスを復元できるように、クラス名を登録
window[ ActorNewMember.name]=ActorNewMember;
/**
* @param {Game_Actor} actor
* @returns {ActorNewMember}
*/
function getnewMemberActorNewMember(actor){
    return actor._mActorNewMember;
}
//新規作成・追加作成時の共通となる初期化処理
Game_Actor.prototype.newMemberActorNewMember =function(){
    if(!this._mActorNewMember){
        //データが配列の場合、newで生成しているクラスの代わりに配列を入れる。各自で対応する事
        this._mActorNewMember = new  ActorNewMember();
    }
};
const Game_Actor_initMembers =Game_Actor.prototype.initMembers
Game_Actor.prototype.initMembers =function(){
    Game_Actor_initMembers.call(this);
    //new時に固有システムの初期化を呼び出す
    this.newMemberActorNewMember();
}
//全アクターに対して初期化処理を実行する
function initAllActor(){ 
    for(const actor of $gameActors._data){ 
        if(actor){ actor.newMemberActorNewMember();}
    }
    }
    //セーブデータ読み込み時は、ここから追加作成処理を呼び出す
const DataManager_extractSaveContents=DataManager.extractSaveContents;
DataManager.extractSaveContents =function(contents){
    DataManager_extractSaveContents.call(this,contents);
    initAllActor();
};
```

コードスニペットも用意しておきます。
Visual Studio Codeを使っているなら、以下の内容を利用することで簡単に上記の内容を作成できます。
(snpActornewMemberと入れることで、テンプレートを参照してコードが自動生成される)
その際、プラグインのファイル名を参照して関数名が他と重複しないように作成されます。
元々はアクターに新たなシステムを追加するための無いようなので、シーン部分の自動生成も含まれています。
使わない場合まとめて削除してください。

```json
	"actorNewMember":{
		"prefix": "snpActorNewMember",
		"description": "アクターに新しいメンバ変数を追加します。",
		"body": [
			"class ${TM_FILENAME_BASE}_Item{",
			"    constructer(){",
			"    }",
			"}",
			"class ${TM_FILENAME_BASE}{",
			"    constructer(){",
			"    ",
			"       /**",
			"        * @type ${TM_FILENAME_BASE}_Item[]",
			"        */",
		 	"        this._list =[];",
			"    }",
			"    /**",
			"     * @param {Number} index",
			"     */",
			"    item(index){",
			"        return this._list[index]",
			"    }",
			"    maxItems(){",
			"        return this._list.length;",
			"    }",
			"}",

			"/**",
			"* @param {Game_Actor} actor",
			"* @returns ${TM_FILENAME_BASE}",
     		"*/",
			"function getnewMember${TM_FILENAME_BASE}(actor){",
			"    return actor._m$TM_FILENAME_BASE;",
			"}",
			"Game_Actor.prototype.newMember${TM_FILENAME_BASE} =function(){",
			"   if(!this._m${TM_FILENAME_BASE}){",
			"       this._m${TM_FILENAME_BASE} = new  ${TM_FILENAME_BASE}();",
			"   }",
			"};",
			"const Game_Actor_initMembers =Game_Actor.prototype.initMembers",
			"Game_Actor.prototype.initMembers =function(){",
			"    Game_Actor_initMembers.call(this);",
			"    this.newMember${TM_FILENAME_BASE}();",
			"}",
			"function initAllActor(){ ",
			"    for(const actor of $$gameActors._data){ ",
			"       if(actor){ actor.newMember${TM_FILENAME_BASE}();}",
			"    }",
			" }",
			"const DataManager_extractSaveContents=DataManager.extractSaveContents;",
			"DataManager.extractSaveContents =function(contents){",
			"    DataManager_extractSaveContents.call(this,contents);",
			"    initAllActor();",
			"};",
			"class Window_${TM_FILENAME_BASE} extends Window_Selectable{",
			"    actor(){",
			"        return $$gameParty.menuActor();",
			"    }",
			"    xxObject(){",
			"        return getnewMember${TM_FILENAME_BASE}(this.actor());",
			"    }",
			"    /**",
			"     * @param {Number} index",
			"     */",
			"    item(index){",
			"        return this.xxObject().item(index)",
			"    }",
			"    maxItems(){",
			"        return this.xxObject().maxItems();",
			"    }",
			"    /**",
			"     * @param ${TM_FILENAME_BASE}_Item item",
			"     */",
			"    isItemEnabled(item){",
			"        return true;",
			"    }",
			"    isCurrentItemEnabled(){",
			"        return this.isItemEnabled(this.item(this._index));",
			"    }",
			"}",
			"const MySystemSymbol ='${TM_FILENAME_BASE}';",
			"const Scene_Menu_onPersonalOk=Scene_Menu.prototype.onPersonalOk",
			"Scene_Menu.prototype.onPersonalOk =function(){",
			"    if(MySystemSymbol=== this._commandWindow.currentSymbol()){",
			"        return;",
			"    }",
			"    Scene_Menu_onPersonalOk.call(this);",
			"}",
			"const Scene_Menu_createCommandWindow=Scene_Menu.prototype.createCommandWindow;",
			"Scene_Menu.prototype.createCommandWindow=function(){",
			"    Scene_Menu_createCommandWindow.call(this);",
			"    this._commandWindow.setHandler(MySystemSymbol,this.commandPersonal.bind(this));",
			"};",
			"const Window_MenuCommand_addOriginalCommands= Window_MenuCommand.prototype.addOriginalCommands;",
			"Window_MenuCommand.prototype.addOriginalCommands =function(){",
			"    Window_MenuCommand_addOriginalCommands.call(this);",
			"};"

		],

	}

```


本ページのソースコード・コードスニペットは著作権を行使しないので、ご自由にコピー・再配布してください。