Unity3D實驗,關於碰撞消息的機制

本篇沒有推薦,純手工.....

目的:測試在多層對象中,碰撞消息的傳遞方式

先附上實驗總結(RigidBody同樣適用):

     *      a、消息的接受者爲 RigidBody2D,如果本層沒有 RigidBody2D,向上(父)尋找
     *      b、消息的處理方式如下:遞歸調用接受消息的 RigidBody2D 的處理以及其子對象的處理,
     *          直到觸發觸發該碰撞的對象層次(深度優先,只調用觸發碰撞的對象的父對象的處理)
     *      c、那麼被處理者:爲觸發碰撞的對象(只要是包含Collider即可)

實驗必備腳本有兩個:打印碰撞消息,和鼠標添加碰撞體

// 打印碰撞消息
public class TriggerCollidePrinter : MonoBehaviour
    {
        private void OnTriggerEnter2D(Collider2D collision)
        {
            Debug.LogFormat("{0} meet {1}, trigger enter.", gameObject.name , collision.gameObject.name);
        }

        private void OnTriggerExit2D(Collider2D collision)
        {
            Debug.LogFormat("{0} through {1}, trigger exit.", gameObject.name, collision.gameObject.name);
        }

        private void OnCollisionEnter2D(Collision2D collision)
        {
            Debug.LogFormat("{0} meet {1}, collision enter.", gameObject.name, collision.gameObject.name);
        }

        private void OnCollisionExit2D(Collision2D collision)
        {
            Debug.LogFormat("{0} through {1}, collision exit.", gameObject.name, collision.gameObject.name);
        }
    }

// 鼠標添加碰撞體
public class PrefabCreaterByMouse : MonoBehaviour {

        public GameObject prefab; // 碰撞體的預製體,含 ColliderBox2D, RigidBody2D, ColliderPrinter
        public int index = 0;

        void Update()
        {
            //  左鍵點擊增加
            if(Input.GetMouseButtonDown(0))
            {
                GameObject go = GameObject.Instantiate(prefab);
                go.name = string.Format("ColliderPrinter{0}", index++);
                Vector3 pos3 = Camera.main.ScreenToWorldPoint( Input.mousePosition );
                pos3.z = go.transform.localPosition.z;
                go.transform.localPosition = pos3;
            }
            //  Ctrl + 右鍵點擊刪除全部
            else if (Input.GetMouseButtonDown(1) && Input.GetKey(KeyCode.LeftControl))
            {
                RaycastHit2D[] hits = Physics2D.RaycastAll(
                    Camera.main.ScreenToWorldPoint(Input.mousePosition),
                    Vector2.zero,
                    Mathf.Infinity);
                foreach (var hit in hits)
                    //GameObject.DestroyImmediate(hit.collider.gameObject);
                    GameObject.Destroy(hit.collider.gameObject);
            }
            //  右鍵點擊刪除
            else if (Input.GetMouseButtonDown(1))
            {
                RaycastHit2D hit = Physics2D.Raycast(
                    Camera.main.ScreenToWorldPoint(Input.mousePosition),
                    Vector2.zero,
                    Mathf.Infinity);
                if (hit.collider != null)
                    GameObject.Destroy(hit.collider.gameObject);
                else
                    Debug.Log("Right button down, not hit;");
            }
        }

    }

所有碰撞體,均添加到默認層,不存在層之間的屏蔽問題。

1、第一次,僅測試啓動:

對象結構和打印結果如下(由於命名很清楚,就不廢話解釋了,只說明一點:father和Child位置重疊,但是多個 father 之間位置是分開的):


這次實驗說明的問題:

1、啓動時候,是會觸發碰撞消息的

2、father4有Rigid而Child4沒有Rigid,理論上,此時應該向上搜索到father4,

然而father4不能和自己碰撞,因而只有father4處理Child4

3、而father3和child3相互碰撞,均由自己的rigidbody接受消息,並交由printer打印

2、father1/child1:

實驗方法:使用鼠標在father1和Child1重疊的部分左鍵點擊,增加一個ColliderPrinter

結構和打印結果如下:

這次實驗說明的問題:

1、ColliderPrinter和Child1的碰撞爲前三行

ColliderPrinter正常打印(第一行),過

father1的rigid接收到消息後,向下傳遞,father1和child1分別處理colliderPrinter

2、father1和ColliderPrinter相互處理,正常,過

3、father2/child2:

結構和打印結果如下:

這次實驗說明的問題:

1、前兩行是Child2的碰撞處理,與前一次實驗少了Child2的打印,因爲去除了打印的腳本

2、後兩行是father和ColliderPrinter的碰撞,正常,過

4、father3/child3:

結構和打印結果如下:

這次實驗說明的問題:

1、消息上面兩行和下面兩行分別是ColliderPrinter和Child3的相互處理以及ColliderPrinter和father3的相互處理

2、由於Child3自己有Rigid,所以消息自己接受,自己處理,相當於Child3和Father3是兩個無關的東西

5、father4/child4:

結構和打印結果如下:


這次實驗說明的問題:

Child4沒有Printer,消息並不會因此傳遞給father,而是直接截獲掉

6、小結和補充說明:

1、在father/Child重疊的情況下,並不一定都是先處理Child的碰撞再處理father的碰撞,筆者多次試驗過程中也是有順序隨機的現象

2、father1/child1和father2/child2應該是最常用的情況

在ColliderPrinter與Child1碰撞的過程中,一定先調用Child1,再調用father1,遞歸調用是深度優先的

3、在child上掛Rigid的情況,如果只修改father的rigid的velocity來完成運動的話,會造成father和child運動脫節

有兩種方法解決:

a、每個週期給child的位置更新

b、father速度更新的時候,給child的速度同時更新

計時可以解決,一般也都不這麼使用,除非特殊功能需要

4、最後再把結論嘮一遍:

     *      a、消息的接受者爲 RigidBody2D,如果本層沒有 RigidBody2D,向上(父)尋找
     *      b、消息的處理方式如下:遞歸調用接受消息的 RigidBody2D 的處理以及其子對象的處理,
     *          直到觸發觸發該碰撞的對象層次(深度優先,只調用觸發碰撞的對象的父對象的處理)
     *      c、那麼被處理者:爲觸發碰撞的對象(只要是包含Collider即可)



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