前提
以下の説明ではUnityのLayersが以下のようになっていると仮定します。
Layerを使っていたときのトラブル
LayerMaskに設定したLayerとゲームオブジェクトのレイヤーが等しいか確認する処理を実装するために、こんな感じのクラスを
using UnityEngine; public class TestLayer01 : MonoBehaviour { [SerializeField] LayerMask sampleLayer; private void Start() { Debug.Log($"sampleLayer.value = {sampleLayer.value}"); Debug.Log($"gameObject.layer = {gameObject.layer}"); } }
こんな感じのオブジェクトに
貼り付けたら出力は両方とも3で同じ数字になると思っていたのですが、実際はこうなりました…。
sampleLayer.valueで表示される数字について少し検討しなければならないようです。
実行結果 sampleLayer.value = 8 gameObject.layer = 3
解決法の模索
「sampleLayer」の値を変更しながら実行すると以下のような値が得られました。
sampleLayer | value |
---|---|
Nothing | 0 |
Everything | -1 |
Default | 1 |
TransparentFX | 2 |
Ignore Raycast | 4 |
Sample | 8 |
Water | 16 |
b | 128 |
仮説
先ほどの表を見ると「Nothing」と「Everything」は少し例外のようですが、「Default」以下の値を見るとどうやら2の累乗の形となっています。
だから、「sampleLayer.value」は2進数で表した時にこうなっているのではないかと考えました。
下から1桁目が「Default」の状態、2桁目が「TransparentFX」の状態、3桁目が「Ignore Raycast」の状態というように対応しており、それぞれチェックをつけている時が1、そうでないときが0になっているのではないか。
つまり、このようになっているのではないかということです。
Layer名 | b | … | Water | Sample | Ignore Raycast | TransparentFX | Default |
---|---|---|---|---|---|---|---|
対応する桁 | 8 | … | 5 | 4 | 3 | 2 | 1 |
それぞれを10進数に直す場合は、n桁目であったらをかければよいので、
- Default:n=1より、
- Sample:n=4より、
となります。
仮説の検証
先ほどの仮説が正しいなら、
のようにしたとき、に出力される「sampleLayer.value」はn=1の「Default」とn=4の「Sample」の和となるので、
となるはずですが、実際に確認したらそうなりました。
仮説の原因
おそらく、Unity側は仮説のように保存することで「sampleLayer.value」を一意に区別できるようになるのだと思います。
例えば、
Layer名 | b | … | Water | Sample | Ignore Raycast | TransparentFX | Default |
---|---|---|---|---|---|---|---|
対応する10進数 | 8 | … | 5 | 4 | 3 | 2 | 1 |
というように保存すると、「sampleLayer」に「Default」と「TransparentFX」を保存しても、「Ignore Raycast」だけを保存しても共に「sampleLayer.value」が3となり区別できません。
解決策
今回は、「sampleLayer」に設定するLayerが1つだけの場合を考えます。
最初の「Layerを使っていたときのトラブル」の場合について考えると、以下のように「TestLayer01」クラスを書き換えればよいことがわかります。
using UnityEngine; public class TestLayer01 : MonoBehaviour { [SerializeField] LayerMask sampleLayer; private void Start() { Debug.Log($"sampleLayer.value = {sampleLayer.value}"); Debug.Log($"gameObject.layer = {gameObject.layer}"); if (Pow(2, gameObject.layer) == sampleLayer.value) { Debug.Log("Layerの一致"); } } int Pow(int n, int p) { int result = 1; for (int i = 0; i < p; i++) { result *= n; } return result; } }
あとがき
次は、LayerMaskに2つ以上のLayerを割り当てた場合について考えようと思います。