close
相信大家已經從上一篇文章 "Unity Layer - 基本介紹" 中,學會了基本的添加與移除 Layer 了。
接下來會講解 Layer 在程式裡面,如何去執行 添加、移除、指派、反轉、比對 等動作。
 
基本上 Layer 就是依靠 位元旗標(Flags) 在運作的,所以不太了解的人可以先到 "列舉 (enum)" 這篇文章去了解。
 
 
首先開啟 Edit > Project Settings > Tags and Layer

image01.jpg

 
再展開 Layers 之後,我們會發現每個欄位的名稱後面都有一個編號,編號為 0 ~ 31,其實這對應著 int 的 32 的位元位置

image02.jpg

 
int 為 32 位元 0000 0000 0000 0000 0000 0000 0000 0000 可以說是 32 個開關,
編號位置的順序由左至右為 31 -> 0。
 
// 取得 GameObject 本身的 Layer, 
// 這裡取得的 Layer 值就是編號 0 ~ 31, 而該值的範圍限制也是 0 ~ 31,  
// 每一個物件的 Layer 只能存一個值, 意思就是說, 每個物件都只能使用一個 Layer. 
int layer = this.gameObject.layer;
 
 
 
// 建立一個 Layer 清單, 
// 該清單可以複選多個 Layer 或者一個都不選, 
// 存放的值跟 int 一樣, 
// 最大值為 0111 1111 1111 1111 1111 1111 1111 1111 = 2147483647
// 最小值為(負數) 1000 0000 0000 0000 0000 0000 0000 0000 = -2147483648
LayerMask mask;
 
 
若 mask 為 0 的話,其二進制為 0000 0000 0000 0000 0000 0000 0000 0000,表示該 mask 狀態為 無選取(Nothing)。
若 mask 為 -1 的話,其二進制為 1111 1111 1111 1111 1111 1111 1111 1111,表示該 mask 狀態為 全選(Everything)。
 
 
現在假設我們要製作遊戲,所以我們先新增 4 個 Layer,分別為 Player(玩家)、Monster(怪物)、Background(背景)、Foreground(前景)
PS:為了排版好看,所以這邊的 Layer 我從編號 10 開始新增。

image03.jpg

 
 
注意!接下來的說明雖然有說明負數的運算方式,但其實不推薦使用負數,因為它可能導致意想不到的錯誤發生。
 
以下為每個 Layer 的值:
 
PS:因為位元位置是從 0 開始算,簡單的說,位元位置就是編號 + 1,所以 Player 編號為 10,他的位置就是 10 + 1 = 11。
 
Player(玩家)     = 編號 10
                  所以由右邊數來第 11 個的位元位置值為 1
                  正數二進制為 0000 0000 0000 0000 0000 0100 0000 0000
                  其值為 1024
                  負數二進制為 1000 0000 0000 0000 0000 0100 0000 0000
                  其值為 -2147482624
 
Monster(怪物)    = 編號 11
                  所以由右邊數來第 12 個的位元位置值為 1
                  正數二進制為 0000 0000 0000 0000 0000 1000 0000 0000
                  其值為 2048
                  負數二進制為 1000 0000 0000 0000 0000 1000 0000 0000
                  其值為 -2147481600
 
Background(背景) = 編號 12
                  所以由右邊數來第 13 個的位元位置值為 1
                  正數二進制為 0000 0000 0000 0000 0001 0000 0000 0000
                  其值為 4096
                  負數二進制為 1000 0000 0000 0000 0001 0000 0000 0000
                  其值為 -2147479552
 
Foreground(前景) = 編號 13
                  所以由右邊數來第 14 個的位元位置值為 1
                  正數二進制為 0000 0000 0000 0000 0010 0000 0000 0000
                  其值為 8192
                  負數二進制為 1000 0000 0000 0000 0010 0000 0000 0000
                  其值為 -2147475456
 
 
下面為範例:
 
若我們想要修改 GameObject 的 Layer 該怎麼做呢?
其實 GameObject 的 Layer 存的是編號(位元位置),預設 Layer 為 0,而 0 為 Default

image04.jpg

 
現在我們將 GameObject 的 Layer 改成 Player
 
// 我們可以使用 LayerMask.NameToLayer 去取得 Player 的編號(位元位置), 
// 在指派給 GameObject 的 Layer
this.gameObject.layer = LayerMask.NameToLayer( "Player" );
 
 
// 或者我們已經知道 Player 的編號(位元位置) 為 10, 
// 我們也可以直接指派 10 給 GameObject 的 Layer
this.gameObject.layer = 10;
 
 
執行結果,Layer 已修改為 Player

image05.jpg

 
 
現在我們建立一個 LayerMask 來存放 Layer,LayerMask 變數命名為 mask,目前預設為 0,所以是 Nothing

image06.jpg

 
 
我們要針對 LayerMask 去執行 添加、移除、指派、反轉、比對 之前,首先我們必須知道各 Layer 的值為多少。
而取得 Layer 的值有以下幾種方法,這邊假設我們想要取得 Player 的 Layer 值
 
// 1. 我們透過計算機或者其它方式, 事先將值算了出來
int layer = 1024;
 
 
// 2. 我們知道 Player 的編號(位元位置) 為 10, 所以透過位元移位的方式取得值 
int layer = (1 << 10);
 
位元移位講解
1 的二進制為 0000 0000 0000 0000 0000 0000 0000 0001
而我們執行 "<< 10",意思就是說將所有的位元往左邊移動 10 格
移動完會變成 0000 0000 0000 0000 0000 0100 0000 0000 換算為十進制之後為 1024
 
// 3. 我們 Player 的名稱, 去取得它的編號(位元位置), 
//    再使用方法 2 去取得 layer 的值
int layer = (1 << LayerMask.NameToLayer("Player"));
 
 
// 4. 取的負數的值, 使用 int,MinValue 加上 layer 的值, 
//    就可以取得該 layer 的負數值, 
//    這邊可得知 layer = -2147482624
int layer = int.MinValue + 1024;
 
 
 
示範如何執行添加:
 
現在我們來嘗試添加 Layer 到我們所建立的 LayerMask 裡面
 
// 我們已經取得了 layer 的值, 值為 Player(1024), 
// 這邊需要先判斷 mask 是否已經有 Player 了, 沒有的話才能夠添加
// 這邊要注意的是, 雖然使用 + 也可以添加 layer 進來, 
// 但不建議使用 + 來做添加, 因為必須經過太多額外的判斷了, 效能不佳
if ( (this.mask & layer) != layer )
{
    this.mask = this.mask + layer;
}
 
執行後結果為

image07.jpg

詳細說明 +
假如目前 mask = 0000 0000 0000 0000 0000 0000 0000 0000
而 layer = 0000 0000 0000 0000 0000 0100 0000 0000
所以 mask + layer = 0 + 1024 = 0000 0000 0000 0000 0000 0100 0000 0000 = 1024
 
若這時候沒有判斷 (this.mask & layer) != layer 而直接在加一次 layer 的話則會變成......
mask = 0000 0000 0000 0000 0000 0100 0000 0000
layer = 0000 0000 0000 0000 0000 0100 0000 0000
mask + layer = 1024 + 1024 = 0000 0000 0000 0000 0000 1000 0000 0000 = 2048
所以 mask 就變成 Monster(2048),所以這邊不建議使用 + 來做添加。
 
 
 
// 我們已經取得了 layer 的值, 值為 Monster(2048)
this.mask = this.mask | layer;
 
執行後結果為

image08.jpg

 

 

 
// 我們已經取得了 layer 的值, 值為 Background(-2147479552), 
this.mask = this.mask | layer;
 
執行後結果為

image09.jpg

 

 
示範如何執行移除:
 
現在我們來嘗試將 Layer 從我們所建立的 LayerMask 中移除
 
// 我們已經取得了 layer 的值, 值為 Player(1024), 
// 這邊需要先判斷 mask 是否包含了 Player, 有的話才能夠移除
if ( (this.mask & layer) == layer )
{
    this.mask = this.mask - layer;
}
 
執行後結果為

image10.jpg

詳細說明 -
假如目前 mask = 0000 0000 0000 0000 0000 1100 0000 0000
而 layer = 0000 0000 0000 0000 0000 0100 0000 0000
所以 mask - layer = 3072 - 1024 = 0000 0000 0000 0000 0000 1000 0000 0000 = 2048
 
若這時候沒有判斷 (this.mask & layer) == layer 而直接在減一次 layer 的話則會變成......
mask = 0000 0000 0000 0000 0000 100 0000 0000
layer = 0000 0000 0000 0000 0000 0100 0000 0000
mask - layer = 2048 - 1024 = 0000 0000 0000 0000 0000 0100 0000 0000 = 1024
所以 mask 就由 Monster(2048) 變成 Player(1024) 了,所以這邊不建議使用 - 來做移除。
 
 
 
// 我們已經取得了 layer 的值, 值為 Monster(2048), 
// 這邊需要先判斷 mask 是否包含了 Monster, 有的話才能夠移除
if ( (this.mask & layer) == layer )
{
    this.mask = this.mask ^ layer;
}
 
執行後結果為

image11.jpg

 

 

 
// 我們已經取得了 layer 的值, 值為 Background(-2147479552), 
// 這邊需要先判斷 mask 是否包含了 Background, 有的話才能夠移除
if ( (this.mask & layer) != 0 && (this.mask & layer) != int.MinValue )
{
    this.mask = this.mask ^ layer;
}
 
執行後結果為

image12.jpg

詳細說明最後一個移除的方式
大家會發現,這邊我們不使用 (this.mask & layer) == layer 去做比對,
而是改用 (this.mask & layer) != 0 && (this.mask & layer) != int.MinValue 去做比對。
 
原因如下......
若目前 mask = 4096 的話,二進制為 0000 0000 0000 0000 0001 0000 0000 0000,
而 layer = -2147479552,二進制為 1000 0000 0000 0000 0001 0000 0000 0000,
執行 & 之後,計算出來的值為 4096,二進制是 0000 0000 0000 0000 0001 0000 0000 0000,
所以 (this.mask & layer) == layer 得出的結果會是為 false,
這就發生了明明 mask 有包含 Background 但比對結果卻為 false 的情形,
因此這邊不推薦使用負數,為的就是避免不可預期的錯誤發生。
 
 
示範如何執行指派:
 
現在我們來嘗試將 Layer 直接指派給 LayerMask
 
// 我們已經取得了 layer 的值, 值為 Player(1024)
this.mask = layer;
 
執行後結果為

image13.jpg

 

 

 
// 我們已經取得了 layer 的值, 值為 Monster(-2147481600)
this.mask = layer;
 
執行後結果為

image14.jpg

 
 
示範如何執行反轉:
 
現在我們來嘗試將 Layer 反轉後再指派給 LayerMask,或直接將 LayerMask 反轉
 
// 我們已經取得了 layer 的值, 值為 Player(1024)
this.mask = ~layer;
 
執行後結果為

image15.jpg

詳細說明 ~ 反轉的意思
反轉就是將 0 轉為 1,1 轉為 0
所以 Player(1024) = 0000 0000 0000 0000 0000 0100 0000 0000
反轉過後就變成了 1111 1111 1111 1111 1111 1011 1111 1111
 
 
 
// 我們知道 Monster(2048) 與 Background(4096), 
// 所以我們直接將數字相加, 再執行反轉
this.mask = ~(2048 + 4096);
 
執行後結果為

image16.jpg

 

 

 
// 這邊直接將目前的 mask 做反轉, 所以原本的 1 就會變 0, 0 就會變 1
this.mask = ~this.mask;
 
執行後結果為

image17.jpg

 
 
示範如何執行比對:
 
比對分三種,講解前我們先將 GameObject 的 Layer 改成 Player 後,再進行說明

image18.jpg

 
 
第一種比較簡單,為 GameObject Layer 與 GameObject Layer 做比對
 
// 直接判斷雙方 layer 是否相同即可
if ( gameObject.layer == gameObject2.layer )
{
}
 
 

 

第二種為 GameObject Layer 與 LayerMask 做比對

 
// 使用位元移位的方式, 先換算出 gameObject layer 的值, 
// 再與 mask 做 '&', 出來後的值若跟 layer 一樣, 那就表示 mask 包含了 layer 
layer = (1 << this.gameObject.layer);
if ( (this.mask & layer) == layer )
{
}
 
詳細過程
假設 gameObject.layer 為 10,所以 (1 << 10) 結果為 1024,因為詳細的算法已經在上面講解過了,所以這邊不再說明。
假設 mask 也是 1024 的話,那計算過程為...
(this.mask & layer) == layer
= (0000 0000 0000 0000 0000 0100 0000 0000 & 0000 0000 0000 0000 0000 0100 0000 0000) == layer
= 0000 0000 0000 0000 0000 0100 0000 0000 == layer
= 1024 == layer
= 1024 == 1024 所以 (this.mask & layer) == layer 結果為 true。
 
 
 
// 假如 layer 為負數 Player(-2147482624)
layer = int.MinValue + (1 << this.gameObject.layer);
if ( (this.mask & layer) != 0 && (this.mask & layer) != int.MinValue )
{
}
 
詳細過程
假設 gameObject.layer 為 10,所以 (1 << 10) 結果為 1024,因為詳細的算法已經在上面講解過了,所以這邊不再說明。
假設 mask 也是 1024,而 layer = imt.MinValue + 1024 = -2147482624,那計算過程為...
(this.mask & layer) != 0 && (this.mask & layer) != int.MinValue
先算 (this.mask & layer)
= (0000 0000 0000 0000 0000 0100 0000 0000 & 1000 0000 0000 0000 0000 0100 0000 0000)
= 0000 0000 0000 0000 0000 0100 0000 0000
= 1024
 
 
(this.mask & layer) != 0 && (this.mask & layer) != int.MinValue
= 1024 != 0 && 1024 != int.MinValue
= 1024 != 0 && 1024 != -2147483648 結果為 true。
 
 
 
// 因為我們知道 gameObject layer 的編號(位元位置), 
// 所以直接將位元往右推, 再與 1 做 '&', 若結果為 1, 那就表示 mask 包含了 layer 
layer = (this.mask >> this.gameObject.layer & 1);
if ( layer == 1 )
{
}
 
詳細過程
假設 mask 為 1024,而 gameObject.layer 為 10,那計算過程為...
(this.mask >> this.gameObject.layer) & 1
= (1024 >> 10) & 1
= (0000 0000 0000 0000 0000 0100 0000 0000 >> 10) & 1   --往右移位 10 個位置
= 0000 0000 0000 0000 0000 0000 0000 0001 & 1
= 0000 0000 0000 0000 0000 0000 0000 0001 & 0000 0000 0000 0000 0000 0000 0000 0001
= 0000 0000 0000 0000 0000 0000 0000 0001
= 1 所以 (this.mask >> this.gameObject.layer) & 1 結果為 true。
 
 
第三種為 LayerMask,與 LayerMask 做比對
 
// 若要完全一樣的話, 直接比對即可
if ( this.mask == this.mask2 )
{
}
 
 
// 若只是要檢查是否包含,只要 '&' 後的值不為 0 跟 int.MinValue 則表示有包含了, 
// 判斷不為 0 以及不為 int.MinValue 的目的, 是怕 mask 的值為負數的緣故
if ( (this.mask & this.mask2) != 0 && (this.mask & this.mask2) != int.MinValue )
{
}
 
詳細過程
假設 mask 包含了 Player 與 Monster,所以 mask = 3072
與 mask2 只包含了 Player,所以 mask2 = 1024
(this.mask & this.mask2) != 0
= (3072 & 1024) != 0
= (0000 0000 0000 0000 0000 1100 0000 0000 & 0000 0000 0000 0000 0000 0100 0000 0000) != 0
= 0000 0000 0000 0000 0000 0100 0000 0000 != 0
= 1024 != 0 所以 (this.mask & this.mask2) != 0 結果為 true。
 
 
 
arrow
arrow

    岳 發表在 痞客邦 留言(0) 人氣()