2019-03-02に更新

Tilemapからミニマップを作成してみた①

読了目安:11分

Unityのバージョン:2018.3.5f1

はじめに

ダンジョン探索型のゲームって、だいたい端っこにダンジョンのミニマップがついてますよね。自分の位置とか一度発見した宝箱の位置とかがわかるやつ。
今回はUnity2017で実装されたTilemapを用いて作成したステージに対応した、ミニマップの作成法を考えてみました。

ただ、AssetStoreには既に高品質のミニマップ機能がいくつもあるので、無理に自作する必要はないと思います。
自分はAssetStoreのツールがTileMapに対応しているのかチェックするのが面倒だった(英語から逃げた)のと、ちょっと調べただけでも参考になる記事がいくつもあったから試してみただけです。

ちなみにできあがったものがこちらになります。
blog.gif

ステージ作成

ステージはこんな感じでTilemapで作成しています。
キャプチャ.PNG

Gridオブジェクトの下に

Ground(通路)

キャプチャ.PNG

Wall(周りの壁)

キャプチャ.PNG

Item(オブジェクト)

キャプチャ.PNG

の3つのTileMapがあります。
Colliderが設定されているのは「Wall」と「Item」です。

マップ画面表示

まずは右下にマップ用の画面を表示します。

1.空のオブジェクト「MiniMap」を作成し、その下に新しくCameraを作成

2.Cameraの設定を以下のように変更

  • 「Position」のz座標を「0以下」にする
  • 「Projection」を「Orthographic」にする
  • 「Viewport Rect」を調整して好きな位置・大きさにウインドウを変更
  • 「Size」を調整して写す範囲を変更
  • 「Audio Listener」コンポーネントを削除

キャプチャ.PNG

右下にマップができました。
キャプチャ.PNG

マップ用テクスチャ作成・表示

今はマップカメラに映っている内容が、Main Cameraに映っている内容と同じです。
これを壁と地面が単色で塗られた画像に変えていきます。
まずは、Tilemapの情報からマップ用のテクスチャを作成します。

1.MiniMap.csを作成し、MiniMapオブジェクトにコンポーネントを追加する
2.MiniMap.csに以下を記述

using UnityEngine;
using UnityEngine.Tilemaps;
using UnityEngine.UI;

public class MiniMap : MonoBehaviour
{
    [SerializeField] Transform _grid;
    [SerializeField] Image _image;

    // マップの色
    [SerializeField] Color _wallColor;
    [SerializeField] Color _groundColor;
    [SerializeField] Color _noneColor;

    // マップ用テクスチャ
    Texture2D _texture;



    void Start()
    {
        Tilemap groundTilemap = _grid.Find("Ground").GetComponent<Tilemap>();
        Tilemap wallTilemap = _grid.Find("Wall").GetComponent<Tilemap>();

        // テクスチャ作成
        Vector3Int size = wallTilemap.size;
        _texture = new Texture2D(size.x, size.y, TextureFormat.ARGB32, false);

        // こうしないと、画像がぼやける
        _texture.filterMode = FilterMode.Point;

        Vector3Int origin = wallTilemap.origin;

        // テクスチャ座標ごとの色を求める
        for (int y = 0; y < size.y; ++y)
        {
            for (int x = 0; x < size.x; ++x)
            {
                // Tilemapのグリッド座標
                Vector3Int cellPos = new Vector3Int(origin.x + x, origin.y + y, 0);

                // 壁タイルが存在する
                if (wallTilemap.GetTile(cellPos) != null)
                {
                    _texture.SetPixel(x, y, _wallColor);
                }
                // 地面タイルが存在する
                else if (groundTilemap.GetTile(cellPos) != null)
                {
                    _texture.SetPixel(x, y, _groundColor);
                }
                // なにもない場所
                else
                {
                    _texture.SetPixel(x, y, _noneColor);
                }
            }
        }

        // テクスチャ確定
        _texture.Apply();

        // テクスチャをImageに適用
        _image.rectTransform.sizeDelta = new Vector2(size.x, size.y);
        _image.sprite = Sprite.Create(_texture, new Rect(0, 0, size.x, size.y), Vector2.zero);

        // _imageをGridの中心に移動
        Vector2 leftDownWorldPos = wallTilemap.CellToWorld(origin);
        Vector2 rightUpWorldPos = wallTilemap.CellToWorld(origin + size);
        _image.transform.position = (leftDownWorldPos + rightUpWorldPos) * 0.5f;
    }

    private void OnDestroy()
    {
        Destroy(_texture);
    }
}

3.MiniMapオブジェクトの下に「Canvas」を、さらに下に「Image」を作成する

  • 「Canvas」の「Order in Layer」をTilemapのTilemap Rendererより大きくしておく

キャプチャ.PNG

4.MiniMapオブジェクトのMiniMapの設定を以下のように変更

  • 「Grid」に「Grid」オブジェクトを設定
  • 「Image」に「MiniMap>Canvas>Image」オブジェクトを設定
  • 「Wall Color」にミニマップに表示される壁の色を設定
  • 「Ground Color」にミニマップに表示される地面の色を設定
  • 「None Color」にミニマップに表示される壁の外側の色を設定

キャプチャ.PNG

実行すると、こんな感じになります。
image.png



MiniMap.csでやっていることは

  1. 「Wall」のTilemapと同じサイズのTextureを作成する
        Tilemap groundTilemap = _grid.Find("Ground").GetComponent<Tilemap>();
        Tilemap wallTilemap = _grid.Find("Wall").GetComponent<Tilemap>();

        // テクスチャ作成
        Vector3Int size = wallTilemap.size;
        _texture = new Texture2D(size.x, size.y, TextureFormat.ARGB32, false);

        // こうしないと、画像がぼやける
        _texture.filterMode = FilterMode.Point;

FilterModeをPointにしないとこんな感じになります。

image.png

3.Tilemapのタイルの有無から、テクスチャ座標の色を決定する

        Vector3Int origin = wallTilemap.origin;

        // テクスチャ座標ごとの色を求める
        for (int y = 0; y < size.y; ++y)
        {
            for (int x = 0; x < size.x; ++x)
            {
                // Tilemapのグリッド座標
                Vector3Int cellPos = new Vector3Int(origin.x + x, origin.y + y, 0);

                // 壁タイルが存在する
                if (wallTilemap.GetTile(cellPos) != null)
                {
                    _texture.SetPixel(x, y, _wallColor);
                }
                // 地面タイルが存在する
                else if (groundTilemap.GetTile(cellPos) != null)
                {
                    _texture.SetPixel(x, y, _groundColor);
                }
                // なにもない場所
                else
                {
                    _texture.SetPixel(x, y, _noneColor);
                }
            }
        }

4.ImageをTextureと同じサイズに変更し、SpriteにTextureを適用する。その後、Imageの中心をステージの中心に合わせる

        // テクスチャ確定
        _texture.Apply();

        // テクスチャをImageに適用
        _image.rectTransform.sizeDelta = new Vector2(size.x, size.y);
        _image.sprite = Sprite.Create(_texture, new Rect(0, 0, size.x, size.y), Vector2.zero);

        // _imageをGridの中心に移動
        Vector2 leftDownWorldPos = wallTilemap.CellToWorld(origin);
        Vector2 rightUpWorldPos = wallTilemap.CellToWorld(origin + size);
        _image.transform.position = (leftDownWorldPos + rightUpWorldPos) * 0.5f;

マップカメラに、マップ画像のみ表示する

このままだと肝心のステージの画像が隠されてしまうので、マップカメラにはマップ画像だけを表示し、逆にMain Cameraではマップ画像を非表示にします。

1. Edit>Project Settings>Tags and Layers でLayersのウインドウを開き、「MiniMap」を追加する
キャプチャ.PNG

2.「MiniMap>Canvas」のLayerを「MiniMap」に変更する
キャプチャ.PNG
3.「MiniMap>Camera」の「Culling Mask」を「MiniMap」に変更する
無題.png
4.「Main Camera」の「Culling Mask」を「MiniMap以外」を選択状態に変更する
無題.png

この状態で実行するとこうなります。
image.png

だいぶそれっぽい感じになってきました。
疲れたので、残りはまた今度書きます。


長野別荘

福岡の片隅で友人と何か作ってます。 Steamでマッチ3パズルゲーム「TAVERN GUARDIANS: BANQUET」配信中。

Crieitはβバージョンで開発中です。進捗は公式Twitterアカウントをフォローして確認してください。 興味がある方は是非記事の投稿もお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

関連記事

コメント