(七)自定义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方法(以及类似方法),所以都对设备产生了比较多的消耗。

猜你喜欢

转载自www.cnblogs.com/llstart-new0201/p/12678243.html