如何使用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
?
我遇到的问题:
- 当我的指针进入
B
时,触发A
上A
OnPointerExit。 - 如果在
B
内单击,OnPointerDown
仅在B
触发但不在A
上触发
EventSystem的优点之一是事件不通过GameObjects。 返回第一个被击中的。 虽然,看起来你不想要那样。 使EventSystem返回多个GameObjects变得复杂,
有两种解决方案:
1.获取 EventSystem
( OnPointerDown
和IPointerDownHandler
)并使用旧学校的光线IPointerDownHandler
系统。
Physics2D.RaycastAll
和Physics2D.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是什么? 我怀疑你想让牙刷随时在口腔内咔哒一声显示?
-
如果是这样,那么解决这个问题的一种有趣方法是将“B”对象的排序调整到层次结构中的“A”对象之上,以便它们覆盖A碰撞区域。 这反过来将允许它们的相互作用阻止由每个’B’对撞机定义的碰撞区域下面定义的区域的相互作用。
我们用这种方法做的是强迫OnPointerDown事件与’B’对撞机一起发生,然后才有机会与’A’对撞机发生碰撞。
请告诉我这是否有意义,并随时询问有关该方法的后续问题。
仍然不确定你的情况是否是我认为的,但这里是我提到的方法的video: 演示video