前回の記事にて公開したプラグインの開発中に得た知識を基に、 これからRPGツクールMVのプラグイン開発をしようと考えている人向けにコアエンジンの概観について記しておきます。 正直、今更感はありますが、誰かの理解の一助となれば幸いです。
対象読者
RPGツクールのプラグイン開発はプログラミング初心者を対象としたものではありません。 したがって、内容をシンプルにまとめるため、本記事もある程度の技術を持った読者を対象として書きます。
対象とする技術レベルは次の通り。
- JavaScriptによるプログラミングができる
- プロトタイプチェーンの仕組みを理解している
- プロトタイプベースの継承を理解している
- ゲームプログラムの基本的な処理について理解している
要はJavaScriptの文法については一切説明しないし、 一般的なゲームプログラミングで使用される用語は説明なしに使うよ、ということです。
プラグインの基本中の基本
プラグインの適用方法
RPGツクールにプラグインを適用するには次の三つが必要となります。
- プラグインファイルの先頭にプラグインであることを示すコメントを記述すること
- プラグインファイルをプロジェクトディレクトリの
js/plugins/
以下に配置すること - エディタのプラグイン設定でプラグインを選択し、ONにすること。
先頭に記述するコメントは次のようなものになります。
/*: * @plugindesc プラグインの概要 * @author 製作者名 * * @help * ヘルプの内容 */
最初の行に:
(コロン)があることに注意。
このあたりは公式マニュアルにしっかりと記述があるので、 詳しいことはそちらを参照するのがよいと思います(パラメータの設定方法などもあります)。 マニュアルはエディタのヘルプメニューから開けます。
プラグインによる拡張
RPGツクールのプラグインにおける拡張方法の基本は関数の置き換えです。
RPGツクールのコアエンジンには特に拡張を意識したような仕組み(例えば、イベントハンドラの登録など)はありません。 その代わり、非常に細かく分割された関数が大量に定義されています。
RPGツクールによるゲームの動作は
- コアエンジンの定義
- プラグインの実行
- コアエンジンの起動
という流れになっているため、プラグイン内でコアエンジンの定義を置き換えると、 その置き換えた動作でゲームが実行されます。 これがプラグインによる拡張の仕組みです。
プラグインによる拡張はだいたい次の二通りの形式になります。
- もとの処理を内部で呼び出す関数に置換する
- もとの処理の一部または全部を書き換えた関数に置換する
var _Foo_bar = Foo.prototype.bar; Foo.prototype.bar = function() { // 追加の処理 _Foo_bar.call(this); // 追加の処理 };
Foo.prototype.bar = function() { // もとの処理のコピー // alert('bar'); alert('baz'); // もとの処理のコピー };
これら二つの大きな違いはプラグイン同士の衝突が発生しやすいか否かです。 前者は別のプラグインが同じ関数を変更しても動作する可能性が高いですが、 後者は他のプラグインの動作を破壊してしまう可能性が高いです。 可能な限り前者の方法で対処し、それが不可能な場合のみ後者を用いるのがよいと思います。
コアエンジン概要
プラグインの拡張方法で示した通り、 プラグインの拡張は既存コードの置き換えのため、コアエンジンの理解が不可欠です。 それゆえ、本記事の以降の内容はこのコアエンジンの解説になります。
動作の仕組み
動作の仕組みを理解するため、まずはindex.htmlを確認してみます *1。 すると、body内に大量のscriptタグがあることがわかります。
これらで読み込むのは順に、
- 外部ライブラリ
- "rpg_"から始まるコアエンジンの定義
- プラグインに関する情報を持った実質JSONファイル
- 起動コード
となっています。
今度は起動コードにあたる"main.js"を確認してみると、非常に簡潔なコードで実行が開始されることがわかります。
一行目は各プラグインを実行するコードです。
次の行ではwindow.onload
にイベントハンドラを設定しています。
その内部では、最初のシーンとしてScene_Boot
を与え、SceneManager
のrun
関数を呼び出すことでゲームを起動しています。
どうやらこのSceneManager
がゲームループの管理をしており、
「シーン」というものがゲーム画面を構成するルートとなる要素のようです。
SceneManager
の定義は"rpg_managers.js"内にあります。
run
関数から処理を辿っていくと、requestAnimationFrame
という関数の呼び出しコードに到達します。
これはJavaScriptのAPIで、指定したコールバック関数を再描画時に呼び出すというものです。
これにupdate
関数を繰り返し登録することでゲームループを実現しているようです。
ゲームループ内では、一般のゲームプログラム同様に、 ユーザ入力の更新、ゲーム状態の更新、描画命令の発行を行っています。
描画の仕組み
RPGツクールではあらゆる描画をPixi.jsという外部ライブラリを用いて行っています。
Pixi.jsはhtml5のcanvas要素を用いた描画とWebGLを用いた描画の二種類の描画方法をサポートしています。 RPGツクールではプラットフォームによってこれら二種類の描画を使い分けているようです。 可能であれば高速なWebGLを、そうでなければcanvasを用いた描画をしているのでしょう。 もしもPixi.jsの機能を用いた拡張を考えるのであれば、これらのモードを意識する必要があります。
Pixi.jsにおけるレンダリングはシーングラフのようなものを構成することで行います。
Pixi.jsのレンダラはシーングラフを受け取り、それを辿りながら各ノードオブジェクトを画面に描画していきます。
シーングラフのノードはPIXI.DisplayObject
またはそれらを束ねるPIXI.Container
になります(Compositeパターンによる構成)。
RPGツクールではPixi.jsの機能を"rpg_core.js"内でラップしており、
各基本クラス
*2
がPIXI.Container
を継承しています。
継承階層は次の通り。
- PIXI.Container
- PIXI.Sprite
- PIXI.extras.TilingSprite
- TilingSprite
- Sprite
- PIXI.extras.TilingSprite
- Tilemap
- ShaderTilemap
- ScreenSprite
- Window
- WindowLayer
- Weather
- ToneSprite
- Stage
- PIXI.Sprite
コアエンジンのコードを読んでいると度々addChild
やremoveChild
という関数の呼び出しが現れます。
これはPIXI.Container
の関数で、シーングラフの子の登録・削除を行う関数です。
またchildren
はシーングラフの子を参照するプロパティです。
すべてのシーンの基底となるScene_Base
はStage
を継承しており、
SceneManager
内ではこのScene_Base
を継承したオブジェクトをレンダラへと渡すことで、
その子となっているWindow
やSprite
を含む、画面上のあらゆるオブジェクトを描画しています。
構成概要
RPGツクールのコアエンジンは次の六つのファイルから成っています。
ファイル名 | 概要 |
---|---|
rpg_core.js | 外部ライブラリやWeb仕様に対するラッパー。描画関連が主だが、入力制御や音声制御、ユーティリティなどもここ。 |
rpg_managers.js | 各種管理クラスの定義。主にエディタで設定した内容の取得用。'$'から始まるグローバル変数の定義もここ。 |
rpg_objects.js | ゲーム内オブジェクトのモデルの定義。ゲームロジックのほとんどはここ。唯一セーブ時に状態が保存される。 |
rpg_sprites.js | スプライトの定義。表示位置更新などの処理もすべてここなのでプレゼンテーションロジックの定義といった方がいいかも。 |
rpg_windows.js | ウィンドウの定義。ウィンドウに関してはドメインロジックもプレゼンテーションロジックもすべてここ。 |
rpg_scenes.js | シーンの定義。スプライトやウィンドウの管理やゲーム状態の更新など。他に書きづらいことは結構雑多に書いてある。 |
名前空間のような仕組みは特に用いられておらず、 "rpg_core.js"と"rpg_managers.js"を除く、 各ファイルに定義されたクラスには対応したプリフィックスが付いています。
ファイル名 | プリフィックス |
---|---|
rpg_objects.js | Game_ |
rpg_sprites.js | Sprite_ または Spriteset_ |
rpg_windows.js | Window_ |
rpg_scenes.js | Scene_ |
rpg_core.js
"rpg_core.js"は外部ライブラリやWeb仕様に対するラッパークラスを提供します。 ここには二種類の注目すべきクラス群があります。
ひとつめは他の要素の基底クラスとなっているクラス群。
各スプライトの基底となっているSprite
、
各ウィンドウの基底となっているWindow
、
各シーンの基底となっているStage
、
とすべてこのファイル内で定義されています。
ふたつめは直接呼び出せるプロパティや関数を持った静的クラス群。
例えば、画面の大きさを取得するためのプロパティなどを持ったGraphics
や、
入力を扱うInput
とTouchInput
はこのファイル内で定義されています。
また、Number
やArray
に対するユーティリティ関数も定義されていたりします(clamp
なんかは覚えておくと非常に便利)。
他に覚えておくと役に立つかもしれないクラスとして、
画像を表すBitmap
やタイルに関する情報を扱うTilemap
なんかがあります。
rpg_managers.js
"rpg_managers.js"は各種管理クラスを提供します。
エディタで設定した内容を取得したい場合などには、 ここに対応したクラスが存在する可能性が高いので探してみるとよいかもしれません。 ファイルの読み込み・書き込み関係もここです。
しかし、おそらくこのファイルの内容で最も重要なのは、 '$'から始まる名前で定義されているグローバル変数です。 RPGツクールではゲーム状態をシーンのプロパティなどではなく、グローバルスコープの変数として記憶しています。 それに当たるのが'$'から始まる各種変数です。
'$data'から始まる変数はエディタで作成したJSONデータをそのままオブジェクト化したものです。 '$dataMap'などコンテキストによって読み込み対象が変わることはありますが、 基本的に読み込み専用で、値の変更は保存されません。
一方、'$game'から始まる変数はまさにゲームの状態を表しており、 その多くがセーブ時にシリアライズされて保存、ロード時にデシリアライズされて復元されます。 '$game'から始まる変数には"rpg_objects.js"にて定義されているモデルオブジェクトが格納されています。
rpg_objects.js
"rpg_objects.js"はプレイヤーや敵といったゲーム内オブジェクトのモデルを提供します。
ここで定義されているモデルの多くは、 "rpg_managers.js"内にて'$game'から始まるグローバル変数にインスタンス化された値が記憶されています。 そのため、任意の場所から容易に値の取得・操作が可能です。 また、これらグローバル変数の値はセーブ時にシリアライズされて保存されるため、 これらのモデルに値を書き込んでおくとセーブデータに記録されます。
モデルの内容は特に共通項目もなく、量も膨大なためここでは説明はしません。 必要に応じて、対応するモデルのコードを読んでください。
ひとつだけ覚えておくとよいかもしれないクラスとしてGame_Interpreter
があります。
これはイベントコマンドの実行クラスです。
もしも実行したい処理に似た処理がコマンドとしてもともと提供されているのであれば、
そのコマンドの処理内容を読むことで動作理解のとっかかりを得ることができるかもしれません。
rpg_sprites.js
"rpg_sprites.js"はスプライトの定義を提供します。
スプライトの定義というと、描画基盤を提供するクラス群について記述しているように聞こえますが、 それらに相当するものはPixi.jsや"rpg_core.js"がすでに提供しています。 どうやらRPGツクールにおける「スプライト」とはモデルを参照して見た目を決定するビューのことを指すようです *3。 したがって、このファイルにはプレゼンテーションロジックが記述されています。
スプライトでは毎フレーム呼び出されるupdate
関数内でモデルの変更を確認し、
それに対応して見た目を変化させています。
このupdate
関数はスプライトなら必ず呼び出される仕様のように見えますが、
実際には親のシーンが自身のupdate
関数内ですべてのchildren
に対してupdate
関数を呼び出しているだけなので注意。
つまり、スプライト内で階層を持った場合にはupdate
関数が勝手に呼び出されることはありません。
このファイル内に定義されているクラスには、
"Sprite_"から始まるスプライトと"Spriteset_"から始まるスプライトセットがあります。
スプライトセットは単純に複数のスプライトを子に持つ、スプライトの集合です。
マップ画面を表すSpriteset_Map
と戦闘画面を表すSpriteset_Battle
しかないので、
それだけ覚えておけばよいと思います。
すべてのスプライトクラスは"rpg_core.js"内で定義されているSprite
クラスを継承しています。
ファイルの先頭にSprite_Base
という、
いかにもあらゆるスプライトの基底となっていそうなクラスがありますが、
実際には一部のクラスしか継承していないので注意。
rpg_windows.js
"rpg_windows.js"はウィンドウの定義を提供します。
すべてのウィンドウクラスはWindow_Base
クラスを継承しており、
さらにそのWindow_Base
クラスは"rpg_core.js"内で定義されているWindow
クラスを継承しています。
Window
クラスは背景やフレームなど複数のスプライトを内部的に持ちますが、
独自ウィンドウを作成する場合に特に重要となるのはcontents
という名前のプロパティで公開されているBitmap
クラスのオブジェクトになります。
これはウィンドウ内部に表示する内容を表す画像で、
この画像を書き換えることによってウィンドウに様々なものを表示することが可能となります。
ウィンドウの書き方については、
似通った機能を持つウィンドウを探して、それを真似して書くのがよいと思います。
項目選択のためのWindow_Selectable
と、
それを継承してコマンド実行機能を実装したWindow_Command
は覚えておくと記述が楽になるかもしれません。
rpg_scenes.js
"rpg_scenes.js"はシーンの定義を提供します。
すべてのシーンクラスはScene_Base
クラスを継承しており、
さらにそのScene_Base
クラスは"rpg_core.js"内で定義されているStage
クラスを継承しています。
シーンのライフサイクルは"rpg_managers.js"のSceneManager
によって管理されています。
SceneManager
から呼び出される関数は次の通り。
関数名 | タイミング |
---|---|
create | シーンの生成時。 |
terminate | シーンの終了時。 |
start | シーンの開始時。最初のupdate 呼び出し前。 |
update | 毎フレーム。 |
stop | シーンの停止時。次の移行シーンが決定した時点。 |
isReady | シーンの開始前。シーンが開始可能かどうかを判定する(画像の読み込み待ち等)。 |
isBusy | シーンの停止後。シーンの移行を延期するかどうかを判定する(遷移エフェクト等)。 |
create
でスプライトやウィンドウを生成して子として登録、
update
でゲーム状態と子の更新、というのが基本的なシーンの書き方となります。
シーンの遷移もまたSceneManager
によって管理されます。
シーン遷移に関するSceneManager
の関数には次のものがあります。
- goto
- push
- pop
名前から想像できる通り、
goto
は単なる移行、
push
は現在シーンをスタックにプッシュしてから移行、
pop
はスタックからポップしたシーンに移行です。
注意すべきはスタックの状態とは無関係に、常にひとつのシーンだけが実行されるという点です。
例えば、マップ画面のシーンからpush
でメニュー画面のシーンを開くと、マップ画面のシーンは破棄されます。
pop
の呼び出しで再度マップ画面のシーンへと戻れますが、このときマップ画面のシーンは再生成されます。
おまけ:各スプライトの前後関係
ソースコード内に各スプライトのZ値に関する記述があったのでメモ。
Z値 | 内容 |
---|---|
0 | Lower tiles |
1 | Lower characters |
3 | Normal characters |
4 | Upper tiles |
5 | Upper characters |
6 | Airship shadow |
7 | Balloon |
8 | Animation |
9 | Destination |
おわりに
RPGツクールMVのプラグイン製作についてググったところ、 プログラミング初心者向けの記事ばかりで、 プログラマ向けの内容が少なかったので一通りの内容について書いてみました。 まだ、プラグイン製作を始めて日が浅いので、何か間違い等あれば指摘してもらえるとありがたいです。
正直、RPGツクールのプラグインもJavaScriptという言語自体も初心者向けではないと思うのですが、 RPGツクールからプログラミングを始める人って多いんでしょうか? 初心者向けの記事をいくつか読んだところ、 解説者自身も初心者ということも多いようでなんだかなぁ、という感じです。 RPGツクールほどシンプルに設計され、規格化された素材に溢れた環境もないのだから、 プログラミングさえしやすければいい教育環境になると思うのだけれど。
執筆時のRPGツクールMVのバージョン:1.3.1