Unity UGUI不規則區域按鈕點選實現
阿新 • • 發佈:2021-06-10
如下圖 想要實現精確的點選不發生遮擋的情況
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_; privateSprite 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)); }); } }
如果感覺還是不太準確,可能還有那麼一點點偏移(偏下)
在設定一下就好,一般來說不設定的話也感覺不出來的(反正我是沒有感覺出來,哈哈哈)
記錄一下方便以後使用.