How to detect multiple/Overlapping GameObjects with the EventSystem?

前端 未结 2 1728
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: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 = 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);
        }
    }
    

提交回复
热议问题