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

时间:2017-09-13 02:50:14

标签: c# unity3d

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

我尝试过的方法:更改图层的顺序。

用户在框架对撞机A内点击后会显示牙刷,但如果用户点击框对撞机B - 牙刷将不会显示,这意味着OnPointerDown未被触发。

我认为这是因为一个BoxCollider2D与另一个BoxCollider2D重叠。在BA,我认为这是罪魁祸首,但我不知道如何解决它,或者是否有其他方法可以实现OnPointerDown

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

enter image description here

DragableObject.cs

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

public void OnPointerDown(PointerEventData eventData)
{
    Debug.Log("pointer down");

    if (GetComponent<DragableObject>() == null)
        return;

    currentObject = GetComponent<DragableObject>();

    MeshRenderer renderer = GetComponent<MeshRenderer>();

    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<ITriggerEffect>();

            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内点击。这里是调试日志。

enter image description here

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

enter image description here

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

enter image description here

我遇到的问题:

  1. 当我的指针输入A时,会触发B上的OnPointerExit。
  2. 如果在B内点击,OnPointerDown仅在B而非A点击

2 个答案:

答案 0 :(得分:2)

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

有两种解决方案:

1 。获取EventSystemOnPointerDownIPointerDownHandler)并使用旧式学校光线投影系统。

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

仅附加到一个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投射光线投射,然后使用ExecuteEvents.Execute手动调用该事件。

使用2D Collider附加到所有GameObject,并确保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> 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);
    }
}

答案 1 :(得分:0)

这里的预期功能是什么?我怀疑你是否希望用户在口腔内咔嗒一声显示牙刷?

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

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

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

    仍然不确定你的情况是否符合我的想法,但这里是我提到的方法的视频:Demo Video