(七)自定义Graphic

1.前言

基于CanvasRenderer实现一个自定义(破产版)的graphic。以往自定义ui形式时,可以新建脚本继承Graphic,通过重写虚方法来实现一些特殊功能。此文则从CanvasRenderer的层面,实现一个Graphic。实现时采用的方法则是SetMesh、SetMaterial以及SetMaterial等方法。完整代码在文末。

2.实现Graphic

2.1 设置网格

网格为ui的平面mesh,可以参考uimesh。由于我们需要网格随着RectTransform的变化而更改,所以此处给出两个网格的实现,一个是固定网格,一个是动态更改网格。生成mesh后通过canvasRender.SetMesh(UIMesh);设置网格。

2.1.1 固定网格

如下代码网格是固定大小:

    public Mesh UIMesh
    {
        get
        {
            if (mesh && useLastMesh)
                return mesh;

            mesh = new Mesh();
            VertexHelper vh = new VertexHelper();

            var r = GetPixelAdjustedRect();
            var v = new Vector4(r.x, r.y, r.x + r.width, r.y + r.height);

            vh.AddVert(new Vector3(-50, -50), Color.white, new Vector2(0, 0));
            vh.AddVert(new Vector3(50, -50), Color.red, new Vector2(1, 0));
            vh.AddVert(new Vector3(50, 50), Color.red, new Vector2(1, 1));
            vh.AddVert(new Vector3(-50, 50), Color.white, new Vector2(0, 1));

            vh.AddTriangle(0, 1, 2);
            vh.AddTriangle(0, 2, 3);

            vh.FillMesh(mesh);

            return mesh;
        }
    }

2.1.2 动态网格

网格根据rect的大小动态更改。通过GetPixelAdjustedRect来获取rect的大小,然后根据获取到的大小数据生成网格。

    public Mesh UIMesh
    {
        get
        {
            if (mesh && useLastMesh)
                return mesh;

            mesh = new Mesh();
            VertexHelper vh = new VertexHelper();

            var r = GetPixelAdjustedRect();
            var v = new Vector4(r.x, r.y, r.x + r.width, r.y + r.height);

            //vh.AddVert(new Vector3(-50, -50), Color.white, new Vector2(0, 0));
            //vh.AddVert(new Vector3(50, -50), Color.red, new Vector2(1, 0));
            //vh.AddVert(new Vector3(50, 50), Color.red, new Vector2(1, 1));
            //vh.AddVert(new Vector3(-50, 50), Color.white, new Vector2(0, 1));

            //vh.AddTriangle(0, 1, 2);
            //vh.AddTriangle(0, 2, 3);

            vh.Clear();
            vh.AddVert(new Vector3(v.x, v.y), Color.white, new Vector2(0f, 0f));
            vh.AddVert(new Vector3(v.x, v.w), Color.white, new Vector2(0f, 1f));
            vh.AddVert(new Vector3(v.z, v.w), Color.white, new Vector2(1f, 1f));
            vh.AddVert(new Vector3(v.z, v.y), Color.white, new Vector2(1f, 0f));

            vh.AddTriangle(0, 1, 2);
            vh.AddTriangle(2, 3, 0);

            vh.FillMesh(mesh);

            return mesh;
        }
    }

2.2 材质

在unity中新建Material,然后将此材质shader选为UI/Default。然后将此材质设置给CanvasRenderer,并设置Texture。如下:

        canvasRender.materialCount = 1;
        canvasRender.SetMesh(UIMesh);
        canvasRender.SetMaterial(material, 0);
        canvasRender.SetTexture(mainTexture);

2.3 设置Rect剔除

RectMaskD的剔除作用是通过CanvasRenderer的EnableRectCliping实现的,入口参数为Rect类型变量。但是主意一点,此Rect的值是Canvas下的坐标,因为此方法定义的是本UI图像在Canvas的剔除位置,至于剔除子物体是由ui的逻辑控制的。如下:

    private void EnableRectClip(Rect rect)
    {
        canvasRender.EnableRectClipping(rect);
    }

2.4 启动位置

可以在start里生成ui也可在update中,如果当ui内容变化(比如贴图或者大小等),则需要标记此变化,在下一帧去更改。原Graphic的做法是在Canvas.willRenderCanvases中进行的,如下:

void Start ()
    {
        SetCanvasRenderer();

        Canvas.willRenderCanvases += WillRenderCanvas;
    }

    void WillRenderCanvas()
    {
        if (!useLastMesh)
        {
            SetCanvasRenderer();
            useLastMesh = true;
        }
    }

3.完整代码

如下是整个破产版Graphic的完整代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class CustomGraphic : MonoBehaviour
{
    public Texture mainTexture;
    public CanvasRenderer canvasRender;
    public Material material;
    public Canvas canvas;
    public RectTransform rectTransform;

    public bool enableClip = true;

    private Mesh mesh;
    private bool useLastMesh = true;

    public Mesh UIMesh
    {
        get
        {
            if (mesh && useLastMesh)
                return mesh;

            mesh = new Mesh();
            VertexHelper vh = new VertexHelper();

            var r = GetPixelAdjustedRect();
            var v = new Vector4(r.x, r.y, r.x + r.width, r.y + r.height);

            //vh.AddVert(new Vector3(-50, -50), Color.white, new Vector2(0, 0));
            //vh.AddVert(new Vector3(50, -50), Color.red, new Vector2(1, 0));
            //vh.AddVert(new Vector3(50, 50), Color.red, new Vector2(1, 1));
            //vh.AddVert(new Vector3(-50, 50), Color.white, new Vector2(0, 1));

            //vh.AddTriangle(0, 1, 2);
            //vh.AddTriangle(0, 2, 3);

            vh.Clear();
            vh.AddVert(new Vector3(v.x, v.y), Color.white, new Vector2(0f, 0f));
            vh.AddVert(new Vector3(v.x, v.w), Color.white, new Vector2(0f, 1f));
            vh.AddVert(new Vector3(v.z, v.w), Color.white, new Vector2(1f, 1f));
            vh.AddVert(new Vector3(v.z, v.y), Color.white, new Vector2(1f, 0f));

            vh.AddTriangle(0, 1, 2);
            vh.AddTriangle(2, 3, 0);

            vh.FillMesh(mesh);

            return mesh;
        }
    }

    private void SetCanvasRenderer()
    {
        if(enableClip)
        EnableRectClip(new Rect(-150, -150, 300, 300));

        canvasRender.materialCount = 1;
        canvasRender.SetMesh(UIMesh);
        canvasRender.SetMaterial(material, 0);
        canvasRender.SetTexture(mainTexture);
    }

    private void EnableRectClip(Rect rect)
    {
        canvasRender.EnableRectClipping(rect);
    }

	void Start ()
    {
        SetCanvasRenderer();

        Canvas.willRenderCanvases += WillRenderCanvas;
    }

    void WillRenderCanvas()
    {
        if (!useLastMesh)
        {
            SetCanvasRenderer();
            useLastMesh = true;
        }
    }

    private void OnRectTransformDimensionsChange()
    {
        useLastMesh = false;
    }

    private Rect GetPixelAdjustedRect()
    {
        if (!canvas || canvas.renderMode == RenderMode.WorldSpace || canvas.scaleFactor == 0.0f || !canvas.pixelPerfect)
            return rectTransform.rect;
        else
            return RectTransformUtility.PixelAdjustRect(rectTransform, canvas);
    }
}

4.结语

由于原UGUI为了更自动化也为了功能的全面性,添加了很多逻辑功能,其中大部分动能用到了GetComponents方法(以及类似方法),所以都对设备产生了比较多的消耗。

原文地址:https://www.cnblogs.com/llstart-new0201/p/12678243.html