事前のお知らせ

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

RPGツクールMZ・MVプラグインの作り方入門その5:敵キャラ毎に消滅音を変更(メモタグ)

前回の記事では、スリップダメージにSEを追加する方法を情報整理しました。
RPGツクールMZ・MVプラグインの作り方入門その4:スリップダメージにSEを追加する

今回の目標(敵キャラ毎に消滅音を変更)

敵の消滅音はデータベースで1つしか設定できません。
スライムなら潰れた音、風船っぽいなら破裂音というように、個別の消滅音を設定できるプラグインを作成します。

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

こちらはタイトルこそ「RPGツクールMV プラグインコマンド集 リファレンス」なんですが、色々な取得スクリプトも一覧になっています。 docs.google.com/spreadsheets/d/1rOIzDuhLC6IqJPEFciYOmXWL_O7X9-hMValMs7DpWCk/edit#gid=0
そして、Excel形式などでダウンロード出来るので、一括検索が出来て便利です。
今回は、この中で「敵」を全シートを通して検索したところ下記が出てきました。
システム効果音(敵消滅)の演奏
SoundManager.playEnemyCollapse();

関数を探す

playEnemyCollapse でコアをGrepします。
2行出てきました。

function を開くと下記のコードです。

SoundManager.playEnemyCollapse = function() {
    this.playSystemSound(11);
};

システムの設定を再生する関数のようです。
となると、ここを呼んでいる場所が再生させている場所でしょう。
Grep結果に戻って、開きます。

Game_Enemy.prototype.performCollapse = function() {
    Game_Battler.prototype.performCollapse.call(this);
    switch (this.collapseType()) {
    case 0:
        this.requestEffect('collapse');
        SoundManager.playEnemyCollapse();
        break;
    case 1:
        this.requestEffect('bossCollapse');
        SoundManager.playBossCollapse1();
        break;
    case 2:
        this.requestEffect('instantCollapse');
        break;
    }
};

これは敵の消滅パターンで分岐してエフェクトとSE再生がされているようです。
ここを書き換えましょう。

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

関数全体をテンプレートに貼って、プラグインを有効化します。まず、どの辺で何が動いているのか、console.logを入れていきます。

(() => {
  "use strict";

  Game_Enemy.prototype.performCollapse = function () {
    Game_Battler.prototype.performCollapse.call(this);
    switch (this.collapseType()) {
      case 0:
        this.requestEffect('collapse');
        SoundManager.playEnemyCollapse();
        console.log('playEnemyCollapse');
        console.log(playEnemyCollapse);
        break;
      case 1:
        this.requestEffect('bossCollapse');
        SoundManager.playBossCollapse1();
        console.log('playBossCollapse1');
        console.log(playBossCollapse1);
        break;
      case 2:
        this.requestEffect('instantCollapse');
        console.log('instantCollapse');
        console.log(instantCollapse);
        break;
    }
  };

})();

戦闘で敵を倒したところでエラーになりました。

rpg_managers.js:1949 ReferenceError: playEnemyCollapse is not defined
    at Game_Enemy.performCollapse (testEnemyCollapse.js:11)
    at Window_BattleLog.performCollapse (rpg_windows.js:4949)
    at Window_BattleLog.callNextMethod (rpg_windows.js:4838)
    at Window_BattleLog.update (rpg_windows.js:4795)
    at rpg_core.js:7035
    at Array.forEach ()
    at WindowLayer.update (rpg_core.js:7033)
    at rpg_scenes.js:262
    at Array.forEach ()
    at Scene_Battle.Scene_Base.updateChildren (rpg_scenes.js:260)

どうやら、playEnemyCollapse は値ではなかったようです。
このように、値ではないものをログで出力しようとするとエラーになります。
しかし、その手前の文字列は表示されているので、case 0 で処理されているのが分かりました。

メモタグの仕様

メモのデータはリファレンスではMetaDataの項目にあります。
katai5plate.github.io/RPGMV-CoreScript-Reference/jsdoc/RPG.MetaData.html

meta の形式 [メモ]の中に <名前:値> もしくは <名前> の形で書く。 <名前:値> の場合の値は String が返ってくる。 <名前> の場合の値は Boolean の true が返る。

とあります。
古くからのプラグインだと、この書式に従っていないものが多い気がするので、多分RPGツクールMVのv1.5辺りで追加された機能かな?と思っています。
今回は再生されるSEファイルを指定します。
タグ名が他のプラグインと被らないような名前をつけるのが良いでしょう。
<CollapseSeFile:ファイル名>
というタグにします。

メモタグのデータ取得方法

下記記事にあります。
tonbi.jp/Game/RPGMakerMV/018/

「this.enemy().meta」で取得します。
今回の場合「this.enemy().meta.CollapseSeFile」と書きます。最後にタグ名を入れれば、それを取得します。
追加して、ログが出るか実験しましょう。

(() => {
  "use strict";

  Game_Enemy.prototype.performCollapse = function () {
    Game_Battler.prototype.performCollapse.call(this);
    switch (this.collapseType()) {
      case 0:
        this.requestEffect('collapse');
        SoundManager.playEnemyCollapse();
        const collapseSe = this.enemy().meta.CollapseSeFile;
        console.log('collapseSe');
        console.log(collapseSe);
        break;
      case 1:
        this.requestEffect('bossCollapse');
        SoundManager.playBossCollapse1();
        break;
      case 2:
        this.requestEffect('instantCollapse');
        break;
    }
  };

})();

ログに「collapseSe」「undefined」と出ました。
undefinedとは未定義という意味です。
今回はタグが入っていない敵を倒したので、出たのでしょう。
次は、タグ「<collapseSe:Cat>」を入れて試します。

今度は「Cat」と出ました。
これで書き込む箇所と、タグの仕様、取得方法が決まりました。

条件分岐を変更する

先程のタグを取得した行を書き換えます。

const collapseSe = this.enemy().meta.CollapseSeFile ? String(this.enemy().meta.CollapseSeFile) : null;

この書き方は「条件 (三項) 演算子」と呼ばれるものです。
developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Conditional_Operator

if文で条件分岐しながら、タグの有無を確認しながら取得するのでも可能ですが、かなり使うので、覚えましょう。
collapseSe という変数に this.enemy().meta.CollapseSeFile の値があるのであれば、 ?直後の値を入れます。
ここで Stringで括っているのは文字列として変換するという明記です。
ツクールの仕様で元々文字列として取得するとあるのですが、可読性と今後の変換の癖としてこうしておいたほうが良いかと思います。
そして、値がない場合に次の:の後ろの値を入れます。

これをif文で書くと

let collapseSe;
if (this.enemy().meta.CollapseSeFile) {
  collapseSe = String(this.enemy().meta.CollapseSeFile);
} else {
  collapseSe = null;
}

となるので、「条件 (三項) 演算子」を覚えておいたほうが良いです。

SEを追加する

先日、検索して使用したスクリプトを書き加えます。
rpgmaker-script-wiki.xyz/playse_mv.php

タグがあるかないかで条件分岐をします。
ここはif文を使うのが妥当です。
「条件 (三項) 演算子」は値を代入する時に使用します。
何かを動作させる時には使用できません。(よね?)

(() => {
  "use strict";

  Game_Enemy.prototype.performCollapse = function () {
    Game_Battler.prototype.performCollapse.call(this);
    switch (this.collapseType()) {
      case 0:
        const collapseSe = this.enemy().meta.CollapseSeFile ? String(this.enemy().meta.CollapseSeFile) : null;
        if (collapseSe) {
          this.requestEffect('collapse');
          AudioManager.playSe({ "name": collapseSe, "volume": 90, "pitch": 100, "pan": 0 })
          break;
        }
        this.requestEffect('collapse');
        SoundManager.playEnemyCollapse();
        break;
      case 1:
        this.requestEffect('bossCollapse');
        SoundManager.playBossCollapse1();
        break;
      case 2:
        this.requestEffect('instantCollapse');
        break;
    }
  };

})();

case文は多数の条件のif文を簡易にするための文法です。
break が実行されると、以降の処理は実行せずに関数を終了します。
developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/switch

別関数に切り離し呼び出す

performCollapseMetaSe という新しい関数を作り、分岐する箇所を変更します。
caseに入る前にタグがあったら、そちらを別関数を呼び出し、以降は処理をしないようにします。

(() => {
  "use strict";

  Game_Enemy.prototype.performCollapse = function () {
    Game_Battler.prototype.performCollapse.call(this);
    const collapseSe = this.enemy().meta.CollapseSeFile ? String(this.enemy().meta.CollapseSeFile) : null;
    if (collapseSe) {
      this.performCollapseMetaSe(); //関数呼び出し
    } else {
      switch (this.collapseType()) {
        case 0:
          this.requestEffect('collapse');
          SoundManager.playEnemyCollapse();
          break;
        case 1:
          this.requestEffect('bossCollapse');
          SoundManager.playBossCollapse1();
          break;
        case 2:
          this.requestEffect('instantCollapse');
          break;
      }
    }
  };

  Game_Enemy.prototype.performCollapseMetaSe = function () {
    const collapseSe = this.enemy().meta.CollapseSeFile ? String(this.enemy().meta.CollapseSeFile) : null;
    if (collapseSe) {
      this.requestEffect('collapse');
      AudioManager.playSe({ "name": collapseSe, "volume": 90, "pitch": 100, "pan": 0 })
    }
  }

})();

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

重複コードを情報の整理し、競合対策をします。

(() => {
  "use strict";

  const _Game_Enemy_performCollapse = Game_Enemy.prototype.performCollapse;
  Game_Enemy.prototype.performCollapse = function () {
    Game_Battler.prototype.performCollapse.call(this);
    const collapseSe = this.enemy().meta.CollapseSeFile ? String(this.enemy().meta.CollapseSeFile) : null;
    if (collapseSe) {
      this.performCollapseMetaSe(); //関数呼び出し
    } else {
      _Game_Enemy_performCollapse.call(this);
    }
  };

  Game_Enemy.prototype.performCollapseMetaSe = function () {
    const collapseSe = this.enemy().meta.CollapseSeFile ? String(this.enemy().meta.CollapseSeFile) : null;
    if (collapseSe) {
      this.requestEffect('collapse');
      AudioManager.playSe({ "name": collapseSe, "volume": 90, "pitch": 100, "pan": 0 })
    }
  }

})();

リファクタ(コード改善)

別関数を作ったことで、

const collapseSe = this.enemy().meta.CollapseSeFile ? String(this.enemy().meta.CollapseSeFile) : null;

という行が2つになってしまいました。
こういうのは情報の整理しておきたいです。
const というのは「その関数内では上書きしない変数」の指定です。
今回は敵を倒す度に関数が呼び出されるので、リセットされて再設定されています。
これを関数の外に出して、共通化するには下記のようにletを使います。

(() => {
  "use strict";

  let collapseSe;	//変数の宣言

  const _Game_Enemy_performCollapse = Game_Enemy.prototype.performCollapse;
  Game_Enemy.prototype.performCollapse = function () {
    Game_Battler.prototype.performCollapse.call(this);
    collapseSe = this.enemy().meta.CollapseSeFile ? String(this.enemy().meta.CollapseSeFile) : null;
    if (collapseSe) {
      this.performCollapseMetaSe(); //関数呼び出し
    } else {
      _Game_Enemy_performCollapse.call(this);
    }
  };

  Game_Enemy.prototype.performCollapseMetaSe = function () {
    // const collapseSe = this.enemy().meta.CollapseSeFile ? String(this.enemy().meta.CollapseSeFile) : null;
    // if (collapseSe) {
    this.requestEffect('collapse');
    AudioManager.playSe({ "name": collapseSe, "volume": 90, "pitch": 100, "pan": 0 })
    // }
  }

})();

デプロイメント対策

このままだと、このコードで使っているSEのファイルは「未使用ファイルを除外する」機能で除外されてしまいます。
この対策はプラグインパラメーターを使用します。
@noteParam で指定したタグに含まれるファイルは、未使用ファイルと判断されません。

/*:
 * @noteParam CollapseSeFile
 * @noteRequire 1
 * @noteDir audio/se/
 * @noteType file
 * @noteData enemies
 */

(() => {
  "use strict";

  let collapseSe;

  const _Game_Enemy_performCollapse = Game_Enemy.prototype.performCollapse;
  Game_Enemy.prototype.performCollapse = function () {
    Game_Battler.prototype.performCollapse.call(this);
    collapseSe = this.enemy().meta.CollapseSeFile ? String(this.enemy().meta.CollapseSeFile) : null;
    if (collapseSe) {
      this.performCollapseMetaSe(); //関数呼び出し
    } else {
      _Game_Enemy_performCollapse.call(this);
    }
  };

  Game_Enemy.prototype.performCollapseMetaSe = function () {
    this.requestEffect('collapse');
    AudioManager.playSe({ "name": collapseSe, "volume": 90, "pitch": 100, "pan": 0 })
  }

})();

完成品

fungamemake.com/archives/12137

次のステップ

スイッチで挙動が変わるプラグインを作ります。
スイッチをプラグインパラメーターで指定できるようにします。

RPGツクールMZ・MVプラグインの作り方入門その6:スイッチに応じてゲームオーバーの処理を変更(プラグインパラメーター)

スポンサードリンク

スポンサードリンク

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

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