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

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

【Unity】2Dタイルマップ12 Tileを掴んで、移動させて、配置する

f:id:hiyotama:20210321230209p:plain
Unity 2020.2.1f1 Personal(2021年3月)
前回の続きです!
今回はTileをクリックして掴んで、移動させて、マウスを離すとTileが配置される機能を実装します。
ゲームでは部屋や町などのエディットモードでオブジェクトを移動させる時に使います。

Tilemapを2つ作成する

まずはTilemapを2つ作成します。
1つはデフォルトのTileを配置しておくTilemap、もう1つはTileを掴んだ時にTileを少し浮かせるTilemapです。
TilemapはHierarchy > 2D Object > Tilemap Isometricから作成します。
名前は「DefaultTilemap」「MoveTilemap」とします。
f:id:hiyotama:20210321222148p:plain

MoveTIlemapのほうだけ2点設定を変えます。
まずはposition.yを0.1に変更します。
次にこの後作成するMaterialをTilemapRendererに設定します。

f:id:hiyotama:20210321222535p:plain

MaterialはProjectビュー > Create > Materialから作成できます。

f:id:hiyotama:20210321222643p:plain

名前を「SelectTileMaterial」にし、Shaderを「Sprites/Default」を選択し、
TintのAlpha(透明度)を0.7くらいに設定して下さい。

f:id:hiyotama:20210321222830p:plain

TilemapControllerスクリプト

前回作成したTilemapController.csの内容を変えていきます。
やりたいことは以下の通りです。


①Isometricモードで5 * 5のTileを自動生成
②クリックした時にTileを掴むメソッド作成(SelectTile)
③Tileを移動させるメソッド作成(MoveTile)
④マウスを話した時にTileを配置するメソッド作成(DeployTile)

まずは①から順番に見ていきます。

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


public class TilemapController : MonoBehaviour
{
    [SerializeField] Tilemap defaultTilemap;
    [SerializeField] Tilemap moveTilemap;
    [SerializeField] Tile blockTile;


    private void Start()
    {
        for (int y = 0; y < 5; y++)
        {
            for (int x = 0; x < 5; x++)
            {
                defaultTilemap.SetTile(new Vector3Int(x, y, 0), blockTile);
            }
        }
    }


    private void Update()
    {

    }
}

TilemapController.cs
Isometricモードで、SetTileメソッドで5 * 5のTileを自動生成しています。
前回の記事で詳しく解説していますのでご参照下さい。

続いて②です。

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


public class TilemapController : MonoBehaviour
{
    [SerializeField] Tilemap defaultTilemap;
    [SerializeField] Tilemap moveTilemap;
    [SerializeField] Tile blockTile;

    private Tile selectTile;
    private Vector3Int originCellPos;
    private Vector3Int selectCellPos;


    private void Start()
    {
        for (int y = 0; y < 5; y++)
        {
            for (int x = 0; x < 5; x++)
            {
                defaultTilemap.SetTile(new Vector3Int(x, y, 0), blockTile);
            }
        }
    }


    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            SelectTile();
        }
    }


    private void SelectTile()
    {
        var pos = Input.mousePosition;
        pos.z = 10f;
        selectCellPos = defaultTilemap.WorldToCell(Camera.main.ScreenToWorldPoint(pos));
        originCellPos = selectCellPos;
        var getTile = defaultTilemap.GetTile<Tile>(selectCellPos);
        if (getTile)
        {
            moveTilemap.SetTile(selectCellPos, getTile);
            defaultTilemap.SetTile(selectCellPos, null);
            selectTile = getTile;
        }
    }
}

TilemapController.cs
画面をクリックした時にSelectTileメソッドが呼ばれます。
TilemapのWorldToCellメソッドにクリックした位置(WorldPoint)を渡すと、TilemapのCell位置が返ってきます。
originalCellPosは、Tileを動かした先に既にTileがあった場合にリセットする時用に位置を保存しています。
GetTileメソッドでクリックしたTileを取得します。
Tileがnullではなかったら、SetTileメソッドでMoveTilemapへ移動させます。
SetTileメソッドの第二引数にnullを指定するとTileを削除できるので、DefaultTilemapのほうのTileは削除します。

続いて③です。

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


public class TilemapController : MonoBehaviour
{
    [SerializeField] Tilemap defaultTilemap;
    [SerializeField] Tilemap moveTilemap;
    [SerializeField] Tile blockTile;

    private Tile selectTile;
    private Vector3Int originCellPos;
    private Vector3Int selectCellPos;


    private void Start()
    {
        for (int y = 0; y < 5; y++)
        {
            for (int x = 0; x < 5; x++)
            {
                defaultTilemap.SetTile(new Vector3Int(x, y, 0), blockTile);
            }
        }
    }


    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            SelectTile();
        }
        else if (Input.GetMouseButton(0) && selectTile)
        {
            MoveTile();
        }
    }


    private void SelectTile()
    {
        var pos = Input.mousePosition;
        pos.z = 10f;
        selectCellPos = defaultTilemap.WorldToCell(Camera.main.ScreenToWorldPoint(pos));
        originCellPos = selectCellPos;
        var getTile = defaultTilemap.GetTile<Tile>(selectCellPos);
        if (getTile)
        {
            moveTilemap.SetTile(selectCellPos, getTile);
            defaultTilemap.SetTile(selectCellPos, null);
            selectTile = getTile;
        }
    }


    private void MoveTile()
    {
        var pos = Input.mousePosition;
        pos.z = 10f;
        Vector3Int nextPos = defaultTilemap.WorldToCell(Camera.main.ScreenToWorldPoint(pos));
        if (selectCellPos != nextPos)
        {
            moveTilemap.SetTile(nextPos, selectTile);
            moveTilemap.SetTile(selectCellPos, null);
            selectCellPos = nextPos;
        }
    }
}

TilemapController.cs
Updateメソッドでクリックしっぱなしの時(Input.GetMouseButtonメソッド)、またselectTileがnullでない時、MoveTileメソッドを実行し続けます。
MoveTileメソッドでは現在フレームでのCell位置(nextPos)と1つ前のフレームのCell位置(selectCellPos)を比較し、違っていたら新しいCell位置にselectTileをセットし、古いCell位置のTileを削除します。
Tileを移動させた場合、selectCellPosを更新します。

最後に④です。

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


public class TilemapController : MonoBehaviour
{
    [SerializeField] Tilemap defaultTilemap;
    [SerializeField] Tilemap moveTilemap;
    [SerializeField] Tile blockTile;

    private Tile selectTile;
    private Vector3Int originCellPos;
    private Vector3Int selectCellPos;


    private void Start()
    {
        for (int y = 0; y < 5; y++)
        {
            for (int x = 0; x < 5; x++)
            {
                defaultTilemap.SetTile(new Vector3Int(x, y, 0), blockTile);
            }
        }
    }


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


    private void SelectTile()
    {
        var pos = Input.mousePosition;
        pos.z = 10f;
        selectCellPos = defaultTilemap.WorldToCell(Camera.main.ScreenToWorldPoint(pos));
        originCellPos = selectCellPos;
        var getTile = defaultTilemap.GetTile<Tile>(selectCellPos);
        if (getTile)
        {
            moveTilemap.SetTile(selectCellPos, getTile);
            defaultTilemap.SetTile(selectCellPos, null);
            selectTile = getTile;
        }
    }


    private void MoveTile()
    {
        var pos = Input.mousePosition;
        pos.z = 10f;
        Vector3Int nextPos = defaultTilemap.WorldToCell(Camera.main.ScreenToWorldPoint(pos));
        if (selectCellPos != nextPos)
        {
            moveTilemap.SetTile(nextPos, selectTile);
            moveTilemap.SetTile(selectCellPos, null);
            selectCellPos = nextPos;
        }
    }


    private void DeployTile()
    {
        moveTilemap.SetTile(selectCellPos, null);

        if (defaultTilemap.HasTile(selectCellPos))
        {
            defaultTilemap.SetTile(originCellPos, selectTile);
        }
        else
        {
            defaultTilemap.SetTile(selectCellPos, selectTile);
        }
        
        selectTile = null;
    }
}

TilemapController.cs
Updateメソッドでマウスを離した時(Input.GetMouseUp)、DeployTileメソッドを実行します。
まずはmoveTilemapのTileを削除します。
Tilemap.HasTileメソッドにCell位置を渡すとTileがあるかをtrue/falseで返してくれます。
trueの時はTileが元あった位置(originCellPos)にselectTileを戻します。
falseの時は移動させたい位置(selectCellPos)にselectTileを配置します。
最後にselectTileをnullにします。(nullにしないとInput.GetMouseButtonとInput.GetMouseButtonUpが意図せぬタイミングで実行されてしまう)

結果

以上でTilemapController.csの設定は完了です。
InspectorからDefaultTilemap, MoveTilemap, BlockTileを設定します。
BlockTileについては前回までに作成しているのでそちらをご活用下さい。

f:id:hiyotama:20210321225517p:plain

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

f:id:hiyotama:20210321225923g:plain

Tileを掴み、移動させ、配置することができています。
また移動先に既にTileがある場合は元の位置に戻されます。

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