本篇没有推荐,纯手工.....
目的:测试在多层对象中,碰撞消息的传递方式
先附上实验总结(RigidBody同样适用):
* 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即可)