「Unity3D」(2)射線檢測與EventSystem判斷UI點擊

按照傳統的做法,EventSystem用做UI的事件處理,射線檢測用做非UI碰撞的判斷,但需要手動添加Collider。EventSystem使用了,GraphicRaycaster組件來檢測UI元素的事件,其底層還是使用了射線來檢測碰撞的。

雖然UI組件勾選Raycast Target能夠按照層級關係阻擋穿透,但其阻擋的是UI組件之間的射線穿透。GraphicRaycaster的源碼中有 var foundGraphics = GraphicRegistry.GetGraphicsForCanvas(canvas);只會判斷Graphic對象,這是UI元素的父類。

那麼,手動的射線就會有穿透UI的問題,所以我們需要一個能判斷UI元素被點擊的需求,看代碼封裝。

/// <summary>
/// Whether touch down or mouse button down over UI GameObject.
/// </summary>
public static bool IsClickDownOverUI()
{
    #if UNITY_EDITOR
    if (Input.GetMouseButtonDown(0))
    #else
    if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Began)
    #endif
    {
        #if UNITY_EDITOR
        if (EventSystem.current.IsPointerOverGameObject())
        #else
        if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
        #endif
        {
            return true;
        }
    }

    return false;
}


/// <summary>
/// Whether touch up or mouse button up over UI GameObject.
/// </summary>
public static bool IsClickUpOverUI()
{
    #if UNITY_EDITOR
    if (Input.GetMouseButtonUp(0))
    #else
    if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Ended)
    #endif
    {
        #if UNITY_EDITOR
        if (EventSystem.current.IsPointerOverGameObject())
        #else
        if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
        #endif
        {
            return true;
        }
    }

    return false;
}
  • 這裏給出了click down和up的分別判斷。
  • 封裝了鼠標和觸摸的判斷。
  • 函數返回true表明UI元素被點中。

射線檢測

原理是,根據屏幕的點擊,手動發射一條射線,然後獲取碰撞的對象。

/// <summary>
/// Raycast a ray from touch up or mouse button up and test whether hit transform,
/// if hit transform return it or return null.
/// [isCheckHitUI] Whether check ray hit ui transform,
/// if hit ui just return null.
/// </summary>
public static Transform Raycast(bool isCheckHitUI = true)
{
    #if UNITY_EDITOR
    if (Input.GetMouseButtonUp(0))
    {
        if (isCheckHitUI)
        {
            if (EventSystem.current.IsPointerOverGameObject())
            {
                return null;
            }
        }

        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    #else
    if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Ended)
    {
        if (isCheckHitUI)
        {
            if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
            {
                return null;
            }
        }

        Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
    #endif

        RaycastHit hit;

        if (Physics.Raycast(ray, out hit))
        {
            // or hit.collider.transform;
            return hit.transform; 
        }
    }

    return null;
}
  • isCheckHitUI 表示UI是否會阻擋射線的選擇,點中UI直接返回null。
  • Camera.main 代表的是擁有Tag MainCamera的Camera。
  • 封裝了鼠標和觸摸的操作。
  • 射線點中了帶有Collider的非UI元素,就會返回其Transform。

「老問題了,更好的實踐,看後續」

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章