9

UGUI中几种不规则按钮的实现方式

 3 years ago
source link: http://baizihan.me/2018/03/non-rectangular-button/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
2018-03-03 •

UGUI中几种不规则按钮的实现方式

UGUI中的按钮默认是矩形的,若要实现非矩形按钮该怎么做呢?比如这样的按钮:

image

本文将介绍两种实现方式供大家选择。

使用alphaHitTestMinimumThreshold

Image类的alphaHitTestMinimumThreshold是一个浮点值,Raycast检测时只有图片中高于该值的部分会抛出点击事件。因此我们可以使用一张alpha通道的值高于该设置值的Sprite用于自定义按钮的点击相应区域。

我们准备一张点击区域alpha高于某值,非点击区域alpha低于某值的Sprite用于Button的Image组件的Sprite。然后给这个Button挂上如下脚本组件即可:

using UnityEngine;
using UnityEngine.UI;

public class AlphaButton : MonoBehaviour
{
    public float alphaThreshold = 0.1f;

    void Start()
    {
        GetComponent<Image>().alphaHitTestMinimumThreshold = alphaThreshold;
    }
}

但这种方法有几个问题:

  1. 由于是代码中需要读取图片的alpha值用于比较,因此图片在导入时需要开启Readable/Write Enable,这样会使运行时贴图大小翻倍,内存中会额外存储一份贴图数据,增大内存开销。

  2. 如果是点击区域内部需要有一些低于设置值的透明样式则无法满足。

  3. 点击区域的调整需要修改图片资源,十分不便。

如果可以接受这些缺点,可以使用这个方法。

使用IsRaycastLocationValid

通过继承Image并重写IsRaycastLocationValid方法可以自定义按钮的可点击区域。

将如下代码放置于项目中:

using UnityEngine;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;

#endif
[RequireComponent(typeof(PolygonCollider2D))]
public class NonRectangularButtonImage : Image
{
    private PolygonCollider2D areaPolygon;

    protected NonRectangularButtonImage()
    {
        useLegacyMeshGeneration = true;
    }

    private PolygonCollider2D Polygon
    {
        get
        {
            if (areaPolygon != null)
                return areaPolygon;

            areaPolygon = GetComponent<PolygonCollider2D>();
            return areaPolygon;
        }
    }

    protected override void OnPopulateMesh(VertexHelper vh)
    {
        vh.Clear();
    }

    public override bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
    {
        return Polygon.OverlapPoint(eventCamera.ScreenToWorldPoint(screenPoint));
    }

#if UNITY_EDITOR
    protected override void Reset()
    {
        base.Reset();
        transform.localPosition = Vector3.zero;
        var w = rectTransform.sizeDelta.x * 0.5f + 0.1f;
        var h = rectTransform.sizeDelta.y * 0.5f + 0.1f;
        Polygon.points = new[]
        {
            new Vector2(-w, -h),
            new Vector2(w, -h),
            new Vector2(w, h),
            new Vector2(-w, h)
        };
    }
#endif
}
#if UNITY_EDITOR
[CustomEditor(typeof(NonRectangularButtonImage), true)]
public class CustomRaycastFilterInspector : Editor
{
    public override void OnInspectorGUI()
    {
    }
}

public class NonRectAngularButtonImageHelper
{
    [MenuItem("GameObject/UI/NonRectangularButtonImage")]
    public static void CreateNonRectAngularButtonImage()
    {
        var goRoot = Selection.activeGameObject;
        if (goRoot == null)
            return;

        var button = goRoot.GetComponent<Button>();

        if (button == null)
        {
            Debug.Log("Selecting Object is not a button!");
            return;
        }

        // 关闭原来button的射线检测
        var graphics = goRoot.GetComponentsInChildren<Graphic>();
        foreach (var graphic in graphics)
        {
            graphic.raycastTarget = false;
        }

        var polygon = new GameObject("NonRectangularButtonImage");
        polygon.AddComponent<PolygonCollider2D>();
        polygon.AddComponent<NonRectangularButtonImage>();
        polygon.transform.SetParent(goRoot.transform, false);
        polygon.transform.SetAsLastSibling();
    }
}

#endif

这段代码大部分参考自雨松大神的这篇文章:

UGUI研究院之不规则按钮的响应区域(十四)

还额外写了一个自动添加组件和设置raycastTarget属性的菜单项。创建完一个普通的按钮后,右键执行命令:

image

这将自动创建一个名为“NonRectangularButtonImage”的子节点,并添加一个同名的脚本组件和一个PolygonCollider2D组件。编辑PolygonCollider2D组件即可设置按钮的点击区域,调整起来也十分方便,既简单又节省内存。

我的Github中这两种方式都有实现,供大家参考:

image

共三组按钮,点击后可以在Console窗口中看到响应Log。

第一组是没有任何处理的普通按钮,由于在Hierarchy中RightButton在下,点击Left的右下角还是右边按钮响应,用于对照。

第二组使用了设置alphaHitTestMinimumThreshold的方式。

第三组使用了重写IsRaycastLocationValid的方式,并故意调整了Button在Hierarchy中的顺序。

如果可以,也希望大家点个Star。

使用alphaHitTestMinimumThreshold的方式

UGUI研究院之不规则按钮的响应区域(十四)

使用mask的方式

Image.alphaHitTestMinimumThreshold

ICanvasRaycastFilter.IsRaycastLocationValid

用支付宝请我喝咖啡
alipay.png
用微信请我吃辣条
wechat.png
最近的文章

2018年终总结2019新年展望

年终总结2018年如同一辆云霄飞车呼啸而过,回顾一年,感慨良多,乘着元旦来写写总结吧。18年年初我还在上海,租着10几平的单间,每天早上坐公交转地铁花1个半小时上班。2月份过年时带妻子第一次回了四川广安的老家,第一次吃广安夜间街边的小摊烧烤。锡纸煨烤的脑花成了她难忘的美食。烤脑花刚端上桌,冒着滋滋热气的样子,此时想来已有点发馋。因是过年,兄弟姊妹难得相聚,还趁着假期到美食遍地的成都旅游。逛了宽窄巷子,武侯祠和春熙路。年前已在备孕,回到上海不久,一天晚上用验孕棒发现呈阳性后,我又出门两次买了...…

2019-01-01 • 继续阅读
更早的文章

使用Mono.Cecil实现IL代码注入

前言腾讯开源的Unity热更解决方案xLua有一个非常吸引人的特性就是Hotfix,其原理是使用Mono.Cecil库对进行C#层编译出来的dll程序集进行IL代码注入。其作者也在知乎的回答中简单说明了原理:如何评价腾讯在Unity下的xLua(开源)热更方案? - 车雄生的回答 - 知乎如果你需要在自己的lua方案中添加这样的Hotfix特效,那可以考虑用这个方法。本文的目的就旨在演示说明Mono.Cecil库的一些基本用法,希望可以帮到你。本文首发于我的个人博客,示例工程在我的gith...…

2017-11-18 • 继续阅读

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK