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));
        });
    }
}

 

如果感觉还是不太准确,可能还有那么一点点偏移(偏下)

 在设置一下就好,一般来说不设置的话也感觉不出来的(反正我是没有感觉出来,哈哈哈)

 

 

 

记录一下方便以后使用.

 Life is not kind,Anguish is inevitable

posted @ 2021-06-10 16:50  剑起苍穹  阅读(644)  评论(0编辑  收藏  举报
/*鼠标点击特效*/