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即可。

相关推荐
q***82916 小时前
如何使用C#与SQL Server数据库进行交互
数据库·c#·交互
Hody9111 小时前
【XR开发系列】Unity第一印象:编辑器界面功能布局介绍(六大功能区域介绍)
unity·编辑器·xr
q***180612 小时前
如何使用C#与SQL Server数据库进行交互
数据库·c#·交互
程序猿_极客12 小时前
JavaScript 的 Web APIs 入门到实战全总结(day7):从数据处理到交互落地的全链路实战(附实战案例代码)
开发语言·前端·javascript·交互·web apis 入门到实战
lrh302518 小时前
Custom SRP - 14 Multiple Cameras
unity·渲染管线·srp
q***25121 小时前
开源模型应用落地-FastAPI-助力模型交互-进阶篇-中间件(四)
开源·交互·fastapi
AA陈超1 天前
虚幻引擎5 GAS开发俯视角RPG游戏 P07-11 实现自动运行
c++·游戏·ue5·游戏引擎·虚幻
Hody912 天前
【XR开发系列】Unity下载与安装详细教程(UnityHub、Unity)
unity·游戏引擎·xr
程序员正茂2 天前
在Unity3d中使用Netly开启TCP服务
unity·tcp·netly
Little丶Seven2 天前
使用adb获取安卓模拟器日志
android·unity·adb·个人开发