如何使用EventSystem检测多个/重叠GameObjects?

我试图实现的目标:牙刷应该出现在用户点击BoxCollider A内的任何地方,包括BoxCollider B内的空间。 但显然在B内部点击将不会显示牙刷(OnPointerDown未被触发)。

我尝试过:改变图层的顺序。

用户点击盒子对撞机A后显示牙刷,但如果用户点击盒子对撞机B – 牙刷将不会显示,这意味着不会触发OnPointerDown

我认为这是因为一个BoxCollider2D在另一个BoxCollider2D重叠。 在我的案例B ,我认为这是罪魁祸首,但我不知道如何解决它,或者是否有另一种方法来实现OnPointerDown

我正在使用Perspective相机。 但是在这个场景中,所有元素都在相同的z position ,即0.是否可以在每个相应的BoxCollider2D触发IPointerHander事件?

在此处输入图像描述

DragableObject.cs

该脚本附在牙刷上。 BoxCollider2D A也属于牙刷。

 public void OnPointerDown(PointerEventData eventData) { Debug.Log("pointer down"); if (GetComponent() == null) return; currentObject = GetComponent(); MeshRenderer renderer = GetComponent(); if (ShowOnTouch) ShowObject(); // Store original state originalPosition = transform.position; originalOrderLayer = renderer.sortingOrder; // Snap to mouse Vector3 newPos = cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 30)); newPos.z = 30; transform.position = newPos; if (BringToFront) { if (renderer != null) { renderer.sortingOrder = 90; } } ObjectActive.Invoke(); } 

TargetListener.cs

此脚本附加到BoxCollider2D B.

 public void OnPointerDown(PointerEventData eventData) { for (int i = 0; i < Affectors.Count; i++) { if (Affectors [i] == DragableObject.currentObject) { DragableObject.currentObject.OnEnterTarget(transform); ITriggerEffect[] iTrigger = GetComponents(); for (int j = 0; j < iTrigger.Length; j++) { Debug.Log("iTrigger enter"); Debug.Log(iTrigger [j]); iTrigger [j].Execute(eventData, PointerState.Down); } } else continue; } } 

如果我点击A牙刷会出现,除非我在B内点击。 这里是调试日志。

在此处输入图像描述

这是附加的BoxCollider2D ,它是*Toothbrush本身和dragable.cs脚本。

在此处输入图像描述


更新:感谢其他人的回答,问题对我来说变得更加清晰。 下面是BoxCollider2D A和BoxCollider2D B.它们都有大多数OnPointerHander脚本。 如何确保在相应的BoxCollider2D上触发所有BoxCollider2D

在此处输入图像描述

我遇到的问题:

  1. 当我的指针进入B时,触发AA OnPointerExit。
  2. 如果在B内单击, OnPointerDown仅在B触发但不在A上触发

EventSystem的优点之一是事件不通过GameObjects。 返回第一个被击中的。 虽然,看起来你不想要那样。 使EventSystem返回多个GameObjects变得复杂,

有两种解决方案:

1.获取 EventSystemOnPointerDownIPointerDownHandler )并使用旧学校的光线IPointerDownHandler系统。

Physics2D.RaycastAllPhysics2D.RaycastNonAlloc可以做到这一点。 出于性能原因,此示例将使用RaycastNonAlloc 。 这很容易。

仅附加到一个GameObject(空GameObject)

 public class HitAll : MonoBehaviour { //Detect up to 100 Objects const int raycastAmount = 100; RaycastHit2D[] result = new RaycastHit2D[raycastAmount]; void Update() { #if UNITY_IOS || UNITY_ANDROID if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began) { checkRaycast(Input.GetTouch(0).position); } #else if (Input.GetMouseButtonDown(0)) { checkRaycast(Input.mousePosition); } #endif } void checkRaycast(Vector2 mousePos) { Vector3 origin = Camera.main.ScreenToWorldPoint(mousePos); int hitCount = Physics2D.RaycastNonAlloc(origin, Vector2.zero, result, 200); Debug.Log(hitCount); for (int i = 0; i < hitCount; i++) { Debug.Log("Hit: " + result[i].collider.gameObject.name); } } } 

2.继续使用EventSystem但重新抛出事件。

首先,使用EventSystem.current.RaycastAll抛出raycast,然后使用ExecuteEvents.Execute手动调用该事件。

使用2D Collider附加到所有Physics2DRaycaster ,并确保将Physics2DRaycaster连接到相机

 public class ThroughEventScript : MonoBehaviour, IPointerDownHandler { public void OnPointerDown(PointerEventData eventData) { rethrowRaycast(eventData, eventData.pointerCurrentRaycast.gameObject); //DO STUFF WITH THE OBJECT HIT BELOW Debug.Log("Hit: " + eventData.pointerCurrentRaycast.gameObject.name); } void rethrowRaycast(PointerEventData eventData, GameObject excludeGameObject) { PointerEventData pointerEventData = new PointerEventData(EventSystem.current); pointerEventData.position = eventData.pressPosition; //pointerEventData.position = eventData.position;} //Where to store Raycast Result List raycastResult = new List(); //Rethrow the raycast to include everything regardless of their Z position EventSystem.current.RaycastAll(pointerEventData, raycastResult); //Debug.Log("Other GameObject hit"); for (int i = 0; i < raycastResult.Count; i++) { //Debug.Log(raycastResult[i].gameObject.name); //Don't Rethrow Raycayst for the first GameObject that is hit if (excludeGameObject != null && raycastResult[i].gameObject != excludeGameObject) { //Re-simulate OnPointerDown on every Object hit simulateCallbackFunction(raycastResult[i].gameObject); } } } //This causes functions such as OnPointerDown to be called again void simulateCallbackFunction(GameObject target) { PointerEventData pointerEventData = new PointerEventData(EventSystem.current); //pointerEventData.ra RaycastResult res = new RaycastResult(); res.gameObject = target; pointerEventData.pointerCurrentRaycast = res; ExecuteEvents.Execute(target, pointerEventData, ExecuteEvents.pointerDownHandler); } } 

这里的预期function是什么? 我怀疑你想让牙刷随时在口腔内咔哒一声显示?

  1. 如果是这样,那么解决这个问题的一种有趣方法是将“B”对象的排序调整到层次结构中的“A”对象之上,以便它们覆盖A碰撞区域。 这反过来将允许它们的相互作用阻止由每个’B’对撞机定义的碰撞区域下面定义的区域的相互作用。

    我们用这种方法做的是强迫OnPointerDown事件与’B’对撞机一起发生,然后才有机会与’A’对撞机发生碰撞。

请告诉我这是否有意义,并随时询问有关该方法的后续问题。

仍然不确定你的情况是否是我认为的,但这里是我提到的方法的video: 演示video