Unity自定义起始和结束角度的环形填充Image

效果演示如下:

  当我们需要进行Image的环形加载时,需要将Image的ImageType设为Filled,但是起始角度只能选择上下左右四个点位,此外只能选择360°满圆周环形。

  试想一下,当我们需要一个起始位置在斜向45°,且只有320°的环形加载时,问题会变得很棘手。全网也没有类似需求和方案,于是咱写了一个还算用得了的组件。

  • 先设置起始角度CustomFillOrigin,组件设有两条辅助线分别是起始线和终点线。
  • 然后设置图片填充的总角度数CustomFillMaxAngle。
  • 然后选择填充方向Clockwise(顺时针或逆时针),此外可以设置辅助线长度,其他功能需求可以自行更改组件代码。

组件继承自Image,组件脚本代码如下:
CustomFillOriginImage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// ******************************************************************
//@file CustomFillOriginImage.cs
//@brief 自定义起始填充角度的image
//@author yufulao, yufulao@qq.com
//@createTime 2024.07.22 01:24:49
// ******************************************************************

using UnityEngine;
using UnityEngine.UI;

namespace Yu
{
public class CustomFillOriginImage : Image
{
[SerializeField] private float customFillOrigin = 0f; // 自定义起始角度,范围0-360
[SerializeField] private float customFillMaxAngle = 360f; // 自定义最大角度,范围0-360
[SerializeField] private bool clockwise = true; // 填充方向:顺时针或逆时针
[SerializeField] private float lineLength = 100f; // 线段长度

public float CustomFillOrigin
{
get => customFillOrigin;
set
{
customFillOrigin = Mathf.Clamp(value, 0f, 360f);
SetVerticesDirty();
}
}

public float CustomFillMaxAngle
{
get => customFillMaxAngle;
set
{
customFillMaxAngle = Mathf.Clamp(value, 0f, 360f);
SetVerticesDirty();
}
}

public bool Clockwise
{
get => clockwise;
set
{
clockwise = value;
SetVerticesDirty();
}
}

public float LineLength
{
get => lineLength;
set
{
lineLength = Mathf.Max(0f, value);
SetVerticesDirty();
}
}

protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
if (overrideSprite == null)
{
return;
}

var rect = GetPixelAdjustedRect();
var v = new Vector4(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
var center = new Vector2((v.x + v.z) / 2, (v.y + v.w) / 2);
var radius = (v.z - v.x) / 2;
var startAngle = customFillOrigin * Mathf.Deg2Rad;
startAngle -= Mathf.PI / 2;
vh.AddVert(center, color, new Vector2(0.5f, 0.5f));
var fillAmountInDegrees = customFillMaxAngle * fillAmount;
var steps = Mathf.CeilToInt(fillAmountInDegrees);
if (clockwise)
{
for (var i = 0; i <= steps; i++)
{
var currentAngle = startAngle + (i * Mathf.Deg2Rad);
var dir = new Vector2(Mathf.Cos(currentAngle), Mathf.Sin(currentAngle));
var pos = center + dir * radius;
var uv = new Vector2(0.5f + dir.x * 0.5f, 0.5f + dir.y * 0.5f);
vh.AddVert(pos, color, uv);
}
}
else
{
for (var i = 0; i <= steps; i++)
{
var currentAngle = startAngle - (i * Mathf.Deg2Rad);
var dir = new Vector2(Mathf.Cos(currentAngle), Mathf.Sin(currentAngle));
var pos = center + dir * radius;
var uv = new Vector2(0.5f + dir.x * 0.5f, 0.5f + dir.y * 0.5f);
vh.AddVert(pos, color, uv);
}
}

for (var i = 1; i < vh.currentVertCount - 1; i++)
{
vh.AddTriangle(0, i, i + 1);
}
}
}
}

Editor类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// ******************************************************************
//@file CustomFillOriginImageEditor.cs
//@brief CustomFillOriginImage的Editor类
//@author yufulao, yufulao@qq.com
//@createTime 2024.07.22 01:36:22
// ******************************************************************

using UnityEditor;
using UnityEngine;

namespace Yu
{
[CustomEditor(typeof(CustomFillOriginImage))]
public class CustomFillOriginImageEditor : Editor
{
private SerializedProperty _customFillOriginProperty;
private SerializedProperty _customFillMaxAngleProperty;
private SerializedProperty _clockwiseProperty;
private SerializedProperty _lineLengthProperty;
private SerializedProperty _fillOriginProperty;
private SerializedProperty _fillClockwiseProperty;
private SerializedProperty _typeProperty;
private SerializedProperty _fillMethodProperty;

private void OnEnable()
{
_customFillOriginProperty = serializedObject.FindProperty("customFillOrigin");
_customFillMaxAngleProperty = serializedObject.FindProperty("customFillMaxAngle");
_clockwiseProperty = serializedObject.FindProperty("clockwise");
_lineLengthProperty = serializedObject.FindProperty("lineLength");

_fillOriginProperty = serializedObject.FindProperty("m_FillOrigin");
_fillClockwiseProperty = serializedObject.FindProperty("m_FillClockwise");
_typeProperty = serializedObject.FindProperty("m_Type");
_fillMethodProperty = serializedObject.FindProperty("m_FillMethod");
}

public override void OnInspectorGUI()
{
serializedObject.Update();
_fillOriginProperty.isExpanded = false;
_fillClockwiseProperty.isExpanded = false;
_typeProperty.isExpanded = false;
_fillMethodProperty.isExpanded = false;
DrawPropertiesExcluding(serializedObject, "customFillOrigin", "customFillMaxAngle", "clockwise", "lineLength", "m_FillOrigin", "m_FillClockwise", "m_Type", "m_FillMethod");
EditorGUILayout.Slider(_customFillOriginProperty, 0f, 360f, new GUIContent("Custom Fill Origin"));
EditorGUILayout.Slider(_customFillMaxAngleProperty, 0f, 360f, new GUIContent("Custom Fill Max Angle"));
EditorGUILayout.PropertyField(_clockwiseProperty, new GUIContent("Clockwise"));
EditorGUILayout.PropertyField(_lineLengthProperty, new GUIContent("Line Length"));
serializedObject.ApplyModifiedProperties();
}

private void OnSceneGUI()
{
var customFilledImage = (CustomFillOriginImage) target;
if (!customFilledImage)
{
return;
}

var transform = customFilledImage.transform;
//起始角度和最大角度的计算
var startAngle = customFilledImage.CustomFillOrigin;
var maxAngle = customFilledImage.CustomFillMaxAngle;
var endAngle = customFilledImage.Clockwise ? startAngle + maxAngle : startAngle - maxAngle;
//计算起始角度和结束角度的弧度值
var startRad = (startAngle - 90) * Mathf.Deg2Rad;
var endRad = (endAngle - 90) * Mathf.Deg2Rad;
//计算起始点和终点的世界坐标
var position = transform.position;
var startPoint = position + new Vector3(Mathf.Cos(startRad), Mathf.Sin(startRad)) * customFilledImage.LineLength;
var endPoint = position + new Vector3(Mathf.Cos(endRad), Mathf.Sin(endRad)) * customFilledImage.LineLength;
Handles.color = Color.red;
Handles.DrawLine(position, startPoint); //绘制起始线段
Handles.DrawLine(position, endPoint); //绘制终点线段
}
}
}