RPGツクールMZでイベントコマンド「スクリプト」からプラグインコマンドを呼び出す方法

**副題「あるいは、RPGツクールMZのプラグインコマンド周りをざっと調べてみた」**

【目次】
  
- 前置き
- 結論
- 読み解き
 - MVのプラグインコマンド振り返り
 - MZでの変更部分とそれへの対応
 - MVとMZで、スクリプトからのプラグインコマンド呼び出し方法が異なる理由

##前置き
自身はあまり実感したことがないのですが、MVの頃から
「プラグインコマンドをスクリプトから呼び出したい」
という需要がぼちぼちあるようです。
また、MZの売りの一つが
[「プラグイン周りの機能強化 ＞ プラグインコマンドの刷新」](https://tkool.jp/mz/new_function/usability.html)
なわけですが、その辺を全然さわれてなかったので、学習がてら調査と整理をしてみました。


##結論
まず結論。
MZでは以下の記述でイベントコマンド「スクリプト」からプラグインコマンドを呼び出せます。

```JavaScript:
const args = {/* ここにプラグインコマンドのパラメータを記述 */};
PluginManager.callCommand(this,（プラグイン名） , （コマンド名）, args);
```

argsという**オブジェクト**にプラグインコマンドのパラメータをプロパティとして記述し、それをPluginManagerのcallCommandメソッドにプラグイン名、コマンド名と共に渡します。

例）
プラグイン名「test.js」のコマンド名「Test」をイベントコマンドスクリプトから呼び出す例です。
「test.js」は与えられた引数をconsole.logに出力するだけのプラグインであり、
パラメータ名は「testKey」としています。
![img1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107226/59eb77ea-91db-a991-edf4-88d8cf1a615c.png)![img2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107226/5a014d27-193e-e089-7ac4-1e945a74db02.png)
きちんと呼び出しできてますね。

方法だけ知りたい、という人はここまでです。
上のスクリプトをコピペしてこのページはそっ閉じしましょう。


##読み解き
上記が何をしているのか、そしてMVからどう変わったのかを自身の備忘録も兼ねて以下に記します。  
なお、ざっくりとしたイメージを掴むことを最優先とし、「Game_Interpreter」や
「PluginManager」の細かい挙動や仕組みは省略している点をご了承ください。

###MVのプラグインコマンド振り返り
まずはMVの振り返りです。
MVにおいてイベントコマンド「スクリプト」からのプラグインコマンド呼び出しは、
いくつか方法はありますが、以下のコードがシンプルです。

```JavaScript:
var args = [/* ここにプラグインコマンドのパラメータを記述 */];
this.pluginCommand(command, args);
```
※argsは**配列**

イベントコマンド全般（「スクリプト」「プラグインコマンド」含む）は「Game_Interpreter」というクラスによって呼び出しが行われます。
たとえば「文章の表示」ならば「Game_Interpreter.prototype.command101」
「選択肢の表示」ならば「Game_Interpreter.prototype.command102」というように、
「command ＋ コマンド番号」の形でメソッド名が与えられています。
（※参考：トリアコンタンさま [RPGツクールMV プラグインコマンド集　リファレンス](https://docs.google.com/spreadsheets/d/1rOIzDuhLC6IqJPEFciYOmXWL_O7X9-hMValMs7DpWCk/edit#gid=1266374350)）

そして今回のお題であるプラグインコマンドについては「356番」が与えられています。
以下がMVコアスクリプトの「command356」のコードです。

```JavaScript:rpg_objects.js
// Plugin Command
Game_Interpreter.prototype.command356 = function() {
    var args = this._params[0].split(" ");
    var command = args.shift();
    this.pluginCommand(command, args);
    return true;
};
```
詳細は端折りますが、command356が呼び出されると、コマンド名とコマンドパラメータを取得し、
それを「this.pluginCommand」に引数として渡して処理を投げます。
この「this.pluginCommand」が核心部分です。

では、次に処理を投げられた「pluginCommand」について見てみましょう。

```JavaScript:rpg_objects.js
Game_Interpreter.prototype.pluginCommand = function(command, args) {
    // to be overridden by plugins
};
```

**おおっとぉ、中身が空だ！！**
コメントを直訳すると「プラグインでオーバーライドされます」ですね。
そう、MVにおいては**コマンドのチェックと実行開始はプラグイン側に委ねられていたのです。**

一般的には以下のような要領で、各自でプラグインコマンドのコマンド名と引数を取得し、
switchで自身のコマンド名に該当するかを分岐し、実行を行っていました。

```JavaScript:プラグイン側の記述
const _game_interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args) {
    _game_interpreter_pluginCommand.call(this, command, args);
    switch (command) {
        case 'CommandName1':
            /* CommandName1の処理 */
            break;
        case 'CommandName2':
            /* CommandName2の処理 */
            break;
       /* 以下、プラグインコマンドの分だけ分岐 */
    };
};
```
簡単に流れを追うと、まずは他のプラグインと処理が競合しないようにエイリアスとしてそれまでの「pluginCommand」の定義を保存し、
改めて「pluginCommand」をオーバーライドしています。
その中で先ほどエイリアスを付けて保存しておいた関数を呼び出して他のプラグイン側の処理を実行、
続けてswitchにより自身のプラグインのコマンドをチェックし、該当するならばそれぞれの処理を行う、という流れです。
繰り返しとなりますが、**MVではプラグインコマンドのチェックと実行開始はプラグイン側に委ねられていたのです。**

【MVのプラグインコマンドのフローを整理】
プラグインコマンドが実行されると
　＞「Game_Interpreter.command356」が呼ばれる
　　＞「Game_Interpreter.command356」から「Game_Interpreter.pluginCommand」が呼ばれる
　　　＞ただし「Game_Interpreter.pluginCommand」は各自プラグイン側で記述

###MZでの変更部分とそれへの対応
MZでは、公式サイトでもアピールされているように、プラグインコマンド周りが刷新されました。
エディター上での入力のし易さ、プラグイン実行側でのデータの取り扱い易さ、いずれも大きく向上しています。

MZでは、「PluginManager」という静的クラスの「registerCommand」メソッドが**プラグインコマンドとその実行内容を事前に受け取り、「PluginManager」内に保持してくれる**ようになっています。

```JavaScript:rmmz_objects.js
PluginManager.registerCommand = function(pluginName, commandName, func) {
    const key = pluginName + ":" + commandName;
    this._commands[key] = func;
};
```
「PluginManager.registerCommand」は第一引数にプラグイン名、第二引数にコマンド名、そして第三引数に「コマンド実行時に実行したい関数」を受け取ります。
するとPluginManagerの「_commands」プロパティに「プラグイン名とコマンド名を連結した物」をkeyとして、第三引数の関数が保存されます。

このメソッドをプラグイン側で呼び出してコマンド名や実行関数を登録しておけば、後はプラグインコマンドが実行された時点で保存されていた関数が呼び出されます。
（※「PluginManager.registerCommand」の記述例は公式の「[プラグイン講座 ＞ プラグインを作ってみる](https://tkool.jp/mz/plugin/make/koushiki.html)」内の「プラグインコマンドの定義」に詳しく解説されていますので、そちらも合わせて読んでおきましょう。）

次にプラグインコマンドが実行された際の流れを見ていきます。
プラグインコマンド実行の基本的な仕組みはMZもMVと同様で、Game_Interpreterクラスの「command ＋ コマンド番号」メソッドで実行されます。
（※参考：トリアコンタンさま [RPGツクールMZ スクリプトリファレンス](https://docs.google.com/spreadsheets/d/1aqY-xzFqT0vnZE-OkfsMYsP9Ud91vWTrBLU-uDkJ-Ls/edit#gid=270496334)）

ただし、MZではプラグインコマンド実行時は「Game_Interpreter.**command357**」が呼び出されます。
ここポイント。**「356」でなく「357」**です。

```JavaScript:rmmz_objects.js
Game_Interpreter.prototype.command357 = function(params) {
    PluginManager.callCommand(this, params[0], params[1], params[3]);
    return true;
};
```
これはほぼ、「PluginManager.callCommand」を呼び出すだけのメソッドです。
この「PluginManager.callCommand」が核心部分であり、冒頭の結論です。
MVでいう「Game_Interpreter.pluginCommand」に相当すると言って差し支えないでしょう。

```JavaScript:rmmz_managers.js
PluginManager.callCommand = function(self, pluginName, commandName, args) {
    const key = pluginName + ":" + commandName;
    const func = this._commands[key];
    if (typeof func === "function") {
        func.bind(self)(args);
    }
};
```
先ほどの「PluginManager.registerCommand」と対になる処理ですね。
「PluginManager.registerCommand」で受け取っていた関数を、今度は呼び出しています。

つまり、**MVだとプラグイン側で記述していたコマンドのチェックや実行開始の処理は、MZだとコアスクリプトで初めから用意されており、プラグイン側の記述は不要です。素晴らしい。**
（※注：アノテーション除く）

【MZのプラグインコマンドのフローを整理】
プラグインコマンドが実行されると
　＞「Game_Interpreter.**command357**」が呼ばれる
　　＞「Game_Interpreter.command357」から「PluginManager.callCommand」が呼ばれる

###MVとMZで、スクリプトからのプラグインコマンド呼び出し方法が異なる理由
なお、MVでプラグインコマンドを受け取っていた「command356」は、MVとの互換性維持のためかMZにもそのまま残してあります。

```JavaScript:rmmz_objects.js
// Plugin Command MV (deprecated)
Game_Interpreter.prototype.command356 = function(params) {
    const args = params[0].split(" ");
    const command = args.shift();
    this.pluginCommand(command, args);
    return true;
};

Game_Interpreter.prototype.pluginCommand = function() {
    // deprecated
};
```
ただし、コメントにもあるように**「deprecated（非推奨）」**となっていますし、実際にMZでイベントコマンド「スクリプト」から「pluginCommand」を呼び出しても何も起こりません。

何故か？

それは、何度も繰り返してきましたが**プラグインコマンドのチェックと実行を担う「pluginCommand」は空だからです。**

**MZにおいて、イベントコマンド「スクリプト」から「this.pluginCommand」を呼び出したとしても、プラグイン側がpluginCommandメソッドの中身を記述していなければ、空の関数が呼び出されただけで何も実行されずに終わります**

**言い換えると、プラグインコマンドを使用するプラグインをMVとMZの両対応とするには
MZ向けの「PluginManager.registerCommand」による記述と、
MV向けの「Game_Interpreter.pluginCommand」による記述の
両方に備えておく必要がある、
ということです。**

プラグインコマンドを使用するプラグインをMV、MZ両対応にするのは若干メンドーですね。
（その辺を共用化するプラグインを作れば需要があるかな？）

----
以上、イベントコマンド「スクリプト」からプラグインコマンドを呼び出す、という観点を取っ掛かりとして、
MZのプラグインコマンド処理を追ってみました。

**忌憚ないツッコミ待ちだぁ！ щ(ﾟДﾟщ) ﾊﾞｯﾁｺｰｲ**