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即可)



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