『RPGツクールMZ』マップのデータ構造
はじめに
別の記事『『RPGツクールMZ』マップの構成と更新』では、マップで使われるクラス、その包含関係および呼び出し関係を調べました。
今回はマップのファイル読み込みとデータ構造について調べます。
本記事では JavaScript での扱いを調べるので、ツール(『RPGツクールMZ』のエディタ)上での扱いは次の記事を参考にしてください。この辺りは理解している前提で書きます。
- 『RPGツクールMZ』[モード]-[マップ]のヘルプ
- 『RPGツクールMZ』[データベース]-[タイルセット]のヘルプ
- 『RPGツクールMZ』オートタイルのヘルプ
- 『RPGツクールMZ』タイルセット規格のヘルプ
- 公式 素材規格のページ
また例によって『RPGツクールMZ』非公式JavaScriptリファレンス
にクラスなど適宜リンクします。
用語
JavaScript… Webブラウザを中心に使われるプログラミング言語です。Java は別の言語の名前で JavaScript の略称ではありません。
コアスクリプト…『RPGツクールMZ』で作ったゲームに必要なクラスを定義しているスクリプトです。事実上、これを操作するのが『RPGツクールMZ』での JavaScript です。逆に一般の JavaScript プログラマは知らないので、質問しても「なにそれ」って言われる確率が高いです。プラグインは含みません。
プラグイン… ゲームの機能を拡張するプログラムです。付属しているもの、ユーザが公開しているものがあります。自作もできます。『RPGツクールMZ』のプラグインは JavaScript で書かれています。
オブジェクト…関係する値(プロパティ)と機能(メソッド)をまとめたもの。JavaScript のプログラムはこれを組み合わせてできてます。
プロパティ…オブジェクトに付随した変数です。ただし変数と違ってオブジェクトの状態で値が変わるものもあります。逆に設定することでオブジェクトの他のプロパティなどの状態が変わる場合もあります。オブジェクトに .
をつけた後に指定します。例:object.property
メソッド…オブジェクトに付随した関数です。だいたいはプロパティを加工します。オブジェクトに .
をつけた後に指定し、()
の中で引数を指定します。例:object.method()
クラス…オブジェクトの設計図とか型とか説明される、プログラムのひとまとまり。
大域変数…プログラムのどの領域からでも扱える変数が、大域(グローバル)変数です。
配列…値に番号をつけて並べた値。例:[ 10, 4, 6 ]
(これは値として数字を並べていますが他の値も並べられます)
添字…「そえじ」配列(を保持している変数)から値を取り出す際に使う数字。例:array[ 0 ]
( JavaScript の配列の添字は 0 から始まります)
マップデータの読み込み
マップデータはプロジェクトの'data'フォルダに入っています。
マップデータは他の JSONデータと違って、ファイル名が一意ではなく'Map001.json'みたいにナンバーが振ってあるので、ちょっと特別扱いになってます。
データの読み込みを管理しているのは、DataManagerクラスです。
マップの読み込みに関してはズバリloadMapData()
というメソッドがあります。
で、読み込みが完了するとonLoad()
が呼ばれます。
ここで渡されるobject
引数はマップに限らず、ファイル読み込みされるさまざまなデータが入っている可能性があります。
マップデータかどうかはisMapObject()
で判定できます。
もしマップデータを事前にいじっておきたいなら、ここで適当に前処理を入れます。
マップデータ
$dataMap
のおおまかな内容は、リファレンスMapを見ていただくと理解できるかと思います。
ただしdata
プロパティが複雑な構成をしていて、簡単に理解しづらいので解説します。
events
プロパティがさらに複雑なのですが、そちらはまた別に解説する…かもしれません。
タイル情報
マップのタイル1マスは、レイヤー毎のタイルの情報に加え、[影ペン]と[リージョン]の情報を持っています。
z | レイヤー | 説明 |
---|---|---|
5 | なし | リージョンID |
4 | レイヤー2と3の間 | 影ペン(※) |
3 | レイヤー4 | 主にB〜EタイルID |
2 | レイヤー3 | 主にB〜EタイルID |
1 | レイヤー2 | 主にA2右側のタイルID |
0 | レイヤー1 | 主にAのタイルID |
※ 影ペンは1タイルにつき4つに分けられた領域を塗りつぶします。
その情報を 1:左上、2:右上、4:左下、8:右下 のビットとして持っています。
タイル情報の格納方法
data
プロパティはマップのタイル情報が入っています。
コアスクリプトのコードではGame_Map.tileId()
メソッドでタイル情報が取り出せます。
Game_Map.prototype.tileId = function( x, y, z ) {
const width = $dataMap.width;
const height = $dataMap.height;
return $dataMap.data[ ( z * height + y ) * width + x ] || 0;
};
『RPGツクールMZ』のマップは2D(2次元)なのですがdata
プロパティは一次元配列なので、取り出しに一工夫が必要です。
注目するのはこの部分。
( z * height + y ) * width + x
x: x座標, y: y座標, width: マップ幅, height: マップ高さ, z: マップレイヤー
わかりやすいように、ちょっと数式を変形します。
width*height * z + x + y*width
この式を順を追って説明します。
仮にwidth=3、height=2の3×2のマップだとすると[左上, 中上, 右上, 左下, 中下, 右下]
みたいな配列を考えてください。
二次元平面に展開すると次の通り。
左上,中上,右上
左下,中下,右下
x
座標はwidth
の範囲、y
座標はheight
の範囲の数値です。
ですからここでは x(0〜2)、y(0〜1)の範囲です。
たとえば左下
の部分なら x=0、y=1 になります。
実際は最初に書いたように一次元に格納されているので、x + y*width
の式で場所を指定します。
数値を入れてみると0 + 1*3 = 3
です。
data[ 3 ]
一次元配列にして最初の項目を 0 として数えていくと 3 番に左下
があります。
左上,中上,右上,左下,中下,右下
これで指定座標のデータを得られる、というわけです。
そしてひとつのマスには前述の通りマップレイヤー情報が6つ(0〜5番)あります。
四角を6つ積み重ねている3次元配列と考えます。
素直に書くと次のようになります。
左上0,中上0,右上0
左下0,中下0,右下0
左上1,中上1,右上1
左下1,中下1,右下1
左上2,中上2,右上2
左下2,中下2,右下2
左上3,中上3,右上3
左下3,中下3,右下3
左上4,中上4,右上4
左下4,中下4,右下4
左上5,中上5,右上5
左下5,中下5,右下5
実際のdata
プロパティにはすべて数字でかつ一次元配列として格納されています。
今度は左下1
を取り出す方法を考えてみます。元の数式は次のような形でした。
width*height * z + x + y*width
width * height
でマップ全体のタイルのマス目の数が算出されます。幅×高さ = 四角の面積
ですね。数値を入れると3*2 = 6
です。
さらにマップレイヤー(z)が掛けられています。数値を入れると6*1 = 6
です。
左下は先ほどやった通り0 + 1*3 = 3
でした。
3*2*1 + 0 + 1*3 = 9
全部合わせて計算するとこの通り 9 です。
一次元配列にして最初の項目を 0 として数えていくと 9 番に左下1
があります。
左上0,中上0,右上0,左下0,中下0,右下0,左上1,中上1,右上1,左下1…
次の例は1タイルだけのマップを作って、その MapXXX.json の data 部分を取り出したものです。
"data":[2864,3008,139,147,5,255] // [レイヤー1, レイヤー2, レイヤー3, レイヤー4, 影ペン, リージョン]
つまり上記のデータの場合、[タイルID, タイルID, タイルID, タイルID, 影ペン 0b0101 = 5, リージョンID]
となっているわけです。
これがさらにタイルが増えるごとに各レイヤーの数値も増えていきます。
次に示すのはエディタ上でタイルを2つに増やしてタイルをコピーしたデータです。
"data":[2864,2864,3008,3008,139,139,147,147,5,0,255,0]
マップレイヤー毎に数値がまとまっていることが確認できると思います。
エディタ上でのコピー(スポイトして⇧ペースト)だと[影ペン][リージョン]がコピーされないので0になっていることもわかります。
豆知識
豆知識というか疑問なんですが、これレイヤーとかのデータ種毎に配列を二次元にした方が扱いやすいと思います。
一次元配列の方が処理が速いとかそんな理由があったりするんでしょうか?
最後に
もしこのデータ構造を理解していれば、JSONファイルを直接検索してマップの問題となる箇所を特定できたりします。
そして穴掘りなど地形を変えたり、自動的にマップを生成する系のプラグインを作ったりもできるでしょう。
とはいえ…まぁ使いませんよね(笑)
中身がわかれば「幽霊の正体見たり枯れ尾花」少しだけ『RPGツクールMZ』を使っていて怖いことが減るんじゃないかなぁ。
レッツエンジョイ ツクールライフ!
Discussion