UGUI实现的圆形图片
(1)添加一个image,添加圆形的Sprite,添加mask组件。
(2)在image下添加一个image作为子物体,添加如以下的图片:
效果如图:
缺点:
锯齿严重,mask增加了Draw Call的调用,降低性能。禁用mask会明显看到Draw Call调用减少。
实现
原理:由多个三角形面片组成
UV:
代码:
using UnityEngine;
using UnityEngine.Sprites;
using UnityEngine.UI;
public class CircleImage : Image
{
[SerializeField]
private int segements = 100;//拼成块数
[SerializeField]
private float showPercent =1;//显示百分比
protected override void OnPopulateMesh(VertexHelper vh)
{
if (segements > 10000) return;
vh.Clear();
//overrideSprite是否为null
Vector4 uv = overrideSprite!=null?DataUtility.GetOuterUV(overrideSprite):Vector4.zero;
//求出uv中心点
float uvWidth = uv.z - uv.x;
float uvHeight = uv.w - uv.y;
Vector2 uvCenter = new Vector2(0.5f*uvWidth,0.5f*uvHeight);
//uv与实际显示大小转换比例
float width = rectTransform.rect.width;
float height = rectTransform.rect.height;
Vector2 convertRatio = new Vector2(uvWidth / width, uvHeight / height);
//每一块三角片的弧度
float radian = (2 * Mathf.PI) / segements;
//显示半径
float radius = width * 0.5f;
//圆点
UIVertex origin = new UIVertex();
origin.color = color;
//rect中的位置
origin.position = Vector3.zero;
float uvX = origin.position.x * convertRatio.x + uvCenter.x;
float uvY = origin.position.y * convertRatio.y + uvCenter.y;
//对应uv坐标
origin.uv0 = new Vector2(uvX, uvY);
vh.AddVert(origin);
//其他点
int realSegements = (int)(segements * showPercent);//真实三角面数
int vertexCount = realSegements + 1;
//当前弧度
float curRadian = 0;
for (int i = 0; i < vertexCount; i++)
{
float x = Mathf.Cos(curRadian) * radius;
float y = Mathf.Sin(curRadian) * radius;
curRadian += radian;
UIVertex vertexTemp = new UIVertex();
vertexTemp.color = color;
vertexTemp.position = new Vector2(x, y);
float _uvX = vertexTemp.position.x * convertRatio.x + uvCenter.x;
float _uvY = vertexTemp.position.y * convertRatio.y + uvCenter.y;
vertexTemp.uv0 = new Vector2(_uvX, _uvY);
vh.AddVert(vertexTemp);
}
//三角面,顺时针传入顶点,否则背面剔除
for (int i = 1; i <= realSegements; i++)
{
vh.AddTriangle(i, 0, i+1);
}
}
}
效果图:
将属性序列化显示在Inspector面板上
原始:
[SerializeField]
private int segements = 100;//拼成块数
[SerializeField]
private float showPercent =1;//显示百分比
上面两个属性并没有显示在Inspector面板上
代码实现:
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.UI;
using UnityEngine;
[CustomEditor(typeof(CircleImage), true)]
[CanEditMultipleObjects]
public class CircleImageEditor : ImageEditor
{
SerializedProperty _fillPercent;
SerializedProperty _segements;
private void OnEnable()
{
base.OnEnable();
_fillPercent = serializedObject.FindProperty("showPercent");
_segements = serializedObject.FindProperty("segements");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();绘制原本的信息//
// Update the serializedProperty - always do this in the beginning of OnInspectorGUI.
serializedObject.Update();
// Show the custom GUI controls.--显示自定义GUI控件
EditorGUILayout.Slider(_fillPercent, 0, 1, new GUIContent("showPercent"));
EditorGUILayout.PropertyField(_segements);
// Apply changes to the serializedProperty - always do this in the end of OnInspectorGUI.
serializedObject.ApplyModifiedProperties();
if (GUI.changed)
{
EditorUtility.SetDirty(target);
}
}
}
效果:
实现技能灰色效果
代码修改:
//其他点
float tmp = segements * showPercent;
int realSegements = (int)tmp;//真实三角面数
int vertexCount = realSegements==0?0:realSegements + 1;
Debug.Log("realSegements="+ realSegements);
//当前弧度
float curRadian = 0;
for (int i = 0; i < segements +1; i++)
{
float x = Mathf.Cos(curRadian) * radius;
float y = Mathf.Sin(curRadian) * radius;
curRadian += radian;
UIVertex vertexTemp = new UIVertex();
if (i < vertexCount)
{
vertexTemp.color = color;
}
else
{
vertexTemp.color = new Color32(60,60,60,255);
}
vertexTemp.position = new Vector2(x, y);
float _uvX = vertexTemp.position.x * convertRatio.x + uvCenter.x;
float _uvY = vertexTemp.position.y * convertRatio.y + uvCenter.y;
vertexTemp.uv0 = new Vector2(_uvX, _uvY);
vh.AddVert(vertexTemp);
}
修改圆心高亮的问题
因为三角面片的颜色由三个点渐变而成,而圆心的颜色始终为origin.color = color,导致圆心在realSegements为0时,仍然高亮
代码修改:
随showPercent渐变
private readonly Color32 GRAY_COLOR = new Color32(60, 60, 60, 255);
private Color32 GetOriginColor()
{
Color32 colorTmp = (Color.white - GRAY_COLOR) * showPercent;
return new Color32(
(byte)(GRAY_COLOR.r + colorTmp.r),
(byte)(GRAY_COLOR.g + colorTmp.g),
(byte)(GRAY_COLOR.b + colorTmp.b),
255);
}
效果:
解决 pivot改变,图片随之移动和uv不正确的问题
//圆心位置,一直处于rect中心,不随pivot改变
Vector2 originPos = new Vector2((0.5f- rectTransform.pivot.x) * width, (0.5f- rectTransform.pivot.y) * height);
float uvX = uvCenter.x;
float uvY = uvCenter.y;
//对应uv坐标
origin.uv0 = new Vector2(uvX, uvY);
vh.AddVert(origin);
for (int i = 0; i < segements +1; i++)
{
float x = Mathf.Cos(curRadian) * radius;
float y = Mathf.Sin(curRadian) * radius;
curRadian += radian;
UIVertex vertexTemp = new UIVertex();
if (i < vertexCount)
{
vertexTemp.color = color;
}
else
{
vertexTemp.color = new Color32(60,60,60,255);
}
vertexTemp.position = new Vector2(x, y)+ originPos;
float _uvX = x * convertRatio.x + uvCenter.x;
float _uvY = y * convertRatio.y + uvCenter.y;
vertexTemp.uv0 = new Vector2(_uvX, _uvY);
vh.AddVert(vertexTemp);
}
完整代码如下:
图片不随pivot改变而移动
using UnityEngine;
using UnityEngine.Sprites;
using UnityEngine.UI;
public class CircleImage : Image
{
[SerializeField]
private int segements = 100;//拼成块数
[SerializeField]
private float showPercent =1f;//显示百分比
protected override void OnPopulateMesh(VertexHelper vh)
{
if (segements > 10000) return;
vh.Clear();
//overrideSprite是否为null
Vector4 uv = overrideSprite!=null?DataUtility.GetOuterUV(overrideSprite):Vector4.zero;
//求出uv中心点
float uvWidth = uv.z - uv.x;
float uvHeight = uv.w - uv.y;
Vector2 uvCenter = new Vector2(0.5f*uvWidth,0.5f*uvHeight);
//uv与实际显示大小转换比例
float width = rectTransform.rect.width;
float height = rectTransform.rect.height;
Vector2 convertRatio = new Vector2(uvWidth / width, uvHeight / height);
//每一块三角片的弧度
float radian = (2 * Mathf.PI) / segements;
//显示半径
float radius = width * 0.5f;
//圆点
UIVertex origin = new UIVertex();
//byte temp = (byte)(255 * showPercent);
//origin.color = new Color32(temp, temp, temp, 255);
origin.color = GetOriginColor();
//圆心位置,一直处于rect中心,不随pivot改变
Vector2 originPos = new Vector2((0.5f- rectTransform.pivot.x) * width, (0.5f- rectTransform.pivot.y) * height);
float uvX = uvCenter.x;
float uvY = uvCenter.y;
//对应uv坐标
origin.uv0 = new Vector2(uvX, uvY);
vh.AddVert(origin);
//其他点
float tmp = segements * showPercent;
int realSegements = (int)tmp;//真实三角面数
int vertexCount = realSegements==0?0:realSegements + 1;
Debug.Log("realSegements="+ realSegements);
//当前弧度
float curRadian = 0;
for (int i = 0; i < segements +1; i++)
{
float x = Mathf.Cos(curRadian) * radius;
float y = Mathf.Sin(curRadian) * radius;
curRadian += radian;
UIVertex vertexTemp = new UIVertex();
if (i < vertexCount)
{
vertexTemp.color = color;
}
else
{
vertexTemp.color = new Color32(60,60,60,255);
}
vertexTemp.position = new Vector2(x, y)+ originPos;
float _uvX = x * convertRatio.x + uvCenter.x;
float _uvY = y * convertRatio.y + uvCenter.y;
vertexTemp.uv0 = new Vector2(_uvX, _uvY);
vh.AddVert(vertexTemp);
}
//三角面,顺时针传入顶点
for (int i = 1; i <= segements; i++)
{
vh.AddTriangle(i, 0, i+1);
}
}
private readonly Color32 GRAY_COLOR = new Color32(60, 60, 60, 255);
private Color32 GetOriginColor()
{
Color32 colorTmp = (Color.white - GRAY_COLOR) * showPercent;
return new Color32(
(byte)(GRAY_COLOR.r + colorTmp.r),
(byte)(GRAY_COLOR.g + colorTmp.g),
(byte)(GRAY_COLOR.b + colorTmp.b),
255);
}
}