一、通过Mask组件实现
第一种实现方式是通过UGUI原生的「Mask」遮罩组件。实现起来非常简单,首先创建一个Image对象,并挂载「Mask」组件。然后将Image的Sprite设置为事先准备好的圆形图片

然后再将这个Image对象设置为目标对象的父物体即可

但是这种方式有个问题,Mask组件会增加额外的Draw Call调用(Shader中的概念),也就会增加额外的性能消耗。可以通过Game窗口中的Stats界面查看Draw Call调用情况

二、手动实现
2.1 修改渲染模式
既然要实现一个圆形图片组件,那么肯定是基于原生的「Image」组件实现的。因此我们创建一个空物体,并挂载一个空的脚本。让脚本中的类继承Image类
public class CircleImage : Image
{
protected override void OnPopulateMesh(VertexHelper toFill)
{
// 清除顶点数据
toFill.Clear();
}
}
当一个UI元素生成顶点数据时,会调用OnPopulateMesh(VertexHelper toFill)方法。它传入了一个VertexHelper类型的参数,其中记录了将图片渲染到屏幕上所需的顶点和三角形信息。我们要做的就是重写这个方法,并对参数中的顶点和三角形信息进行修改,以达成将图片渲染成圆形的目的。
接下来我们需要将uv贴图坐标与渲染到场景中的坐标的对应关系计算出来。一张图片对应的uv坐标如下图所示

它的四个坐标点在Unity中由一个四维变量进行存储。获取到这个变量,以及UI的宽高后,就可以计算出uv中心点、uv坐标到UI坐标的换算比率等信息
// 获取当前图片的外层uv
var uv = overrideSprite != null ? DataUtility.GetOuterUV(overrideSprite) : Vector4.zero;
var width = rectTransform.rect.width;
var height = rectTransform.rect.height;
var uvWidth = uv.z - uv.x;
var uvHeight = uv.w - uv.y;
// 获取uv中心点
Vector2 uvCenter = new Vector2(uvWidth * 0.5f, uvHeight * 0.5f);
// 计算换算比率
Vector2 convertRatio = new Vector2(uvWidth / width, uvHeight / height);
// 计算UI中心点
Vector2 originPos = new Vector2((0.5f - rectTransform.pivot.x) * width, (0.5f - rectTransform.pivot.y) * height);
// 整个圆形的半径
var radius = width * 0.5f;
这里需要注意一点,计算UI的中点时,要考虑到UI锚点变化的问题。如果将中点始终设为(0,0)的话,当锚点挪动,图案也会跟着一起挪动。因此当锚点不再是(0.5,0.5)时,就需要给中点坐标加一个偏移值,使它始终保持在UI的中心位置。
我们知道,屏幕上显示出来的图案实际上是由GPU渲染出来的。而GPU渲染时是以三角形面片为基本单位进行绘制。三角形的数量越多,绘制出来的圆形也就越精细。

因而我们需要定义出圆形由多少块三角面片组成,后续可以更改这个值来调整圆形的渲染精度。
// 圆形由多少块三角面片拼成
private int _segments = 100;
有了三角面片的数量,我们就可以求出每个三角形所对应的弧度
// 每个三角形的弧度
var radian = (2 * Mathf.PI) / _segments;
准备工作完成,我们现在可以来创建圆心点了。UI的顶点信息在Unity中以UIVertex对象进行存储。我们可以设置它的颜色、位置、uv坐标等信息。创建完成后将其添加到顶点集合中。
// 创建圆心顶点
UIVertex origin = new();
origin.color = color;
origin.position = originPos;
origin.uv0 = new Vector2(uvCenter.x, uvCenter.y);
toFill.AddVert(origin);
接下来需要计算位于圆周上的顶点坐标。有每个三角形对应的弧度、圆形的半径,计算起来也很简单。这里注意Mathf.Cos(x)和Mathf.Sin(x)传入的参数是弧度,而不是角度

最低0.47元/天 解锁文章
6046

被折叠的 条评论
为什么被折叠?



