Unity镂空图像做法

问题和解决方案

现在要完成一个需求,即镂空中间部分的image,外围image可以定义颜色并可选屏蔽点击,而中间的image需要透明且可以穿透,必须不能屏蔽点击。

由此拆分成了两个问题:

1.定义外围image颜色,内部image不绘制

2.外围image可选raycast target,内围image不受raycast target标志位影响。

由此可继承Graphic和ICanvasRaycastFilter来分别实现这个目标。

OnPopulateMesh

继承graphic脚本后重写Graphic.OnPopulateMesh函数,可以在里面去完成重绘操作。与其说不绘制中间的这部分范围,不如说只绘制外面的范围。用VertexHelper.AddTriangle即可完成简单的三角形绘制工作,由此可以根据外image顶点和内image顶点来完成八个三角形的构建绘制工作,如下示意图所示:

因此代码如下所示:

cs 复制代码
protected override void OnPopulateMesh(VertexHelper vh)
{
    if (innerTrans == null)
    {
        base.OnPopulateMesh(vh);
        return;
    }

    vh.Clear();

    UIVertex vertex = UIVertex.simpleVert;
    vertex.color = color;

    //0 outer左下角
    vertex.position = new Vector3(outerLeftBottom.x, outerLeftBottom.y);
    vh.AddVert(vertex);
    //1 outer左上角
    vertex.position = new Vector3(outerLeftBottom.x, outerRightTop.y);
    vh.AddVert(vertex);
    //2 outer右上角
    vertex.position = new Vector3(outerRightTop.x, outerRightTop.y);
    vh.AddVert(vertex);
    //3 outer右下角
    vertex.position = new Vector3(outerRightTop.x, outerLeftBottom.y);
    vh.AddVert(vertex);
    //4 inner左下角
    vertex.position = new Vector3(innerLeftBottom.x, innerLeftBottom.y);
    vh.AddVert(vertex);
    //5 inner左上角
    vertex.position = new Vector3(innerLeftBottom.x, innerLeftTop.y);
    vh.AddVert(vertex);
    //6 inner右上角
    vertex.position = new Vector3(innerLeftTop.x, innerLeftTop.y);
    vh.AddVert(vertex);
    //7 inner右下角
    vertex.position = new Vector3(innerLeftTop.x, innerLeftBottom.y);
    vh.AddVert(vertex);

    //绘制三角形
    vh.AddTriangle(0, 1, 4);
    vh.AddTriangle(1, 4, 5);
    vh.AddTriangle(1, 5, 2);
    vh.AddTriangle(2, 5, 6);
    vh.AddTriangle(2, 6, 3);
    vh.AddTriangle(6, 3, 7);
    vh.AddTriangle(4, 7, 3);
    vh.AddTriangle(0, 4, 3);
}

IsRaycastLocationValid

继承ICanvasRaycastFilter接口,重写内置的IsRaycastLocationValid即可:

cs 复制代码
return innerTrans == null || !RectTransformUtility.RectangleContainsScreenPoint(innerTrans, sp, eventCamera);

整体代码:

cs 复制代码
public class HollowOutMask : Graphic, ICanvasRaycastFilter
{
    [Header("镂空区域")]
    public RectTransform innerTrans;
    RectTransform outerTrans;//背景区域

    Vector3 innerLeftTop = Vector3.zero;//镂空区域的右上角坐标
    Vector3 innerLeftBottom = Vector3.zero;//镂空区域的左下角坐标
    Vector2 outerRightTop = Vector2.zero;//背景区域的右上角坐标
    Vector2 outerLeftBottom = Vector2.zero;//背景区域的左下角坐标

    protected override void Awake()
    {
        base.Awake();

        outerTrans = GetComponent<RectTransform>();
    }

    protected override void OnPopulateMesh(VertexHelper vh)
    {
        if (innerTrans == null)
        {
            base.OnPopulateMesh(vh);
            return;
        }

        vh.Clear();

        UIVertex vertex = UIVertex.simpleVert;
        vertex.color = color;

        //0 outer左下角
        vertex.position = new Vector3(outerLeftBottom.x, outerLeftBottom.y);
        vh.AddVert(vertex);
        //1 outer左上角
        vertex.position = new Vector3(outerLeftBottom.x, outerRightTop.y);
        vh.AddVert(vertex);
        //2 outer右上角
        vertex.position = new Vector3(outerRightTop.x, outerRightTop.y);
        vh.AddVert(vertex);
        //3 outer右下角
        vertex.position = new Vector3(outerRightTop.x, outerLeftBottom.y);
        vh.AddVert(vertex);
        //4 inner左下角
        vertex.position = new Vector3(innerLeftBottom.x, innerLeftBottom.y);
        vh.AddVert(vertex);
        //5 inner左上角
        vertex.position = new Vector3(innerLeftBottom.x, innerLeftTop.y);
        vh.AddVert(vertex);
        //6 inner右上角
        vertex.position = new Vector3(innerLeftTop.x, innerLeftTop.y);
        vh.AddVert(vertex);
        //7 inner右下角
        vertex.position = new Vector3(innerLeftTop.x, innerLeftBottom.y);
        vh.AddVert(vertex);

        //绘制三角形
        vh.AddTriangle(0, 1, 4);
        vh.AddTriangle(1, 4, 5);
        vh.AddTriangle(1, 5, 2);
        vh.AddTriangle(2, 5, 6);
        vh.AddTriangle(2, 6, 3);
        vh.AddTriangle(6, 3, 7);
        vh.AddTriangle(4, 7, 3);
        vh.AddTriangle(0, 4, 3);
    }

    /// <summary>
    /// 过滤掉射线检测
    /// </summary>
    public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
    {
        return innerTrans == null || !RectTransformUtility.RectangleContainsScreenPoint(innerTrans, sp, eventCamera);
    }

    private bool NeedUpdateBounds()
    {
        if (innerTrans == null)
        {
            return false;
        }

        Bounds bounds = RectTransformUtility.CalculateRelativeRectTransformBounds(outerTrans, innerTrans);
        if (innerLeftTop == bounds.max
            && innerLeftBottom == bounds.min
            && outerRightTop == outerTrans.rect.max
            && outerLeftBottom == outerTrans.rect.min)
        {
            return false;
        }
        return true;
    }

    private void UpdateBounds()
    {
        if (innerTrans == null)
        {
            return;
        }
        Bounds bounds = RectTransformUtility.CalculateRelativeRectTransformBounds(outerTrans, innerTrans);
        innerLeftTop = bounds.max;
        innerLeftBottom = bounds.min;
        outerRightTop = outerTrans.rect.max;
        outerLeftBottom = outerTrans.rect.min;
    }

    private void Update()
    {
        if(NeedUpdateBounds())
        {
            UpdateBounds();
            SetAllDirty();
        }
    }
}

注意我们需要赋值一个innerImage transform给这个graphic脚本,作为内image大小和位置控制;而外image则调整挂载这个graphic脚本本身的transform即可。

相关推荐
变身缎带25 分钟前
Unity中较为完善的NetworkManager
unity·游戏引擎
作孽就得先起床35 分钟前
unity webGL导出.glb模型
unity·c#·游戏引擎·webgl
世人万千丶3 小时前
鸿蒙跨端框架 Flutter 学习 iverpod 实战:超越 Provider 的响应式状态管理
学习·flutter·华为·交互·harmonyos·鸿蒙
心前阳光3 小时前
Unity发布运行在PICO4的安卓程序
android·unity·游戏引擎
孟无岐3 小时前
【Laya】Scene3D 介绍
typescript·游戏引擎·游戏程序·laya
sunfove3 小时前
Python制作小游戏:用线性代数思想构建 2048 游戏引擎
python·线性代数·游戏引擎
孟无岐3 小时前
【Laya】Sprite3D 介绍
typescript·游戏引擎·游戏程序·laya
Howrun7774 小时前
虚幻引擎_创建组件
游戏引擎·虚幻
Jessica巨人4 小时前
pico相关插件介绍
unity
leo__5204 小时前
基于模糊控制的MATLAB避障算法实现
算法·matlab·unity