『RPGツクールMZ』コモンイベントの構成と更新
はじめに
『RPGツクールMZ』の[コモンイベント]が JavaScript 上でどのように実装されているか調べていきます。
ツール上のコモンイベントについては、次の記事にまとめています。
この辺についてはすでに理解しているものとして書いていきます。
『RPGツクールMZ』非公式JavaScriptリファレンス
適宜このリファレンスのページにクラスなどリンクします。
用語
JavaScript… Webブラウザを中心に使われるプログラミング言語です。Java は別の言語の名前で JavaScript の略称ではありません。
コアスクリプト…『RPGツクールMZ』で作ったゲームに必要なクラスを定義しているスクリプトです。事実上、これを操作するのが『RPGツクールMZ』での JavaScript です。逆に一般の JavaScript プログラマは知らないので、質問しても「なにそれ」って言われる確率が高いです。プラグインは含みません。
プラグイン… ゲームの機能を拡張するプログラムです。付属しているもの、ユーザが公開しているものがあります。自作もできます。『RPGツクールMZ』 のプラグインは JavaScript で書かれています。
オブジェクト…関係する値(プロパティ)と機能(メソッド)をまとめたもの。JavaScript のプログラムはこれを組み合わせてできてます。
プロパティ…オブジェクトに付随した変数です。ただし変数と違ってオブジェクトの状態で値が変わるものもあります。逆に設定することでオブジェクトの他のプロパティなどの状態が変わる場合もあります。オブジェクトに . をつけた後に指定します。例:object.property
メソッド…オブジェクトに付随した関数です。だいたいはプロパティを加工します。オブジェクトに . をつけた後に指定し、()の中で引数を指定します。例:object.method()
クラス…オブジェクトの設計図とか型とか説明される、プログラムのひとまとまり。
継承…クラスのプロパティ・メソッドを引き継いで、さらに機能を追加したクラスを作ることです。
大域変数…プログラムのどの領域からでも扱える変数が、大域(グローバル)変数です。
クラスツリー
RPGツクールMZ 非公式JavaScriptリファレンス - クラスツリーから[イベント]関連のクラスを抜粋したものが次のリストです。
データベース
その他オブジェクト
クラスの包含関係を追ってみる
クラスの継承関係はだいたいわかったとして、稼働している状態でどのオブジェクトがどのオブジェクトを抱えているのかを追っていき…ません(笑)
コモンイベントのうち自動で呼び出されるのは、マップ表示中なので大まかな部分は『RPGツクールMZ』マップの構成と更新の内容を先に頭に入れておいてね!
データ構造
コモンイベントはプロジェクトの'data/CommonEvents.json'に入っています。
データの読み込みを管理しているのは、DataManagerクラスです。
コモンイベントは大域変数$dataCommonEventsに読み込まれます。
この辺の流れはマップなどの他の JSONデータを保持している大域変数と同じです。
$dataCommonEventsの中身はCommonEventの配列です。
CommonEventは次のようなプロパティで構成されています。
| プロパティ | 説明 | 
|---|---|
| id | コモンイベントID | 
| name | 名前 | 
| trigger | トリガー( 0:なし、1:自動実行、2:並列処理 ) | 
| switchId | スイッチID | 
| list | [実行内容] | 
なのでアクセス方法は次の通り。
$dataCommonEvents[コモンイベントID].name
この中でlistはEventCommandデータの配列です。
かなり複雑なのでここでは[実行内容]のこととだけ理解してください。細かい部分は別記事で語りたい(語るとは言っていない)
豆知識
listじゃ何のリストかわかんないよ!配列(Array)に配列(list)って名前つけるな情報量ゼロ!もっと適切な識別子をつけなさいよ!!
と思っていましたが、これ「プログラムリスト」の意味でつけてるということにふと気づきました。
懐かしの 8bit BASIC 時代は F4キーでプログラムリストを表示するlistというコマンドが出力されていましたが、そのノリだったのです!(憶測です)
そんな古い感覚のネーミングであることも驚きですが「イベントコマンドはプログラムではない」という態度をずっと取ってたツクールが、コアスクリプト的には何のてらいもなくプログラム扱いしてるところが、ちょっと面白いですね。
コモンイベントの実行タイミング
コモンイベントが実行されるタイミングは次の3パターンがあります。
- [コモンイベント]イベントコマンドで呼ばれた
- [自動実行]の条件が整った
- スキル・アイテムの[使用効果]の[コモンイベント]から呼ばれた
- [並列処理]の条件が整った
イベントコマンドで呼ばれた
イベントコマンドで呼ばれた場合、Game_Interpreterのcommand117()→setupChild()メソッドが呼ばれ、Game_Interpreterが持っているオブジェクトとして新たにGame_Interpreterが追加されます。
具体的には_childInterpreterプロパティです。
元のGame_Interpreterがさらにどのオブジェクトに持たれている(包含されている)かによって、[並列処理]かその他かの挙動が変わります。
マップイベントから呼ばれることも、コモンイベントやバトルイベントから呼ばれることもあります。
自動実行
Game_Mapのメソッドがupdate()→updateInterpreter()→setupStartingEvent()→setupAutorunCommonEvent()と呼ばれ、条件が合うコモンイベントがあったらGame_Mapの_interpreterプロパティに実行するコモンイベントのlistが設定されます。
listを設定するのはGame_Interpreterのsetup()メソッドで、要は[実行内容]の実行開始処理を行ったということです。
次に示すのはsetupStartingEvent()メソッドのコードです。
Game_Map.prototype.setupStartingEvent = function() {
    this.refreshIfNeeded();
    if( this._interpreter.setupReservedCommonEvent() ) {
        return true;
    }
    if( this.setupTestEvent() ) {
        return true;
    }
    if( this.setupStartingMapEvent() ) {
        return true;
    }
    if( this.setupAutorunCommonEvent() ) {
        return true;
    }
    return false;
};
(たぶん公式のヘルプには書いてないのですが)このコードを読むと実行するイベントの優先順位がわかります。
- 
setupReservedCommonEvent(): [使用効果]で予約されているコモンイベント
- 
setupTestEvent(): [テスト]実行時に渡されるイベント
- 
setupStartingMapEvent(): マップイベント
- 
setupAutorunCommonEvent(): [自動実行]を持つコモンイベント
Game_Mapのメソッドがupdate()→updateInterpreter()と呼ばれ、そこで_interpreterプロパティのupdate()メソッドが呼ばれます。
これによってマップ上でひとつだけ実行される[実行内容]の処理が行われます。
豆知識
「[テスト]実行時に渡されるイベント」ってなんやねんという話ですよね。
実は[実行内容]の行を選択して[テスト]すると'data'フォルダに'TEST_'という接頭辞付きのファイルがいくつかできます。
イベントエディターを閉じたりするまで削除されないファイルなので、目にしたことがあるかもしれません。
その中の'TEST_Event.json'が「[テスト]実行時に渡されるイベント」です。
JSONファイルは当然テキストなので、テキストエディタで開けます。
『RPGツクールMZ』のツール上では細かい検索ができなかったりしますが、これを使うと[実行内容]の検索ができて便利…な時もあります(笑)
それに、イベントコマンドがどのようなデータ形式で記録されているか知りたいときに、1行だけ選択して[テスト]してみるとその部分だけ切り出された JSONファイルになるので解析に便利です。
あんまり解析する必要はないと思いますがねっ!
使用効果
スキル・アイテムの[使用効果]の[コモンイベント]が設定してあるものを使うと、Game_ActionのapplyGlobal()メソッドからGame_TempのreserveCommonEvent()メソッドが呼ばれ、実行するコモンイベントが予約されます。
そして先ほど自動実行の項目で説明したsetupReservedCommonEvent()メソッドで予約されたコモンイベントが処理されます。
豆知識
このスキル・アイテムの[使用効果]で予約される仕組みですが実は割とどこで使ってもよく、[コモンイベント]が用意されていない[移動ルート]中でも使えたりします。
使い方も簡単で、次のようなスクリプトでOKです。
$gameTemp.reserveCommonEvent( コモンイベントID )
いや…こんな簡単なら標準で[移動ルート]に[コモンイベント]コマンド用意して良くね?とか思います。
とはいえ思いもかけない不具合があるかもしれません。使う場合は自己責任でプリーズ。
並列処理
まず、マップの読み込み時にGame_Mapのsetup()→setupEvents()メソッドが呼ばれ、並列実行を行うコモンイベントを選んで、Game_CommonEventオブジェクトを作って_commonEventsプロパティにセットされます。
Game_Mapのメソッドがupdate()→updateEvents()と呼ばれ、_commonEventsプロパティに含まれるGame_CommonEventすべてのupdate()メソッドが呼ばれます。
Game_CommonEvent.prototype.update = function() {
    if( this._interpreter ) {
        if( !this._interpreter.isRunning() ) {
            this._interpreter.setup( this.list() );
        }
        this._interpreter.update();
    }
};
コードを読むと、_interpreterプロパティが設定されている場合、(まだ実行されていないならコモンイベントのlistを設定して)_interpreterのupdate()メソッドを呼んでいます。
このupdate()メソッドは毎フレーム呼ばれるので並列処理となるわけです。
最後に
コモンイベントはあちこちから呼ばれるので意外な挙動してしまったりしますが、直接扱うのはイベントコマンドのみなので仕組み自体は比較的シンプルです。
正直、マップイベントの中に[実行内容]まで突っ込まないで、コモンイベント的な外部オブジェクトに分割しておいた方が何かと仕組みがスッキリするんじゃないかと思ったりしますが、この辺も過去からの資産の蓄積ゆえに変更が難しい部分なんでしょうかね。
ついでに言うとマップ毎に分割されたコモンイベントもあると、運用しやすいんですけど。
そのマップ特有の[自動実行]や[並列処理]を実行させるために、どうしてもマップ上に透明のマップイベントを置く必要があって管理面倒なんで。
…要求は尽きませんね!
レッツエンジョイ ツクールライフ!


Discussion