既存の関数に処理を追加する

ツクールプラグインでよくやるのが、コアスクリプトの関数に処理を追加することです。

例えば、次の関数があります。Sprite_Clickableのアップデート処理です。

Sprite_Clickable.prototype.update = function() {
    Sprite.prototype.update.call(this);
    this.processTouch();
};

この処理に独自の内容を追加する場合を例にします。

apply()を使って再定義する

コアスクリプトを直接変更してしまうと元に戻しにくくなりますので、自分のプラグインに処理を記載するのがよいです。公式のプラグイン講座でも紹介されているのは次の形です。関数を再定義します。

const _Sprite_Clickable_update = Sprite_Clickable.prototype.update;
Sprite_Clickable.prototype.update = function() {
    _Sprite_Clickable_update.apply(this, arguments);
    // 独自の処理を追加
}

1行目で、元々の関数を変数として保存します。この時の変数名は自由(※)です。いちいち考えるのは大変なので、頭に「_」を付け、さらに「prototype」も「_」に置き換えた形がよく使われます。

ラグイン全体を関数で括っていない場合、変数名がユニークなものになるように注意する必要があります。括っているなら、プラグイン間の変数名重複は気にしなくてよくなります。

3行目のapply()によって、保存した関数の内容(元々の処理)を実行します。引数(this, arguments)は常に同じ書き方で構いません。

そしてapply()の前か後に独自の処理を追加します。完成です。

なお、さらに別のプラグインで同じ関数に処理を付け足す場合も、全く同じ書き方をすればOKです。処理がどんどん付け足されていきます。この書き方がされている限り、競合は発生しにくいです。

apply()で実行される「元々の処理」には、他のプラグインで付け足された内容も含まれると考えると安心できると思います。

再定義のチェックポイント:戻り値があるか

関数を再定義する際は、元の関数に戻り値があるかに注目します。こちらのコアスクリプト関数をご覧ください。

Window_Message.prototype.isAnySubWindowActive = function() {
    return (
        this._choiceListWindow.active ||
        this._numberInputWindow.active ||
        this._eventItemWindow.active
    );
};

メッセージウィンドウで、サブウィンドウが動作中かをチェックする関数です。結果がreturnされていますね。独自処理を入れる場合も、結果をreturnし忘れないように注意しましょう。

const _Window_Message_isAnySubWindowActive = Window_Message.prototype.isAnySubWindowActive;
Window_Message.prototype.isAnySubWindowActive = function() {
    return _Window_Message_isAnySubWindowActive.apply(this, arguments) || this.myCheck();
}

上記例では、returnする内容の後半に独自の判定myCheck()が追加されています。論理和(||)ですので、元々の処理の結果がtrueだった場合、myCheck()は実行されません。

分かりにくい場合は1行で書かずに、いったん変数で受けるとすっきりします。

const _Window_Message_isAnySubWindowActive = Window_Message.prototype.isAnySubWindowActive;
Window_Message.prototype.isAnySubWindowActive = function() {
    const originalResult = _Window_Message_isAnySubWindowActive.apply(this, arguments);
    const myResult = this.myCheck();
    return originalResult || myResult;
}

最初の例と比べると、myCheck()が必ず実行されるという違いも出てきます。

また、元々の処理が完全に不要になるケースもあるでしょう。もしそうなったとしても、他のプラグインが元々の処理を期待している可能性があります。一応実行しておくというのも悪い考えではないでしょう。

const _Window_Message_isAnySubWindowActive = Window_Message.prototype.isAnySubWindowActive;
Window_Message.prototype.isAnySubWindowActive = function() {
    // 結果は使用しないが、他のプラグインが困るかもしれないので実行しておく。
    _Window_Message_isAnySubWindowActive.apply(this, arguments);
    return this.myCheck();  
}

ただ、他のプラグインがこれを利用していない場合、全く無駄な処理を実行していることになります。あまり気にせず、潰してしまうのも一つの方針です。

ということで、apply()を使って処理を追加する例を紹介しました。

自作ツールの紹介

再定義でやることは、関数を変数に保存して、apply()を実行して――と機械的ですので、私は自作ツールを利用しています。公開しますので、プラグインをよく作るという方はご利用ください。

再定義の補助ツール

ツールの内容としては、コアスクリプト関数の1行目をコピペするだけで、変数定義やapply()の行が自動生成され、結果がクリップボードに保存されるというものです。インデントはでたらめなので、エディタのフォーマッタを使用してください。

最後に補足として、競合しやすいケースを紹介します。

競合ケース1:条件分岐

次の形は競合が発生しやすいです。

const _Sprite_Clickable_update = Sprite_Clickable.prototype.update;
Sprite_Clickable.prototype.update = function() {
    if (myFlag) {
        // 独自の処理
    } else {
        _Sprite_Clickable_update.apply(this, arguments);
    }
}

処理を分岐させる形です。独自の処理が実行される時は、元々の処理が実行されません。もしも他のプラグインが元々の処理に機能を追加していたなら、それを潰してしまうことになります。とは言っても、このような書き方をするしかないケースはよくあります。できる範囲で構いませんので、「元々の処理が実行されない」というパターンは作らないようにしましょう。

競合ケース2:オーバーライド

元々の処理を生かしようがなく、完全に書き直すことがあります。その場合、次のようにapply()を使用せずにそのまま書きます。オーバーライド(上書き)の形です。変数定義も当然不要です。

Sprite_Clickable.prototype.update = function() {
    //独自の処理
};

同類プラグイン(同関数に手を加えている)が他にある場合、プラグインリストの並びが重要です。この完全な「上書き型」プラグインがリストの下に定義されると、そこより上にある同類プラグインは潰されます。

これを意識して、上書き型のプラグインを作成する場合は次の2つの方針を選び、ユーザー向けのアナウンスをヘルプに記載するとよいです。

  • リストのなるべく上に配置してもらう(他のプラグインに機能を付け足してもらう)
  • リストのなるべく下に配置してもらう(このプラグインだけは確実に動作させる)

機能を付け足されると不具合が起きる可能性が高い場合は下の方針でいいでしょう。下の方針を採用する際は「○○機能を使用するプラグインと競合する可能性が高いので、競合が起きたらあきらめてくれ」とでも書いておきましょう。

プラグインをどう作ろうとプラグイン作者の自由ですし、どんなに気を付けても競合する時はします。競合については「して当然」と考えていいと思います。


ということで、既存の関数の再定義についてでした。