【Unity】LayerMaskとは?当たり判定やカメラに表示するレイヤーを指定する構造体

LayerMaskとは? Unity

こんにちは、ともくんのゲーム作り部屋にようこそ!

このページでは、

「レイヤーマスクってなに?」

「レイヤーマスクを使った処理を作りたい!」

というお悩みの方に向けた内容となっています。

Unityでは、当たり判定やカメラを表示させるオブジェクトをグルーピングするLayer(レイヤー)という機能があります。

そして、どのレイヤーに処理を影響させるかどうかを決める際に、LayerMask(レイヤーマスク)という構造体が使われます。

このLayerMaskを使うことで、指定したLayerに対してだけ、当たり判定の処理を作るということができるようになります。

そこで、このページでは、UnityのLayerMask(レイヤーマスク)という構造体について、どういうものなのか、また使い方までをまとめていきます。

この記事を書いた人

ゲーム作りを学び始めた一児のパパです。
このブログは、子供から「ゲームを作ってみたい!」と言われ、非プログラマーでゲーム作りをしたことない僕が、ゲーム作りの本を読んで独学でゲーム開発を学んでいるブログです。
同じように初めてゲーム作りをしている方と一緒に学んでいけるようなブログに出来たらいいなと思っています。
また、「このコードはおかしい」とか「もっと良い書き方があるよ!」などあれば、どんどん指摘して頂けると助かります。

LayerMask(レイヤーマスク)とは?

まずは、LayerMask(レイヤーマスク)がどういうものなのかについて紹介していきます。

当たり判定やカメラで表示するレイヤーを管理できる構造体

LayerMaskとは、冒頭でも解説した通りで、当たり判定を行うオブジェクトやカメラで表示させたいオブジェクトをまとめているレイヤーを管理することができる構造体です。

そもそもレイヤーというのは、オブジェクト毎にインスペクターウィンドウで一つだけ設定することができる項目です。

そして、このレイヤーに所属しているオブジェクトに対してだけ、当たり判定を行ったり、カメラ表示したりすることができるようになります。

LayerMaskは、どのレイヤーに対してその処理を作用するか決めている構造体で、このLayerMaskの中で指定したレイヤーにだけ処理が行われるようになります。

例えば、オブジェクトの形状に合わせて「Sphere」「Cube」「Capsule」というレイヤーを作っておき、それぞれのレイヤーに所属するオブジェクトを配置しておきます。

そして、SphereとCubeのレイヤーを指定したLayerMaskを作り、カメラでの表示処理にこのLayerMaskを指定してあげることで、SphereとCubeのオブジェクトは表示されますが、Capsuleのオブジェクトは表示されなくなります。

このようにLayerMaskを使うことで、複数のレイヤーをまとめた構造体を作ることができ、作用するレイヤーを指定することができます。

なお、カメラで表示させたいレイヤーを指定する場合は、Cameraコンポーネントにある「CullingMask」というプロパティで設定できます。

スクリプトから作用するレイヤーを変更できる

LayerMaskを使うことで、スクリプトから作用するレイヤーを指定できるようになるので、途中で当たり判定やカメラ表示の対象となるレイヤーを変更するといった処理を作れるようになります。

例えば、先ほどと同じように、「Sphere」「Cube」「Capsule」というレイヤーを作成しておき、以下のように変数で「maskA」「maskB」という2つのLayerMaskを作っておきます。

maskA:「Cube」「Sphere」のレイヤー
maskB:「Capsule」のレイヤー

そして、スペースキーを押した際にカメラで表示させるレイヤーを、maskAmaskBで切り替えて設定する処理をスクリプトで作っておきます。

すると、以下のようにスペースキーを押すだけで、表示させるレイヤーのオブジェクトを変更することができます。

LayerMask(レイヤーマスク)の使い方

ここからは、LayerMaskの使い方について紹介していきます。

LayerMaskの作成方法

LayerMaskを作る方法はいくつかありますが、ここでは簡単に作れる方法として、

  • インスペクターウィンドウから作成する
  • GetMaskメソッドで作成する
  • LayerMaskにレイヤーの追加や削除を行う

の3つを紹介していきます。

インスペクターウィンドウから作成する

LayerMaskを作る際に最も簡単な方法が、インスペクターウィンドウから指定する方法です。

まず、スクリプトの中で、以下のようにLayerMask型の変数を宣言して、頭にpublic修飾子か、SerializeFieldの属性を付けて、インスペクターウィンドウから変数を扱えるようしておきます。

public LayerMask mask;

そして、スクリプトをアタッチしているオブジェクトのインスペクターウィンドウから、先ほどの変数を選択すると、レイヤーをチェックで指定してLayerMaskを作成できます。

例えば、ゲーム内にオブジェクトを複数配置して、色ごとに「Red」「Blue」「Yellow」というレイヤーを作って指定しておきます。

そして、カメラオブジェクトに対して、以下のスクリプトをアタッチします。

using UnityEngine;

public class Test : MonoBehaviour
{
    public LayerMask mask;  // LayerMask型の変数を宣言する

    void Start()
    {
        Camera.main.cullingMask = mask; // カメラに表示するLayerMaskを指定する
    }
}

5行目でLayerMask型の変数maskをpublicを付けて宣言しているので、カメラオブジェクトを選択してインスペクターウィンドウから、layerMaskの項目でレイヤーを指定することができます。

ここでは、上記のようにRedBlueのレイヤーを指定しています。

あとは、Startメソッドの中の9行目で、CameraコンポーネントのcullingMaskに、変数のmaskを代入してあげることで、カメラで表示させるレイヤーを指定することができます。

実際にこれでゲームを実行してみると、

RedとBlueで指定したレイヤーのオブジェクトは表示されていますが、Yellowのレイヤーのオブジェクトは表示されなくなり、LayerMaskを作れているのが分かります。

GetMaskメソッドで作成する

LayerMaskを作る際に、GetMaskメソッドを使って作ることもできます。

このGetMaskメソッドは、LayerMaskの構造体で定義されているメソッドで、引数にレイヤー名を指定してLayerMask型の値を取得します。

LayerMask.GetMask(layerNames);

layerNames:取得したいレイヤー名(string型)

この引数には、string型でレイヤー名を指定し、複数のレイヤーを指定したい場合は、以下のように「,」で引数の値を増やしていきます。

LayerMask.GetMask(layerNameA, layerNameB);

例えば、先ほどと同様に「Red」「Blue」「Yellow」のレイヤーをそれぞれのオブジェクトに設定しておき、カメラオブジェクトに対して、以下のスクリプトをアタッチしておきます。

using UnityEngine;

public class Test : MonoBehaviour
{
    LayerMask mask;  // LayerMask型の変数を宣言する

    void Start()
    {
        mask = LayerMask.GetMask("Red", "Yellow");  // GetMaskでLayerMaskを作成する
        Camera.main.cullingMask = mask; // カメラに表示するLayerMaskを指定する
    }
}

5行目でLayerMask型の変数を宣言して、9行目でGetMaskメソッドを使って引数でRedYellowのレイヤーを指定して、LayerMaskを作成しています。

そして、10行目でCameraコンポーネントのcullingMaskに、変数の値を渡してあげることで、LayerMaskを指定することができます。

ゲームを実行してみると、

今度はRedとYellowのレイヤーだけが表示され、LayerMaskを作成できているのが分かります。

LayerMaskにレイヤーの追加や削除を行う

LayerMaskを1から作るのではなく、すでにあるLayerMaskに対して、レイヤーを追加したり削除したりすることもできます。

LayerMaskにレイヤーを追加する

まず、LayerMaskにレイヤーを追加する場合は、以下のように記述して追加したLayerMaskを作成できます。

layerMask | (1 << layerId);

layerMask:すでに取得しているLayerMask
layerId:追加したいレイヤー番号(int型)

例えば、「Red」「Blue」という2つのレイヤーを使って、それぞれのオブジェクトに設定しておきます。

そして、カメラオブジェクトに以下のスクリプトをアタッチしておきます。

using UnityEngine;

public class Test : MonoBehaviour
{
    LayerMask mask;     // LayerMask型の変数を宣言する
    bool isChanged = false;     // LayerMaskを変更したかどうかを判定

    void Start()
    {
        mask = LayerMask.GetMask("Red");    // GetMaskでLayerMaskを作成する
        Camera.main.cullingMask = mask;     // カメラに表示するLayerMaskを指定する
    }

    void Update()
    {
        // スペースキーを押した場合
        if (Input.GetKeyDown(KeyCode.Space) && !isChanged)
        {
            mask = mask | (1 << 7); // レイヤーを追加する処理
            Camera.main.cullingMask = mask;     // 変更したLayerMaskを指定する
            isChanged = true;
        }
    }
}

10行目でmaskの変数に、GetMaskメソッドRedのレイヤーを指定しています。

そして、スペースキーを押した場合の中の19行目で、maskにBlueのレイヤー番号である「7」を追加しています。

これでゲームを実行してみると、

スペースキーを押した際に、最初は表示されていなかったBlueのレイヤーのオブジェクトが表示されるようになり、レイヤーを追加できているのが分かります。

LayerMaskからレイヤーを削除する方法

次に、LayerMaskからレイヤーを削除する方法として、以下のように記述します。

layerMask & ~(1 << layerId);

layerMask:すでに取得しているLayerMask
layerId:削除したいレイヤー番号(int型)

先ほどと同様に、「Red」「Blue」という2つのレイヤーを準備して、以下のスクリプトをカメラオブジェクトにアタッチしてみます。

using UnityEngine;

public class Test : MonoBehaviour
{
    LayerMask mask;     // LayerMask型の変数を宣言する
    bool isChanged = false;     // LayerMaskを変更したかどうかを判定

    void Start()
    {
        mask = LayerMask.GetMask("Red", "Blue");    // GetMaskでLayerMaskを作成する
        Camera.main.cullingMask = mask;     // カメラに表示するLayerMaskを指定する
    }

    void Update()
    {
        // スペースキーを押した場合
        if (Input.GetKeyDown(KeyCode.Space) && !isChanged)
        {
            mask = mask & ~(1 << 6); // レイヤーを削除する処理
            Camera.main.cullingMask = mask;     // 変更したLayerMaskを指定する
            isChanged = true;
        }
    }
}

5行目でmaskの変数を作り、Startメソッド内の10行目でGetMaskメソッドを使って、RedBlueのレイヤーを指定しておきます。

そして、スペースキーを押した場合の中の19行目で、maskからRedのレイヤー番号である「6」を削除しています。

これでゲームを実行してみると、

スペースキーを押したらRedのレイヤーのオブジェクトが表示されなくなり、レイヤーを削除できているのが分かります。

Raycastによる当たり判定を行うレイヤーの指定

ここまでカメラに表示するレイヤーを決めるLayerMaskの例を多く挙げましたが、Raycastメソッドを使った当たり判定の処理でも、LayerMaskを活用することができます。

Raycastメソッドとは、まっすぐに進む光線を飛ばして、その先にコライダーの付いているオブジェクトがあった場合、当たり判定をbool型で返すことができるメソッドです。

このRaycastメソッドの引数にLayerMaskを指定することで、特定のレイヤーのみに当たり判定を行うことができます。

Physics.Raycast(ray, out hitInfo, maxDistance, layerMask)

ray:光線を開始する位置と飛ばす方向(Ray型)
hitInfo:光線で当たったオブジェクト情報(RaycastHit型)
maxDistance:光線を飛ばす最大距離(float型)
layerMask:当たり判定を行うレイヤー(LayerMask型もしくはint型)

なお、Raycastメソッドの引数の設定は、他にもいくつか種類があります。

ここでは、「Red」「Blue」「Yellow」の3つのレイヤーを使って、それぞれコライダーの付いたオブジェクトを配置しておき、クリックしたら特定のレイヤーだけを破壊するという処理を作っていきます。

空オブジェクトを配置して、以下のスクリプトをアタッチしておきます。

using UnityEngine;

public class Test : MonoBehaviour
{
    public LayerMask mask;     // LayerMask型の変数を宣言する

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);    // マウスカーソルの位置からの光線を定義
            RaycastHit hit;

            if (Physics.Raycast(ray, out hit, Mathf.Infinity, mask))
            {
                Destroy(hit.collider.gameObject);   // 当たり判定のあるオブジェクトの消去
            }
        }
    }
}

5行目でLayerMask型の変数を宣言して、インスペクターウィンドウからRedBlueのレイヤーだけを指定しておきます。

次に、マウスでクリックした場合の中の14行目で、Raycastメソッドを使ってマウスカーソルの位置からカメラの奥に向かって光線を飛ばすようにして、当たり判定を行っています。

そして、このRaycastメソッドの引数に、LayerMaskの変数を指定しておくことで、RedとBlueのレイヤーだけに当たり判定が行われるようにしています。

あとは、当たり判定が行われたときに、そのオブジェクトを削除するDestroyメソッドを16行目で記述しています。

これでゲームを実行してみると、

RedとBlueのレイヤーのオブジェクトは、マウスでクリックして破壊することができますが、Yellowのオブジェクトは当たり判定の処理が行われなくなっているのが分かります。

ちなみに、この仕組みを使うことで、以下のように手前にYellowのオブジェクトがあったとしても、後ろのRedBlueのレイヤーのオブジェクトに対して、当たり判定の処理を行うことができるようになります。

まとめ

このページでは、UnityのLayerMask(レイヤーマスク)について、どういう仕組みなのか、また使い方までをまとめていきましたが、いかがでしたでしょうか?

LayerMaskとは、Raycastメソッドの当たり判定やカメラを表示させるレイヤーを管理することができる構造体です。

このLayerMaskを使うことで、スクリプトから当たり判定などに作用するレイヤーを指定することができるようになります。

LayerMaskを作る際はint型で作成することもできますが、少し複雑なので、インスペクターウィンドウ経由で作るか、LayerMaskで定義されているGetMaskメソッドを使うのが便利です。

最後までお読みいただきまして、ありがとうございました!

コメント