事前のお知らせ

RPGツクールMZ 攻略本レベル5 プラグインの作り方

RPGツクールMZ・MVプラグインの作り方入門その3:コマンドを追加する

前回の記事では、機能を変更したいコアの中の探し方を情報整理しました。
RPGツクールMZ・MVプラグインの作り方入門その2:コアの変更箇所を検索

今回の目標(メニューにコマンドを追加)

タイトル画面にメニューを追加します。
そのメニューを選ぶと、作者のサイトがブラウザで開きます。
RPGツクールMVで作成します。
(MZでも使用できますが、途中経過と参照するリファレンスが異なりますが、結果は同じです)

検索するキーワードの選定

katai5plate.github.io/RPGMV-CoreScript-Reference/
のクラスツリーを眺めてみると Class: Scene_Title が目に入ったので、リンク先を見ます。
katai5plate.github.io/RPGMV-CoreScript-Reference/jsdoc/Scene_Title.html

Properties:

Name Type Description
_commandWindow Window_TitleCommand コマンドウィンドウ
_backSprite1 Sprite 背景1
_backSprite2 Sprite 背景2
_gameTitleSprite Sprite タイトル

この4要素で構成されているようです。
今回はコマンドを追加したいので「Window_TitleCommand」のリンクを見ます。
katai5plate.github.io/RPGMV-CoreScript-Reference/jsdoc/Window_TitleCommand.html
…なるほど、分からん。
となったところで、「Window_TitleCommand」でGrepします。
結構多いですね… functionの直前の英単を読んで、機能を予想しましょう。
開いて、それっぽいものを探します。
今回は「Window_TitleCommand.prototype.makeCommandList」が怪しいです。


Window_TitleCommand.prototype.makeCommandList = function() {
    this.addCommand(TextManager.newGame,   'newGame');
    this.addCommand(TextManager.continue_, 'continue', this.isContinueEnabled());
    this.addCommand(TextManager.options,   'options');
};

なんか、newGame、continue、optionsとか、タイトル画面のメニュー内容っぽいでしょ?

関数の目星が合っているか確認(console.log)

まず、関数をまるっとテンプレートにコピーしてプラグイン化します。

(() => {
  "use strict";

  Window_TitleCommand.prototype.makeCommandList = function () {
    this.addCommand(TextManager.newGame, 'newGame');
    this.addCommand(TextManager.continue_, 'continue', this.isContinueEnabled());
    this.addCommand(TextManager.options, 'options');
    console.log('TextManager.options');
    console.log(TextManager.options);
  };

})();

ここで、関数の中に console.log('TextManager.options'); console.log(TextManager.options); を入れました。
凄い乱暴に言ってしまうと「TextManager.options」はツクールで言う変数みたいなものです。
(ぜんぜん違うんだけど、イメージとしてね…見るに耐えん!という人は、良い伝え方を教えて下さい!) Visual studio code を使っていると、文字色が変わっていると思いますが、この青い文字は変数名です。
''で括られると赤になります。
これは文字列扱いになっています。
このように色分けされるエディタを使用すると、何をどう見たら良いのか、目星がつけやすくなります。
このプラグインをONにして、テストプレイをします。
タイトル画面が開いたら、F8キー(もしくはF12キー)を押してみましょう。
testTitleLink.js:17 TextManager.options
testTitleLink.js:18 オプション
と出ている行がありますね。
(なぜか2回) これがログと呼ばれているものです
''で括ると、その文字そのものが、括らないと変数の値がConsoleに表示されます。
TextManager.optionsは、ツクールの「データベース>用語>オプション」に登録されている文字列を引き出して表示しています。
console.log は末永く付き合う手法になると思いますので、覚えましょう。

コマンド追加表示

今回は、本当にコマンドがここで追加できるのかを確認するために、オプションらしき箇所をコメントアウトして実験しましょう。
// this.addCommand(TextManager.options, 'options'); オプションが表示されなくなりました。
間違い無さそうです。
コメントアウトを戻して、console.logは削除しましょう。

オプションを表示している次の行に
this.addCommand('作者サイト', 'website');
と追加します。

この意味は最初の項目(TextManager.options)が表示文字列を指定します。
次の項目は(多分)シンボルと呼ばれる…インデックスみたいなものだと思ってください。
(この情報合ってるのかな…イメージは合ってると思いますが、正確な用語が分かってない…) コマンドが追加されました!
しかし、クリックしても何も起こりません。
コマンドの表示だけ追加しても動作しないのは、当然ですね。

コマンド実行内容との関連付け(関数の探し方の裏道)

次は、このコマンドを選んだ時に動作する内容を他のコマンドを参考にするために調べます。
最もシンプルそうな「options」が良いでしょう。
「options」でGrepします。

色々見ましたが、なかなか見つかりません…ここは先輩の知恵を借りましょう。
(我ながら邪道…真面目な人はJavaScriptの教科書とリファレンスを読みましょう)
タイトル画面にメニューを追加するプラグインを探します。
シンプルなプラグインを見つけました。

▼Dark Plasma Shutdown Menu(DarkPlasma様作) - DarkPlasma_ShutdownMenu.js
plugin.fungamemake.com/archives/12862
タイトルメニュー及びゲーム終了メニューにシャットダウンの項目を追加します

以下、7個の関数がありました。

  • function shutdown() {
  • Window_TitleCommand.prototype.makeCommandList = function () {
  • Scene_Title.prototype.createCommandWindow = function () {
  • Scene_Title.prototype.commandShutdown = function() {
  • Scene_GameEnd.prototype.createCommandWindow = function() {
  • Window_GameEnd.prototype.makeCommandList = function() {
  • Scene_GameEnd.prototype.commandShutdown = function () {

これらの関数名をツクールコア内でGrepしてみると、ツクールの関数は以下の4つです。

  • Window_TitleCommand.prototype.makeCommandList = function () {
  • Scene_Title.prototype.createCommandWindow = function () {
  • Scene_GameEnd.prototype.createCommandWindow = function() {
  • Window_GameEnd.prototype.makeCommandList = function() {

そして、タイトルに関係するのは2つだけです。

  • Window_TitleCommand.prototype.makeCommandList = function () {
  • Scene_Title.prototype.createCommandWindow = function () {

そのうちの1つは既に使っている「Window_TitleCommand.prototype.makeCommandList」です。
残りの「Scene_Title.prototype.createCommandWindow」をコアからコピーします。

Scene_Title.prototype.createCommandWindow = function() {
    this._commandWindow = new Window_TitleCommand();
    this._commandWindow.setHandler('newGame',  this.commandNewGame.bind(this));
    this._commandWindow.setHandler('continue', this.commandContinue.bind(this));
    this._commandWindow.setHandler('options',  this.commandOptions.bind(this));
    this.addWindow(this._commandWindow);
};

どうやら、Scene_Title.prototype.createCommandWindowで当たりのようです。
実験としてプラグインに下記を追記します。

  Scene_Title.prototype.createCommandWindow = function () {
    this._commandWindow = new Window_TitleCommand();
    this._commandWindow.setHandler('newGame', this.commandNewGame.bind(this));
    this._commandWindow.setHandler('continue', this.commandContinue.bind(this));
    this._commandWindow.setHandler('options', this.commandOptions.bind(this));
    this._commandWindow.setHandler('website', this.commandOptions.bind(this));
    this.addWindow(this._commandWindow);
  };

「作者サイト」でオプションと同じ動作をするようになりました。

実行内容の作成

これで、実行内容が追加できれば、メニューコマンドの追加が完成です。
「commandOptions」でGrepします。
「Scene_Title.prototype.commandOptions」が見つかりました。

Scene_Title.prototype.commandOptions = function() {
    this._commandWindow.close();
    SceneManager.push(Scene_Options);
};

どうやら、タイトルメニューウィンドウを閉じて、オプションシーンを呼び出しているようです。
内容は自分で作る必要があります。

    Scene_Title.prototype.commandWebsite = function () {
        window.open('https://tkool.jp/mv/');
        this._commandWindow.activate();
    };

これは、別ウィンドウにRPGツクールMVのサイトを開き、元のコマンドウィンドウを再度有効化する処理です。
このような実行内容は、「JavaScript url 別ウィンドウ」で検索すると下記が出てきました。
developer.mozilla.org/ja/docs/Web/API/Window/open

上記を参考に「Scene_Title.prototype.createCommandWindow」内を書き換えます。

//this._commandWindow.setHandler('website', this.commandOptions.bind(this));
this._commandWindow.setHandler('website', this.commandWebsite.bind(this));

全体は下記のようになります。

(() => {
  "use strict";

  Window_TitleCommand.prototype.makeCommandList = function () {
    this.addCommand(TextManager.newGame, 'newGame');
    this.addCommand(TextManager.continue_, 'continue', this.isContinueEnabled());
    this.addCommand(TextManager.options, 'options');
    this.addCommand('作者サイト', 'website');
  };

  Scene_Title.prototype.createCommandWindow = function () {
    this._commandWindow = new Window_TitleCommand();
    this._commandWindow.setHandler('newGame', this.commandNewGame.bind(this));
    this._commandWindow.setHandler('continue', this.commandContinue.bind(this));
    this._commandWindow.setHandler('options', this.commandOptions.bind(this));
    this._commandWindow.setHandler('website', this.commandWebsite.bind(this));
    this.addWindow(this._commandWindow);
  };

  Scene_Title.prototype.commandWebsite = function () {
    window.open('https://tkool.jp/mv/');
    this._commandWindow.activate();
  };

})();

競合対策(重複コードをフック)

しかし、ここで完成とするのは早計です。
このままでは、先程参考にさせていただいたプラグインのように、他にメニューにコマンドを追加するプラグインと容易に競合します。
そこで、下記のように書き換えます。

(() => {
  "use strict";

  const _Window_TitleCommand_makeCommandList = Window_TitleCommand.prototype.makeCommandList;
  Window_TitleCommand.prototype.makeCommandList = function () {
    _Window_TitleCommand_makeCommandList.call(this);
    this.addCommand('作者サイト', 'website');
  };

  const _Scene_Title_createCommandWindow = Scene_Title.prototype.createCommandWindow;
  Scene_Title.prototype.createCommandWindow = function () {
    _Scene_Title_createCommandWindow.call(this);
    this._commandWindow.setHandler('website', this.commandWebsite.bind(this));
    this.addWindow(this._commandWindow);
  };

  Scene_Title.prototype.commandWebsite = function () {
    window.open('https://tkool.jp/mv/');
    this._commandWindow.activate();
  };

})();

詳細情報

const _Window_TitleCommand_makeCommandList = Window_TitleCommand.prototype.makeCommandList;
という行は、コアのもともとの関数全体を
_Window_TitleCommand_makeCommandList
という別名の関数に代入します。

そして、元の関数名の中で、一度同じ内容を実行させるのが
_Window_TitleCommand_makeCommandList.call(this);
です。

これにより、同じ関数に追加機能をしているプラグインの動作も並び順に関係なく実行し、追加されていくことになります。

おまけ

「じゃ、最初から全部それやっとけば、競合とかしないんじゃないの?」と思いますよね?
筆者も初期は思っていました。

しかし、簡単なプラグインを数作っていくうちに、「完全に関数を上書きしないと実現できない処理」もあることに気づきました。
例えば、所持金の単位をアイコンにする場合です。
前の処理を実行してから、アイコンを表示すると、単位とアイコンが重なって表示されてしまいます。
(この場合はデータベースから単位を空欄にすれば良いじゃない?的な方法もありますが)

また、元の関数を保存していても、そちらを呼び出すタイミング(今回のように読み込んで追加したり、分岐で元の処理を呼び出したり)がなければ、効果がありません。

そんな風に可能な限り、関数を上書きではなく追加するよう工夫できるのが、良いプラグインを作る要素の1つだと考えています。
ちなみに、このネタのプラグインの完成品は下記です。
MNKR タイトルリンクコマンド追加プラグイン

オリジナルのシーンとウィンドウとコマンドを作りたい人向け記事

下記でとても丁寧に情報の整理されています。

www.5ing-myway.com/create-rpgmaker-plugin-window/

次のステップ

スリップダメージにSEを追加する方法の情報を整理します。

RPGツクールMZ・MVプラグインの作り方入門その4:スリップダメージにSEを追加する

スポンサードリンク

スポンサードリンク

-RPGツクールMZ, 攻略本レベル5 プラグインの作り方

Copyright© RPGツクールMZ・MV初心者的備忘録 - ムノクラのメモ帳 , 2021 All Rights Reserved.