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