Unity3D:UGUI Button不规则图片点击事件过滤器

Unity UGUI按钮点击的时候,由于有些时候会有透明部分,当点击按钮式总会出现透明部分挡住其他按钮的时候,为了避免这种情况,就有了今天的这个组件。

基本原理就是,获取图片点击位置的像素点颜色,判断是否透明,然后重写IsRaycastLocationValid接口,一开始我用鼠标点击位置,去获取像素点,但是好像没有效果,后来查了资料才知道,图片像素点坐标和鼠标图片上点击的位置,坐标系是不一样的,而且图片的像素坐标还会根据image的大小有变化。

using UnityEngine;
using UnityEngine.UI;

public class ImageRaycastFilter : 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>();
    }

    /// <summary>
    /// 重写IsRaycastLocationValid接口
    /// </summary>
    /// <param name="sp"></param>
    /// <param name="eventCamera"></param>
    /// <returns></returns>
    public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
    {
        _sprite = _image.sprite;

        var rectTransform = (RectTransform)transform;
        Vector2 localPositionPivotRelative;
        RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)transform, sp, eventCamera, 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;
        }

        // 如果texture导入过程报错,则提示错误原因
        try
        {
            return _sprite.texture.GetPixel(x, y).a > alpahThreshold;
        }
        catch (UnityException e)
        {
            Debug.LogError("Mask texture not readable, set your sprite to Texture Type 'Advanced' and check 'Read/Write Enabled'" + e.Message);
            return false;
        }
    }
}

上面就是代码,只要将这个代码放到Button上就可以了,其中那个alpahThreshold是判断是否可以点击的透明度,0是全透明的时候不能点击,0.5是半透明的时候不能点击。

最重要的一点就是图片要设置成可读写

You May Also Like

About the Author: 大腿Plus

发表评论