属性雷达图UGUI渲染器

属性雷达图UGUI渲染器

在许多游戏会使用雷达图展示角色的各个属性值大小。这是一种较为常见的UI界面。


MMORPG手游《仙境传说RO:守护永恒的爱》中创建角色页面

开源库项目UI Polygon采用拓展UGUI组件的方式,构建边长可调节的多边形UI组件。可用于实现雷达图效果。


效果图

作者Blog:http://ciaccodavi.de/

开源库链接:https://lab.uwa4d.com/lab/5b5d1083d7f10a201fea7299


使用方法

使用方法十分简单。首先将项目中的“UIPolygon.cs”文件导入Unity工程,在工程中的UI组件Canvas下Create Empty。

接着给这个空物体添加“UIPolygon.cs”组件:

最后调节属性,实现需要的效果。例如:是否空心、旋转角度、多边形边数、凹凸程度等。





实现方式

Unity官方发布了部分UGUI的源码(下载地址:https://bitbucket.org/Unity-Technologies/ui/downloads/?tab=downloads),通过对源码的阅读,可以帮助我们拓展实现更多的UI功能。

该开源库项目便是创建了一个继承自MaskableGraphic的类UIPolygon,实现了上述效果。

MaskableGraphic类是UGUI的核心组件,继承自Graphic,是一个抽象类,它的派生类有RawImage、Image、Text等。在这里不做详细说明,有兴趣的读者,推荐阅读由凯奥斯撰写的UGUI内核大探究系列,关于MaskableGraphic的一篇:https://blog.csdn.net/ecidevilin/article/details/52555253

首先在Update()函数中确定组件大小长度变量size,等于该GameObject长宽的较小值;确定镂空时,镂空部分的长度不能超过size的一半。

void Update()
{
  size = rectTransform.rect.width;
  if (rectTransform.rect.width > rectTransform.rect.height)
    size = rectTransform.rect.height;
  else
    size = rectTransform.rect.width;
  thickness = (float)Mathf.Clamp(thickness, 0, size / 2);
}

接着继承虚函数OnPopulateMesh(VertexHelper vh),并重写绘制逻辑。OnPopulateMesh函数用于收集Mesh信息,在具体的类中各自实现。VertexHelper类保存了Mesh的基本信息,通过这些信息构成Mesh网络。它的成员函数AddUIVertexQuad用于保存一个四边形的基本Mesh信息。四边形是Unity中UI绘制的一个基本图元:


四边形图元

这部分内容不再详细描述,有兴趣的读者,推荐阅读由宣雨松撰写的《UGUI深度研究之源码鉴赏》,其中有较为详细的解析。

以网格锚点pivot.xy=(0.5,0.5)的五边形为例,对于一个五边形来说,实际是绘制了六个四边形。

在不镂空的情况下:

设定两个变量默认在中心点O:

Vector2 prevX = Vector2.zero;
Vector2 prevY = Vector2.zero;

绘制第一个四边形,四个顶点pos0~3分别为[O,A,O,O],其中A点位置根据用户在面板上设定的百分比、锚点位置、size大小、旋转角度等进行计算:

float degrees = 360f / sides;
float outer = -rectTransform.pivot.x * size * VerticesDistances[i];
float rad = Mathf.Deg2Rad * (i * degrees + rotation);
float c = Mathf.Cos(rad);
float s = Mathf.Sin(rad);
pos1 = new Vector2(outer * c, outer * s);

对于A点的坐标即为(size*0.5*1,0),第一个四边形等同于没有绘制,主要用途是计算A点位置。

保存pos1和pos2的位置:

pos0 = prevX;
pos1 = new Vector2(outer * c, outer * s);
pos2 = Vector2.zero;
pos3 = Vector2.zero;
prevX = pos1;
prevY = pos2;
vh.AddUIVertexQuad(SetVbo(new[] { pos0, pos1, pos2, pos3 }, new[] { uv0, uv1, uv2, uv3 }));

然后,绘制第二个四边形,四个顶点pos0~3分别为:[A,B,O,O],剩余的四边形同理。

对于镂空的情况:

依次绘制四边形[A,B,A,A]、[A,B,C,D]等等,顶点位置的计算与上述方式相似:

float inner = -rectTransform.pivot.x * size * VerticesDistances[i] + thickness;
pos2 = new Vector2(inner * c, inner * s);
pos3 = prevY;

最后提供一些重载的绘制接口,可以在程序中动态调用:

public void DrawPolygon(int _sides)
{
  sides = _sides;
  VerticesDistances = new float[_sides + 1];
  for (int i = 0; i < _sides; i++) VerticesDistances[i] = 1;
  rotation = 0;
}
public void DrawPolygon(int _sides, float[] _VerticesDistances)
{
  sides = _sides;
  VerticesDistances = _VerticesDistances;
  rotation = 0;
}
public void DrawPolygon(int _sides, float[] _VerticesDistances, float _rotation)
{
  sides = _sides;
  VerticesDistances = _VerticesDistances;
  rotation = _rotation;
}

性能测评

使用Profiler查看UI的DrawCall与Batch情况,可以看到,使用该组件会产生一个DrawCall并且满足UGUI的合批规则。

通过脚本每帧更改多边形颜色,重绘该多边形界面,通过UWA GOT Online测试Mono堆内存的分配:

可见堆内存分配稍高,针对这样的现象我们可以针对SetVbo()函数的局部变量vbo进行处理:

UIVertex[] vbo = new UIVertex[4];

将vbo变量设置为该类的成员变量。Mono堆内存分配就会小很多:

快用UWA Lab合辑Mark好项目!

请输入图片描述

今天的推荐就到这儿啦,或者它可直接使用,或者它需要您的润色,或者它启发了您的思路......

请不要吝啬您的点赞和转发,让我们知道我们在做对的事。当然如果您可以留言给出宝贵的意见,我们会越做越好。

更多精彩开源库请关注:lab.uwa4d.com

  • Bonnie 发表在 9月5日 回复

    很棒!