エフアンダーバー

個人開発の記録

RPGツクールMV プラグイン開発の手引き

前回の記事にて公開したプラグインの開発中に得た知識を基に、 これからRPGツクールMVのプラグイン開発をしようと考えている人向けにコアエンジンの概観について記しておきます。 正直、今更感はありますが、誰かの理解の一助となれば幸いです。

www.f-sp.com

対象読者

RPGツクールのプラグイン開発はプログラミング初心者を対象としたものではありません。 したがって、内容をシンプルにまとめるため、本記事もある程度の技術を持った読者を対象として書きます。

対象とする技術レベルは次の通り。

  • JavaScriptによるプログラミングができる
    • プロトタイプチェーンの仕組みを理解している
    • プロトタイプベースの継承を理解している
  • ゲームプログラムの基本的な処理について理解している

要はJavaScriptの文法については一切説明しないし、 一般的なゲームプログラミングで使用される用語は説明なしに使うよ、ということです。

プラグインの基本中の基本

プラグインの適用方法

RPGツクールにプラグインを適用するには次の三つが必要となります。

  • プラグインファイルの先頭にプラグインであることを示すコメントを記述すること
  • プラグインファイルをプロジェクトディレクトリのjs/plugins/以下に配置すること
  • エディタのプラグイン設定でプラグインを選択し、ONにすること。

先頭に記述するコメントは次のようなものになります。

/*:
 * @plugindesc プラグインの概要
 * @author 製作者名
 *
 * @help
 * ヘルプの内容
 */

最初の行に:(コロン)があることに注意。

このあたりは公式マニュアルにしっかりと記述があるので、 詳しいことはそちらを参照するのがよいと思います(パラメータの設定方法などもあります)。 マニュアルはエディタのヘルプメニューから開けます。

プラグインによる拡張

RPGツクールのプラグインにおける拡張方法の基本は関数の置き換えです。

RPGツクールのコアエンジンには特に拡張を意識したような仕組み(例えば、イベントハンドラの登録など)はありません。 その代わり、非常に細かく分割された関数が大量に定義されています。

RPGツクールによるゲームの動作は

  1. コアエンジンの定義
  2. プラグインの実行
  3. コアエンジンの起動

という流れになっているため、プラグイン内でコアエンジンの定義を置き換えると、 その置き換えた動作でゲームが実行されます。 これがプラグインによる拡張の仕組みです。

プラグインによる拡張はだいたい次の二通りの形式になります。

  • もとの処理を内部で呼び出す関数に置換する
  • もとの処理の一部または全部を書き換えた関数に置換する
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タグがあることがわかります。

これらで読み込むのは順に、

  1. 外部ライブラリ
  2. "rpg_"から始まるコアエンジンの定義
  3. プラグインに関する情報を持った実質JSONファイル
  4. 起動コード

となっています。

今度は起動コードにあたる"main.js"を確認してみると、非常に簡潔なコードで実行が開始されることがわかります。

一行目は各プラグインを実行するコードです。 次の行ではwindow.onloadにイベントハンドラを設定しています。 その内部では、最初のシーンとしてScene_Bootを与え、SceneManagerrun関数を呼び出すことでゲームを起動しています。 どうやらこの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"内でラップしており、 各基本クラス *2PIXI.Containerを継承しています。 継承階層は次の通り。

  • PIXI.Container
    • PIXI.Sprite
      • PIXI.extras.TilingSprite
        • TilingSprite
      • Sprite
    • Tilemap
      • ShaderTilemap
    • ScreenSprite
    • Window
    • WindowLayer
    • Weather
    • ToneSprite
    • Stage

コアエンジンのコードを読んでいると度々addChildremoveChildという関数の呼び出しが現れます。 これはPIXI.Containerの関数で、シーングラフの子の登録・削除を行う関数です。 またchildrenはシーングラフの子を参照するプロパティです。

すべてのシーンの基底となるScene_BaseStageを継承しており、 SceneManager内ではこのScene_Baseを継承したオブジェクトをレンダラへと渡すことで、 その子となっているWindowSpriteを含む、画面上のあらゆるオブジェクトを描画しています。

構成概要

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や、 入力を扱うInputTouchInputはこのファイル内で定義されています。 また、NumberArrayに対するユーティリティ関数も定義されていたりします(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

*1:著作権の問題があるため基本的にコードは載せません

*2:プロトタイプベースの継承なので「クラス」と呼ぶのは適切でない気もしますが、他に適当な言葉が思い当たらないため本記事では「クラス」と呼びます

*3:"rpg_core.js"内ではScreenSpriteやToneSpriteといったクラスが定義されており、これらはただの描画基盤なので若干不正確かもしれません