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)