『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