射線
通常在Unity中使用射線通常離不開兩個東西,一個是Ray另一個是RaycastHit。Ray和RaycastHit都是struct結構體類型。
Ray是具有開始點和方向的無窮線。說白了就是數學上的射線。那么RaycastHit是什么呢?他是獲取射線投射碰撞返回的信息的一個結構體。它里面儲存了關於射線碰撞的大量信息,如碰到游戲對象的剛體、碰撞器、UV紋理坐標等等。Ray是射線,RaycastHit是接收射線返回的信息。
Ray
利用VS查看一下Ray的定義:
里面有兩個Vector3結構體,其中direction是射線的方向,drigin是射線的原點。用這兩個Vector3就能確定一條射線。通過查詢Ray構造函數,我們得知定義一條射線的方法。例如,從當前位置向前發射一條射線:
1 //從當前變換位置開始,沿當前變換的Z方向創建一條射線。 2 3 Ray ray = new Ray(transform.position, transform.forward);
RaycastHit
利用VS查看一下RaycastHit,相比Ray它包含了更豐富的內容:
point接觸點,normal法線,distance距離,rigidbody剛體,transform變換,textureCoord紋理坐標...等諸多射線返回信息的屬性。
Physics.Raycast 射線投射
但是到此為止,查閱Ray和RaycastHit里並沒有定義讓這射線發出去的方法。原來Unity把射線發射的方法定義在了Physics物理里面,這里面包含了大量的全局物理屬性和輔助方法。射線發出的方法也在內。例如:Raycast(),RaycastAll(),RaycastNonAlloc()。其中Raycast()最為常用,廣范。
Physics.Raycast()在場景中投下可與所有碰撞器碰撞的一條光線。
Raycast()有很多重載。
參數如下:
Vector3 origin:在世界坐標,射線的起始點。
Vector3 direction:射線的方向。
float distance:射線的長度。
int layermask:投射射線,選擇投射的層蒙版。(需要注意:並不指Layer索引層,而是指圖層掩碼參數!!往下看)
out RaycastHit hitInfo:包含碰到器碰撞的更多信息。
QueryTriggerInteraction queryTriggerInteraction:指定是否查詢碰到觸發器。
例子1
發射射線檢測:
1 using UnityEngine;using System.Collections; 2 public class ExampleClass : MonoBehaviour { 3 void Update() { 4 if (Physics.Raycast(transform.position,transform.forward , 100))//從當前位置向前發射一條長為100的射線是否碰到東西 5 print("前方100米內有東西!"); 6 } 7 }
例子2
發射已有射線接收並存儲返回的碰撞信息:
1 using UnityEngine;using System.Collections; 2 public class ExampleClass : MonoBehaviour { 3 void Update() { 4 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);//相機到鼠標位置發射一條射線 5 RaycastHit hit;//碰撞信息 6 if (Physics.Raycast(ray, out hit, 100))//發射已有射線返回碰撞信息 7 Debug.DrawLine(ray.origin, hit.point);//打印射線 8 } 9 }
光線投射選擇性的與圖層碰撞
首先我在Layers添加了一個名為Enemy的User Layer 8 。然后添加了Player和Enemy兩個方塊,將Enemy方塊標記Layer層Enemy。
為MainCamera掛載MyRay.cs腳本組件。

1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 5 public class MyRay : MonoBehaviour { 6 7 // Use this for initialization 8 void Start () { 9 10 } 11 12 void Update() 13 { 14 15 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 16 17 RaycastHit hit; 18 19 int layerMask = 8;//層序號 20 21 if (Physics.Raycast(ray, out hit, 1000,layerMask)) 22 23 Debug.LogWarning("碰撞敵人!"); 24 } 25 }
結果Console並沒有打印碰撞敵人。
將layerMask下標值移位8。
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 5 public class MyRay : MonoBehaviour { 6 7 // Use this for initialization 8 void Start () { 9 10 } 11 12 void Update() 13 { 14 15 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 16 17 RaycastHit hit; 18 19 int layerMask = 1 << 8;//圖層掩碼參數 20 21 if (Physics.Raycast(ray, out hit, 1000,layerMask)) 22 23 Debug.LogWarning("碰撞敵人!"); 24 } 25 }
運行之后,將鼠標光標放在Enemy方塊上。Console輸出:
分析上述代碼:Layer的8並不是我們需要的傳遞的int類型參數。
原來,Unity使用一個32位的整數來代表圖層掩碼參數。舉個例子,以下表示32位全是零:
0000 0000 0000 0000 0000 0000 0000 0000
默認情況下Unity使用前8層作為內置層。所以當你在不使用任何圖層參數掩碼參數的情況下調用光線投射方法時,它會對所有默認的這8層進行光線投射,可以用如下掩碼表示:
0000 0000 0000 0000 0000 0000 1111 1111
我們把敵人所在層設置為第8層(下標為9),我們只希望對這個層進行光線投射。所以我們把掩碼設置為:
0000 0000 0000 0000 0000 0001 0000 0000
設置這個位掩碼簡單的辦法可以使用位移操作。我們只需要將下標為9的位置設置為1,這意味着我們只需要左移8位即可。使用向左位移操作符向左移8位,如下代碼:
int layerMask =1<<8;
要是希望多個圖層掩碼,比如8層和9層,用按位或操作符即可:
int layerMask = (1<<8)|(1<<9);
查閱官方API之后,會發現還有其他更簡單的方法,再提供幾個其他方法:
官方使用案例:
1 using UnityEngine; 2 using System.Collections; 3 4 public class ExampleClass : MonoBehaviour { 5 public LayerMask mask = -1; 6 void Update() { 7 if (Physics.Raycast(transform.position, transform.forward, 100, mask.value)) 8 Debug.Log("Hit something"); 9 10 } 11 }
使用他這個方法,會檢測所有碰撞層。
LayerMask結構體里有個靜態方法GetMask()很好用,把LayerName傳進去就自動轉int類型圖層掩碼。比位操作符好理解多了。
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 5 public class MyRay : MonoBehaviour { 6 7 // Use this for initialization 8 void Start () { 9 10 } 11 12 void Update() 13 { 14 15 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 16 17 RaycastHit hit; 18 19 if (Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("Enemy"))) 20 21 Debug.LogWarning("碰撞敵人!"); 22 23 } 24 }