ビット演算でレイヤーマスクを設定してみました。シーンには床の他に3つのPlaneと1つのCubeがあります。Cubeから見ると、3つのPlaneは重なっています。
赤いPlaneには0番目、緑には1番目、青には2番目のレイヤーを設定しています。
Cubeからレイを投げる時にレイヤーマスクにPhysics.AllLayersを設定すると、赤いPlaneにレイが当たります。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class LayerMaskTest : MonoBehaviour
{
public Text text;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hitInfo;
if (Physics.Raycast(transform.position, -transform.right, out hitInfo, Mathf.Infinity, Physics.AllLayers))
{
text.text = hitInfo.collider.name;
}
else
{
text.text = "ヒットなし";
}
}
}
}
すべてのレイヤーでなく2番目のレイヤーだけを設定すると、赤と緑をすり抜けて青にレイが当たります。
LayerMask layerMask = 1 << 2;
RaycastHit hitInfo;
if (Physics.Raycast(transform.position, -transform.right, out hitInfo, Mathf.Infinity, layerMask))
{
text.text = hitInfo.collider.name;
}
2番目のレイヤーを指定するときに、左シフト演算子(<<)を使っています。単に2を代入すると青でなく緑が有効になってしまいます。
LayerMask layerMask = 2;
これはマスキングの判定に2進数の値が使われるからです。0番目のレイヤーにレイを当てるかどうかは、レイヤーマスクに設定された2進数の値の1桁目が1か0かによります。
4を代入すると青にレイがあたります。4は2進数で100であり、このとき1桁目と2桁目は0なので、赤と緑は無視されるからです。
2進数を表示したいときは、System.Convert.ToStringメソッドを使います。第一引数に値を入れて、第2引数を2とすると、その値が2進数になります。第2引数を16にすると16進数になります。
Debug.Log("2: " + System.Convert.ToString(2, 2));
Debug.Log("1 << 2: " + System.Convert.ToString(1 << 2, 2));
Debug.Log("4: " + System.Convert.ToString(4, 2));
初めに指定したPhysics.AllLayersの2進数は、32桁すべてが1です。
Debug.Log("Physics.AllLayers: " + Convert.ToString(Physics.AllLayers, 2));
レイヤーを設定するプルダウンメニューの一番下のAdd Layer...をクリックすると、0~31の32個のレイヤーが使えることがわかります。
左シフト演算子(<<)は2進数の値を左にずらして、開いた桁に0を追加します。これは10進数を10倍や100倍するように、2進数を2倍、4倍、8倍・・2のn乗倍します。
0から数えて2番目のレイヤーだけ有効にするときに 1 << 2 を使ったのは、上のように 100 という2進数の値を得るためです。
複数のレイヤーを有効にする
今度は、同じレイヤーマスクを設定して、レイを3つのPlaneに別々に飛ばしてみます。
LayerMask layerMask = 1 << 0;
RaycastHit hitInfo;
string s;
s = "赤: " + Physics.Raycast(transform.position, -transform.right, out hitInfo, Mathf.Infinity, layerMask).ToString();
s += " 緑: " + Physics.Raycast(transform.position, transform.forward, out hitInfo, Mathf.Infinity, layerMask).ToString();
s += " 青: " + Physics.Raycast(transform.position, transform.right, out hitInfo, Mathf.Infinity, layerMask).ToString();
text.text = s;
1 << 0 は1と同じで、赤だけに当たります。
複数のレイヤーを指定したいときは、論理OR演算子(|)を使います。
LayerMask layerMask = 1 << 0 | 1 << 2;
これは桁ごとに見てどちらも0のときだけ0、それ以外は1になります。001と100を桁ごとに見ると、どちらも0なのは2桁目だけなので、101という値になって、緑だけレイが当たらなくなります。
つまり、有効にしたいレイヤーを | で区切って並べれば良いだけです。
特定のレイヤーを無視する
また、赤だけを無視したいときはビットごとの補数演算子(~)を使います。
LayerMask layerMask = ~(1 << 0);
これは桁ごとに0と1を反転させて、001を110にするので、赤を無視して青と緑だけにレイが当たります。
~Physics.AllLayersとするとすべて無効になります。
特定のレイヤーを切り替える
論理排他的OR演算子(^)を使うと、0と1、1と0の組み合わせの桁だけ1になって、それ以外は0になります。特定のレイヤーだけ有効と無効を切り替えることができます。
LayerMask layerMask = 1 << 0 | 1 << 2;
Debug.Log(Convert.ToString(layerMask, 2));
Debug.Log(Convert.ToString((layerMask ^ 1 << 0), 2));
Debug.Log(Convert.ToString((layerMask ^ 1 << 1), 2));
Debug.Log(Convert.ToString((layerMask ^ 1 << 2), 2));
反転したいレイヤーだけが1になっている値と演算を行うだけです。
特定のレイヤーが有効か調べる
論理AND演算子(&)を使うと、1と1の組み合わせの桁だけ1になって、他はすべて0になります。調べたい桁だけを1にした値と組み合わせると、特定のレイヤーが有効か調べられます。
例えば、0番目と2番目だけ有効にするときレイヤーマスクの値は101です。このとき1番目のレイヤーが有効かどうか調べたいときは、010という値と組み合わせます。すると、両方1の桁は無いので0になります。
LayerMask layerMask = 1 << 0 | 1 << 2;
Debug.Log(Convert.ToString((layerMask & 1 << 1),2));
Debug.Log((layerMask & 1 << 1) != 0);
同様に2番目のレイヤーを調べると結果は100です。
LayerMask layerMask = 1 << 0 | 1 << 2;
Debug.Log(Convert.ToString((layerMask & 1 << 2),2));
Debug.Log((layerMask & 1 << 2) != 0);
調べたレイヤーが有効なときは0でない値になります。0でないときに処理を変えるようにします。
レイヤーマスクだけでなく複数のもののオンオフを管理したいときにビット演算が使われます。