ちょっといま、別のゲームに取り組んでまして。
あ、どうもkenoです。
Unity 2Dで面白いもの出来そうなので色々調べてたんです。
ただ、Tilemapについてスクリプトで操る方法があんまりかいてないなぁと思ったのでここで共有したいと思います。
今日の目標
・Tilemapをスクリプトからいじって新しくタイルを生成したり、情報を取得したりする
まず、前提知識として知っておきたかったことがあります。
Unity2Dではhierarchyからtilemapを作成することができます。一応説明すると、tilemapはタイルと呼ばれる四角い板を自分の好きなようにぺたぺた貼ることができて、簡単にステージなどを作成することが出来る機能です。
僕はSpriteで自分のキャラクターを作って、colliderとrigidbodyで当たり判定をつけて、当たったタイルをどうこうしようと思ってたんですね。
でもtilemap colliderはあるけども当たったタイルだけを取得することは出来ないぽいですね。出来ると思ってた。
そこで!スクリプトから取得出来るようにしようじゃないかーという企画(ブログ)です。
・Tileの情報を取得する方法
まずはお好きなようにtilemapにtileを貼って下さい。それから、tilemapをスクリプトで使うには
Using UnityEngine.Tilemaps;
が必要なので追加しておきましょう。
それをスクリプトでどう取得するかって話なんですが…
[SerializeField]
Tilemap tilemap;
これでOKです。
で、重要なのはもう一つ、TileBaseという変数です。これがいわゆるTileでして、一応Tileという名前の変数もあるんですが、これはTileBaseをクラス的に継承したものなんだとか。取り敢えずTileBase使っておいたらいいかなと思います。
そして、tileの情報を取得するためにはtilemap.GetTile(Vector3Int v);を使います。ちなみにもしタイルがあるかどうかを調べるならtilemap.HasTile(Vector3Int v)を使うことができます。
先程述べたようにOnCollisionEnterとかで当たったタイルの情報を取得するのは出来ないみたいなので、タイルの座標からタイルの情報を取得します。
どのようにタイルを取得するのか、座標の指定方法等については次回別の記事でご紹介します。
次に、tileをセットする方法!
こちらはtilemap.SetTile(Vector3Int v, TileBase t);で可能です。
タイルをセットする位置とタイルのデータが必要ということですね。
ここで、割と重要(と僕は思ってる)な知識なんですが、タイルはVector3Intで座標を指定するんですね。では、(0, 0, 0)を指定するとどこにタイルが貼られるでしょうか。
正解は(0, 0, 0)をタイルの左下に合わせた位置です。
以下の画像を見てください。
わかりますか?ここが原点なんですね。例えば、タイルのマス目でマウスのカーソルのあるところに特定のタイルを描画したいというならマウスの座標の小数点を切り捨てると実現できますね。(←ここ重要)
0.0 ~ 0.9までは小数点切り捨てにすると0になるので、いまマウスがいるタイルが分かりますね。
tilemapの走査
さて、このtilemapをどう取得するかって話なんですが、tilemapには、所属しているタイルの一番右端の座標や左端の座標、上端や下端の座標を取得するプロパティが装備されています。
それがこちら↓
//一番右端
tilemap.cellBounds.max.x
//一番左端
tilemap.cellBounds.min.x
//一番上端
tilemap.cellBounds.max.y
//一番下端
tilemap.cellBounds.min.y
こんな感じ。
これらを使用してfor文を書くとすべてのセルを調べることができます。
for(int y=tilemap.cellBounds.min,y;y<tilemap.cellBounds.max.y;y++){
For(int x=tilemap.cellBounds.min.x;x<tilemap.cellBounds.max.x;x++){
Vector3Int v = new Vector3Int(x,y,0);
if(tilemap.HasTile(v)){
var tile = tilemap.GetTile(v);
}
}
}
また、もう少し簡単な方法として、cellBounds.allPositionsWithinを使う方法もあります。こちらはtilemap内にあるtileのすべての座標を配列として取得できるもので、通常は以下のように使うらしいです。
Foreach(var position in tilemap.cellBounds.allPositionsWithin){
Vector3Int v = new Vector3Int(position.x,position.y,position.z);
~~~~
}
しらべてもよくわからなかったのですが、allPositionsWithinはすべてのtileのポジションを格納している配列の先頭のイテレータをもらう?形のようで、foreachで使わない場合はなかなか使い方が難しそうでした。しかしこの方法なら一つずつ調べられますね。
max.xやmin.yを使った方法では、tilemapに所属するすべてのtileをちょうどかこえるような四角形で一つずつ見ていくので、例えばタイルのない場所があったとしてもnullで保存することができます。でもallPositionsWithinのほうが効率はいいのかなぁ。
ちなみに、max.xやmin.yを使う場面では、動的に新しくtileを追加した場合にはmaxやminの範囲が拡張されますが、今あるtileを削って範囲が小さくなっても自動的に縮小されることはないみたいです。プログラムの仕様上ってやつですね。そんな時はcellBounds.CompressBounds();を使いましょう。
はい。ここまでの情報で何となくtilemapをスクリプトで操る方法が分かったんじゃないでしょうか。
次回の記事で、マウスで指定したタイルを取得するという仕組みについて思いついたことがあるのでご説明しましょう。こちらはキャラの当たり判定などの仕組みにも応用できるはずです。
では。