How to detect multiple/Overlapping GameObjects with the EventSystem?

前端 未结 2 1729
Happy的楠姐
Happy的楠姐 2021-01-25 03:06

What I try to achieve: Toothbrush should show up wherever the user clicks inside BoxCollider A, including space inside BoxCollider B

相关标签:
2条回答
  • 2021-01-25 03:16

    What is the intended functionality here? I'm suspecting that you want the toothbrush to display anytime a user clicks within the mouth space?

    1. If so, then one fun way to fix this would be to adjust the ordering of the 'B' objects to be above the 'A' object in the Hierarchy so that they overlay the A collision area. This in turn will allow their interaction to block the interaction of the area defined underneath the collision area define by each 'B' collider.

      What we're doing with this methodology is forcing the OnPointerDown event to occur with the 'B' collider before it has a chance to collide with the 'A' collider.

    Let me know if that makes sense and feel free to ask follow up questions about the method.

    Still not sure if your case is what I think it is, but here's a video of the method i mentioned: Demo Video

    0 讨论(0)
  • 2021-01-25 03:33

    One of the good and advantages of the EventSystem is that events don't go through GameObjects. The first one that is hit is returned. Although, it looks like you don't want that. It complicated to make EventSystem return multiple GameObjects,

    There two solutions for you:

    1.Get ride of EventSystem (OnPointerDown and IPointerDownHandler) and use the old school raycast system.

    Physics2D.RaycastAll and Physics2D.RaycastNonAlloc can do this. This example will use RaycastNonAlloc for performance reasons. It's very easy.

    Attach to only one GameObject(empty 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.Continue using EventSystem but rethrow the event.

    First, you throw raycast with EventSystem.current.RaycastAll then you manually invoke the event with ExecuteEvents.Execute.

    Attach to all the GameObject with 2D Collider and make sure that Physics2DRaycaster is attached to the camera:

    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> raycastResult = new List<RaycastResult>();
    
            //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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题