Unity全面入門筆記8-瞭解GameObject(2)

深入瞭解GameObject

GameObject的屬性

  • Tag

Name下方的選項組,對應GameObject.tag,多用在控制碰撞和查找物體上。在創建標籤時可以使用系統自帶的標籤,也可以在下拉菜單中點擊New Tag來創造自定義標籤。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-pNepgVu7-1569681832645)(C:\Users\UPiracy\AppData\Roaming\Typora\typora-user-images\1568358774570.png)]

點擊Add Tag…後,Inspector會跳轉到Tags&Layers界面,在該界面下創建自定義標籤後,需要重新點選GameObject再將其tag設置爲新創建的Tag。

在腳本中我們可以使用Tag來查找GameObject,使用Tag來查找比使用Name來查找消耗的資源更少一些:

GameObject GameObject.FindGameObjectWithTag(string tag);
GameObject[] GameObject.FindGameObjectsWithTag(string tag);

CompareTag方法可以用於判斷物體的Tag:

bool GameObject.CompareTag(string tag);

這個函數比簡單的使用字符串判斷句this.tag == tag要高效的多。

CompareTag最常用於碰撞的判定,如下面的例子:

void OnCollisionEnter(collider other)
{
    if(other.CompareTag("bullet"))
    {
        GameOver();
    }
}
  • Layer

Tag右側的選項組,對應腳本中的GameObject.layer。它的作用涉及到了攝像機的渲染和物理碰撞的檢測。

同樣的我們可以自定義Layer。Unity最多支持32層Layer,因爲Unity經常需要使用Int32作爲掩碼來輸入Layer信息。比如說我需要使用射線檢測系統來跟蹤鼠標指向的目標,但只需要檢測Unit而不需要檢測其它物體,我們就可以將Unit設置爲單獨的一個Layer,比如User Layer8 = Unit,然後使用下面的代碼來實現:

readonly LayerMask mask = 1<<8;
void Update()
{
    if(Input.GetMouseButtonDown(0))
    {
        RaycastHit hit;
        if(Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition),out hit, mask.value));
        {
            Debug.Log("Hit on a Unit!");
            DoSomething();
        }
	}
}

上面的代碼中,Raycast等射線檢測的知識將等到物理章節中解釋。作爲參數的mask作爲一個掩碼,從最低位到最高位分別表示Layers列表中的第0個元素到第31個元素,每一位如果是1則表示選中該層Layer,爲0表示未選中,而Raycast函數中接收mask傳入的掩碼,只對第8層進行檢測,從而實現目標效果。

層與層的碰撞可以在主界面菜單欄的"Edit"->“Project Settings”->“Physics"中修改,2D遊戲則是"Edit”->“Project Settings”->“Physics 2D”。在這個窗口頁面的最下方有一個碰撞檢測表格,在這個表格中勾除某些格就可以使對應的兩個層間不再發生碰撞,如圖:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-JOdeMgID-1569681832646)(C:\Users\UPiracy\AppData\Roaming\Typora\typora-user-images\1568360459997.png)]

在上圖的情況中,標籤爲New Layer的物體不再和其他標籤爲New Layer或Default的物體發生碰撞。

可以通過鎖定某一層來防止不小心移動該層的物體。具體的做法是在Unity主界面的工具欄中點擊Layers下拉菜單並改變對應層最右邊的鎖定選項。在這個菜單中的眼睛圖標代表了一個層的可見性,當該選項被勾除則這一層不會被渲染。後面的鎖是鎖定性,被勾選時該層物體不能被選中:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-bbEKrVnx-1569681832647)(C:\Users\UPiracy\AppData\Roaming\Typora\typora-user-images\1568360534346.png)]

在改變一個父物體的Layer屬性時,會彈出如圖所示的窗口,詢問是否要將改變應用到所有子物體上。選擇Yes則子物體的Layer也會一起改變,選擇No則只改變父物體的Layer。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-PCnIefKZ-1569681832647)(C:\Users\UPiracy\AppData\Roaming\Typora\typora-user-images\1568360576672.png)]

生成與銷燬GameObject

  • 使用Instantiate實例化新的GameObject

GameObject是不能使用new來實例化的,必須使用Instantiate函數:

GameObject Instantiate(GameObject obj);
GameObject Instantiate(GameObject obj, Transform father);
GameObject Instantiate(GameObject obj, Vector3 position, Quaternion rotation);
GameObject Instantiate(GameObject obj, Vector3 position, Quaternion rotation, Transform father);
class InstantiateTest : MonoBehavior
{
	[SerilizedField]
	private GameObject bullet;
	
	void Start()
	{
		GameObject newBullet = Instantiate(bullet, transform.position, transform.rotation);
		newBullet.GetComponent<Bullet>().Shoot();
	}
}
  • 使用Destroy銷燬GameObject
void Destroy(GameObject obj);
void Destroy(GameObject obj, float delay);
class ExplosionEffectManager : MonoBehavior
{
	[SerilizedField]
	private GameObjcet explosionEffect;
	
	public void ShowEffect(Vector3 center)
	{
		GameObjcet newEffect = Instantiate(explosionEffect, center + Random.insideUnitSphere, transform.rotation);
		Destroy(newEffect, 5f);
	}
}
  • 使用物品池來管理物體

每次調用Instantiate都會申請新的內存空間,而每次Destroy都會將這些內存遺棄等待GC處理。對於經常生成又經常被摧毀的物品,如子彈、特效等,快速而大量的Instantiate和Destroy會導致大量的GC消耗。這些物體最好使用一個物品池來管理,物品池可以是棧也可以是隊列或其它的什麼結構,其根本思路在於通過改變activeSelf而非直接使用Destroy來實現類似銷燬的效果,以此減輕GC負擔。

下面的例子是用一個棧封裝的子彈池,其它腳本在創建或銷燬子彈時不再使用Instantiate和Destroy,轉而使用BulletPool.Born和BulletPool.Dies:

public class BulletPool
{
    public static Stack<GameObject> pool = new Stack<GameObjcet>();
    
    public static GameObject Born(GameObject bullet, Vector3 position)
    {
        if(pool.Count == 0)
        {
            GameObject obj = Instantiate(bullet, position, new Quaternion(0,0,0,0));
            obj.Initialize();
            return obj;
        }
        else
        {
            GameObject obj = pool.Pop();
            obj.SetActive(true);
            obj.Initialize();
            return obj;
        }
    }
    
    public static void Dies(GameObject bullet)
    {
        pool.Push(bullet);
        bullet.SetActive(false);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章