いぬおさんのおもしろ数学実験室

おいしい紅茶でも飲みながら数学、物理、工学、プログラミング、そして読書を楽しみましょう

Unityのマップで、どのタイルと当たったのか判定する

前回紹介した、『たのしい2Dゲームの作り方 第2版』(STUDIO SHIN2023翔泳社)にひと通り目を通しました。スプライトなど、アセットはダウンロードして使わせてもらい、あれこれ実験をやりました。大変勉強になりました。

Unityではマップを使えるんですね。この本にはマップ作り方、利用の仕方も書いてあり、早速試してみることに。2Dゲームの舞台を方眼紙のようなもので管理することがあるのですが、これをマップと言います。地面の絵、草原、花、……というように小さなタイルを用意しておき、マップの縦横の各区画にこれらをペタペタ貼り付けて舞台を作るのです。主人公のキャラクタを、その上を移動させたりしてゲームが進みます。マップはその絵だけを言うわけではありません。Unityではタイルとの当たり判定をすることができます。主人公が岩の絵にぶつかったらそこから先には行けない、とようにすることが簡単にできます。

UnityではマップにTileMap Collider 2D というコライダをアタッチすれば当たり判定ができます。通常のオブジェクト同士の当たり判定同様、OnTrriger~などで捕捉できます。しかし、もしぼくが間違えていたらすみませんが、「マップのどのタイルに当たったのか」はすぐには分かりません。これは「花に主人公がぶつかったらボーナスをもらえる」といったことが(すぐには)できないことを意味します。マップは、もちろん効率的に舞台を作る(絵を描く)ためもありますが、「主人公がここに来たら敵が確率30%で出現」とか、「ここへ来たら扉が開く」とか、それぞれのタイルに意味を持たせる目的もあるわけです。サイトをあちこち調べると、苦労している皆さんもいらっしゃるよう。もう少し複雑なマップもあるようで、それを使えばことによるとできるのかも知れませんが、とりあえず何とかならないものかと考えました。一応、以下の方法で対応しました。
直接関係あるかは分かりませんが、今回のプロジェクトではCanvasのRenderModeやRenderCameraは以下のようにしてあります。

タイルマップは以下の通り。Gridオブジェクトというのができあがり、その下にTileMapが作成されるのでした。TileMapの子にImageオブジェクトがありますが、これはぼくが作り、ここへ配置したものです。

単なるスプライトは、シーンに貼り付けることはできますがRectTransformは含まれません(TileMapにもありません)。今回、マップのどこで当たりが発生したか調べるのにこのRectTransformを使います。そこでImageオブジェクトを用意し(RectTransformは含まれている)、ちょうど自分がこしらえたマップをピッタリ覆うサイズに調整(手作業)して、TileMapの子として登録するのです。こうすれば、当たりが発生したとき主人公のキャラクタがImageオブジェクトのどこにいるのか分かり、それがマップ上の位置ということになります。子にしておけばTileMapを移動しても特に他の変更をせずに済みます。
今回ぼくが作った、マップをちょうど覆うImageオブジェクトのRectTransformは次の通りです。

肝心なのはWidthとHeightです。実験によるとImageオブジェクトの左下隅が(-Width / 2, -Height / 2)、右上隅が(Width / 2, Height / 2)でした。ちょうど真ん中が(0, 0)ということですね。あとは主人公の空間内の位置をRectTransform上でのこの座標に変換すればよいのです。それは以下でうまく行きました。
Vector2 pos1;
GameObject goChara = getGo("title_chara 1");
GameObject goImage = getGo("Image");
RectTransform rt = goImage.GetComponent<RectTransform>();
Vector3 pos = goChara.transform.position;
Vector2 sp = RectTransformUtility.WorldToScreenPoint(Camera.main, pos);
RectTransformUtility.ScreenPointToLocalPointInRectangle(
                            rt, sp, Camera.main, out pos1);

マップを(-Width / 2, -Height / 2)~(Width / 2, Height / 2)の範囲の矩形と考え、上の方法でこの範囲内の座標が分かるのですから、最後に単純な比例計算によってマップのどのタイルで当たりが起こったのか求められる、ということです。

なかなか面白いです。いろいろ考える余裕も少し出てきました。どんなのを作ろうかな、と楽しみです