1. 程式人生 > 其它 >Unity UGUI不規則區域按鈕點選實現

Unity UGUI不規則區域按鈕點選實現

如下圖 想要實現精確的點選不發生遮擋的情況

UGUI 想要只點擊錶盤有反應的效果 (點選箭頭處沒有反應)

原理:精靈畫素檢測

UGUI在處理控制元件是否被點選的時候,主要是根據IsRaycastLocationValid這個方法的返回值來進行判斷的,而這個方法用到的基本原理則是判斷指定點對應畫素的RGBA數值中的Alpha是否大於某個指定臨界值。

例如,我們知道半透明通常是指Alpha=0.5,而對一個字尾名為png格式的圖片來說半透明或者完全透明的區域理論上不應該被響應的,所以根據這個原理,我們只需要設定一個透明度的臨界值,然後對當前滑鼠位置對應的畫素進行判斷就可以了

重要的一點

下面是重寫IsRaycastLocationValid 的方法直接掛在按鈕上即可

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 不規則按鈕點選  核心程式碼
/// </summary>
[RequireComponent(typeof(Image))]
public class BuGuiZhe : MonoBehaviour, ICanvasRaycastFilter
{
     
        private Image image_;
        private
Sprite sprite_; [Tooltip("設定Sprite響應的Alpha閾值")] //工具功能提示 [Range(0, 0.5f)] public float alpahThreshold = 0.5f; void Start() { image_ = GetComponent<Image>(); } /// 重寫IsRaycastLocationValid介面 public bool IsRaycastLocationValid(Vector2 vtor2, Camera main_Camera) { sprite_
= image_.sprite; var rectTransform = (RectTransform)transform; Vector2 localPositionPivotRelative; RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)transform, vtor2, main_Camera, out localPositionPivotRelative); // 轉換為以螢幕左下角為原點的座標系 var localPosition = new Vector2(localPositionPivotRelative.x + rectTransform.pivot.x * rectTransform.rect.width, localPositionPivotRelative.y + rectTransform.pivot.y * rectTransform.rect.height); var spriteRect = sprite_.textureRect; var maskRect = rectTransform.rect; var x = 0; var y = 0; // 轉換為紋理空間座標 switch (image_.type) { case Image.Type.Sliced: { var border = sprite_.border; // x 軸裁剪 if (localPosition.x < border.x) { x = Mathf.FloorToInt(spriteRect.x + localPosition.x); } else if (localPosition.x > maskRect.width - border.z) { x = Mathf.FloorToInt(spriteRect.x + spriteRect.width - (maskRect.width - localPosition.x)); } else { x = Mathf.FloorToInt(spriteRect.x + border.x + ((localPosition.x - border.x) / (maskRect.width - border.x - border.z)) * (spriteRect.width - border.x - border.z)); } // y 軸裁剪 if (localPosition.y < border.y) { y = Mathf.FloorToInt(spriteRect.y + localPosition.y); } else if (localPosition.y > maskRect.height - border.w) { y = Mathf.FloorToInt(spriteRect.y + spriteRect.height - (maskRect.height - localPosition.y)); } else { y = Mathf.FloorToInt(spriteRect.y + border.y + ((localPosition.y - border.y) / (maskRect.height - border.y - border.w)) * (spriteRect.height - border.y - border.w)); } } break; case Image.Type.Simple: default: { // 轉換為統一UV空間 x = Mathf.FloorToInt(spriteRect.x + spriteRect.width * localPosition.x / maskRect.width); y = Mathf.FloorToInt(spriteRect.y + spriteRect.height * localPosition.y / maskRect.height); } break; } try { return sprite_.texture.GetPixel(x, y).a > alpahThreshold; } catch (UnityException e) { Debug.LogError("請檢查圖片設定是不是已經勾選: Advanced/Read/Write Enabled'" + e.Message); // 如果texture匯入過程報錯,則刪除元件 //Destroy(this); return false; } } }

然後在按鈕上繫結方法進行測試 (測試指令碼如下)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Btn_coner : MonoBehaviour
{
    private Button btn;
    int sdsd = 0;
    // Start is called before the first frame update
    void Start()
    {
        btn = this.GetComponent<Button>();
        btn.onClick.AddListener(delegate {

            sdsd++;
            Debug.Log(string.Format("點選了{0}次", sdsd));
        });
    }
}

如果感覺還是不太準確,可能還有那麼一點點偏移(偏下)

在設定一下就好,一般來說不設定的話也感覺不出來的(反正我是沒有感覺出來,哈哈哈)

記錄一下方便以後使用.