Unity(C#)初心者・入門者向けチュートリアル ひよこのたまご

AndroidやiOS向けアプリを簡単に作れるゲーム開発環境Unity(ユニティ)の使い方を、チュートリアル方式で一緒に学びましょう!

【Unity】2Dタイルマップ18 2マスで1オブジェクトのTileを反転させる

Unity 2021.1.0f1 Personal(2021年3月)
f:id:hiyotama:20210327033253p:plain

前回の続きです!
前回は2マスで1オブジェクトのTileを掴んで移動させて配置させるところまで進めました。
今回はこちらのオブジェクトを移動させている時にスペースキーを押すと、オブジェクトが左右反転する機能を実装していきます。

MoveTileControllerの更新

Tilemapなどの設定を前回終了時の状態まで進めておいて下さい。
準備が整いましたら前回作成したMoveTileController.csを更新します。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Tilemaps;


public class MoveTileController : MonoBehaviour
{
    [SerializeField] TileScriptableObject tileSO;
    [SerializeField] Tilemap defaultTilemap;
    [SerializeField] Tilemap moveTilemap;

    private Vector3Int deployCheckPos;
    private PlacedTiles selectTile;
    private List<PlacedTiles> placedTiles = new List<PlacedTiles>();


    private void Start()
    {
        SetFurniture("tansu", 0, 0);
        SetFurniture("table", 2, 3);
    }


    private void SetFurniture(string name, int x, int y)
    {
        var tileStore = tileSO.tileDataList.Single(t => t.name == name);

        for (int i = 0; i < tileStore.tiles.Length; i++)
        {
            defaultTilemap.SetTile(new Vector3Int(x, y - i, 0), tileStore.tiles[i]);
        }

        placedTiles.Add(new PlacedTiles(tileStore, false, x, y));
    }


    private void Update()
    {
        if (Input.GetMouseButton(0) && selectTile != null)
        {
            MoveTile();
        }
        else if (Input.GetMouseButtonUp(0))
        {
            if (selectTile == null)
            {
                SelectTile();
            }
            else
            {
                DeployTile();
            }
        }

        if (Input.GetKeyDown(KeyCode.Space) && selectTile != null)
        {
            selectTile.xFlip = !selectTile.xFlip;
            UpdateTile(0, 0);
        }
    }


    private void SelectTile()
    {
        // Cell位置取得
        var mousePos = Input.mousePosition;
        mousePos.z = 10f;
        var cellPos = defaultTilemap.WorldToCell(Camera.main.ScreenToWorldPoint(mousePos));
        // 家具が無ければ終了
        if (!defaultTilemap.HasTile(cellPos)) return;

        // 家具を保存
        for (int i = 0; i < placedTiles.Count; i++)
        {
            for (int j = 0; j < placedTiles[i].tileBundle.Count; j++)
            {
                if (placedTiles[i].tileBundle[j].x == cellPos.x && placedTiles[i].tileBundle[j].y == cellPos.y)
                {
                    selectTile = placedTiles[i];
                    break;
                }
            }
            if (selectTile != null) break;
        }

        // 選択TileをMoveTilemapへ移動
        for (int i = 0; i < selectTile.tileBundle.Count; i++)
        {
            var pos = new Vector3Int(selectTile.tileBundle[i].x, selectTile.tileBundle[i].y, 0);
            moveTilemap.SetTile(pos, selectTile.tileBundle[i].tile);
            UpdateTileXFlip(moveTilemap, pos);
            defaultTilemap.SetTile(pos, null);
        }

        deployCheckPos = cellPos;
    }


    private void MoveTile()
    {
        // 移動先を取得
        var mousePos = Input.mousePosition;
        mousePos.z = 10f;
        Vector3Int nextPos = defaultTilemap.WorldToCell(Camera.main.ScreenToWorldPoint(mousePos));
        // 家具の中心点から移動していなければ終了
        if (selectTile.GetBasisPoint() == nextPos) return;

        // Tile位置を変更
        UpdateTile(selectTile.GetBasisPoint().x - nextPos.x, selectTile.GetBasisPoint().y - nextPos.y);

        deployCheckPos = selectTile.GetBasisPoint() + Vector3Int.one;
    }


    private void DeployTile()
    {
        // 家具中心位置とチェックポイントが違ったら値を更新して終了
        if (selectTile.GetBasisPoint() != deployCheckPos)
        {
            deployCheckPos = selectTile.GetBasisPoint();
            return;
        }

        // moveTilemapからdefaultTilemapへTileを移動
        selectTile.tileBundle.ForEach(tb => {
            var pos = new Vector3Int(tb.x, tb.y, 0);
            moveTilemap.SetTile(pos, null);
            defaultTilemap.SetTile(pos, tb.tile);
            UpdateTileXFlip(defaultTilemap, pos);
        });

        selectTile = null;
    }


    private void UpdateTile(int diffX, int diffY)
    {
        // 更新前Tileを消す
        selectTile.tileBundle.ForEach(tb => moveTilemap.SetTile(new Vector3Int(tb.x, tb.y, 0), null));

        // Tile情報の更新
        int x = selectTile.tileBundle[0].x - diffX;
        int y = selectTile.tileBundle[0].y - diffY;
        selectTile.UpdateTilePos(x, y);

        // 更新後Tileを配置
        selectTile.tileBundle.ForEach(tb => {
            var pos = new Vector3Int(tb.x, tb.y, 0);
            moveTilemap.SetTile(pos, tb.tile);
            UpdateTileXFlip(moveTilemap, pos);
        });
    }


    private void UpdateTileXFlip(Tilemap tilemap, Vector3Int pos)
    {
        if (selectTile.xFlip)
        {
            tilemap.SetTransformMatrix(pos, Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0.0f, 180.0f, 0.0f), Vector3.one));
        }
        else
        {
            tilemap.SetTransformMatrix(pos, Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0.0f, 0.0f, 0.0f), Vector3.one));
        }
    }


    public class PlacedTiles {
        public bool xFlip;
        public List<TileBundle> tileBundle = new List<TileBundle>();

        public PlacedTiles(TileStore tileStore, bool xFlip, int x, int y)
        {
            this.xFlip = xFlip;
            for (int i = 0; i < tileStore.tiles.Length; i++)
            {
                this.tileBundle.Add(new TileBundle(tileStore.tiles[i], 0, 0));
            }
            UpdateTilePos(x, y);
        }


        public void UpdateTilePos(int x, int y)
        {
            // xFlipによって配置が変わる
            for (int i = 0; i < tileBundle.Count; i++)
            {
                tileBundle[i].x = xFlip ? x-i : x;
                tileBundle[i].y = xFlip ? y : y-i;
            }
        }


        public Vector3Int GetBasisPoint()
        {
            return new Vector3Int(tileBundle[0].x, tileBundle[0].y, 0);
        }


        public class TileBundle
        {
            public Tile tile;
            public int x;
            public int y;

            public TileBundle(Tile tile, int x, int y)
            {
                this.tile = tile;
                this.x = x;
                this.y = y;
            }
        }
    }
}

MoveTileController.cs

PlacedTilesクラスのUpdateTilePosメソッド
PlacedTilesクラスにxFlipというbool型変数を追加しました。
反転させた時にtrue、平常時にfalseとなります。
xFlipはUpdateTilePosメソッド内で使われ、true(反転)の場合はx軸方向にTileをズラして並べ、false(通常)の場合はy軸方向にTileをズラして並べます。

f:id:hiyotama:20210328011913p:plain
true(反転)の場合はxを1ずつマイナスして並べる

f:id:hiyotama:20210328011946p:plain
false(通常)の場合はyを1ずつマイナスして並べる

UpdateTileXFlipメソッド
UpdateTileXFlipメソッド内でTileの角度を変更し反転を実現させています。
Tilemap.SetTransformMatrixメソッドの第一引数で指定した位置のTileを、第二引数で指定したMatrix4x4の状態に変更します。
Matrix4x4.TRSメソッドでtranslation(位置), rotation(角度), scaling(大きさ)を変更できます。今回はrotationのYの値を変更してTileを反転させています。

deployCheckPos:
Tileの反転とは直接関係ありませんが、deployCheckPosという変数を追加しました。
これにより家具の配置先まで移動したあとにもう一度同じ場所をクリックすることで、DeployTileメソッドが実行されるようになりました。

結果

それでは結果を見ていきます。

f:id:hiyotama:20210328012747g:plain

無事Tileを掴んで移動する途中で家具を反転することができました。

今回は以上となります。
ありがとうございました〜。