AI提供的代码,内部部分代码会有问题。不要赋值任何精灵图片,仅仅当做一个圆形进度条用即可!

效果:

innerSprite.cs

using UnityEngine;
using UnityEngine.UI;

[AddComponentMenu("UI/Circle Energy Bar")]
public class CircleEnergyBar : Image
{
    [Header("Circle Settings")]
    [Range(3, 360)]
    public int segments = 100;
    
    [Header("Ring Settings")]
    [Range(0.1f, 0.9f)]
    public float ringThickness = 0.2f;
    [Range(0f, 1f)]
    public float fillAmount = 1f;
    public Color ringColor = Color.white;
    
    [Header("Direction Settings")]
    [Range(0f, 360f)]
    public float startAngle = 0f;
    public FillDirection fillDirection = FillDirection.Clockwise;
    
    [Header("Inner Circle Settings")]
    public Sprite innerSprite;
    public Color innerColor = Color.white;

    public enum FillDirection
    {
        Clockwise,
        CounterClockwise,
        BothSides
    }

    protected override void OnPopulateMesh(VertexHelper toFill)
    {
        toFill.Clear();
        
        float width = rectTransform.rect.width;
        float height = rectTransform.rect.height;
        float outerRadius = Mathf.Min(width, height) * 0.5f;
        float innerRadius = outerRadius * (1f - ringThickness);
        
        // 绘制环形能量条
        DrawRing(toFill, outerRadius, innerRadius, fillAmount);
        
        // 绘制内部圆形图片
        if (innerSprite != null)
        {
            DrawInnerCircle(toFill, innerRadius);
        }
    }

    private void DrawRing(VertexHelper vh, float outerRadius, float innerRadius, float fill)
    {
        int actualSegments = Mathf.CeilToInt(segments * fill);
        if (actualSegments < 3) actualSegments = 3;
        
        float anglePerSegment = 360f / segments;
        
        // 根据填充方向调整起始角度和结束角度
        float startAngleRad = startAngle * Mathf.Deg2Rad;
        float totalAngle = 360f * fill;
        
        // 添加顶点
        for (int i = 0; i <= actualSegments; i++)
        {
            float currentAngle = CalculateCurrentAngle(i, actualSegments, totalAngle);
            float radian = currentAngle * Mathf.Deg2Rad;
            
            // 外圆顶点
            float outerX = Mathf.Cos(radian) * outerRadius;
            float outerY = Mathf.Sin(radian) * outerRadius;
            Vector2 outerUV = GetUV(outerX, outerY, outerRadius * 2);
            
            UIVertex outerVertex = new UIVertex();
            outerVertex.color = ringColor;
            outerVertex.position = new Vector3(outerX, outerY, 0);
            outerVertex.uv0 = outerUV;
            vh.AddVert(outerVertex);
            
            // 内圆顶点
            float innerX = Mathf.Cos(radian) * innerRadius;
            float innerY = Mathf.Sin(radian) * innerRadius;
            Vector2 innerUV = GetUV(innerX, innerY, outerRadius * 2);
            
            UIVertex innerVertex = new UIVertex();
            innerVertex.color = ringColor;
            innerVertex.position = new Vector3(innerX, innerY, 0);
            innerVertex.uv0 = innerUV;
            vh.AddVert(innerVertex);
        }
        
        // 添加三角形
        for (int i = 0; i < actualSegments; i++)
        {
            int index = i * 2;
            vh.AddTriangle(index, index + 1, index + 2);
            vh.AddTriangle(index + 1, index + 3, index + 2);
        }
    }

    private float CalculateCurrentAngle(int segmentIndex, int totalSegments, float totalAngle)
    {
        float baseAngle = startAngle;
        
        switch (fillDirection)
        {
            case FillDirection.Clockwise:
                return baseAngle + segmentIndex * (totalAngle / totalSegments);
                
            case FillDirection.CounterClockwise:
                return baseAngle - segmentIndex * (totalAngle / totalSegments);
                
            case FillDirection.BothSides:
                float halfAngle = totalAngle / 2f;
                if (segmentIndex <= totalSegments / 2)
                {
                    return baseAngle - segmentIndex * (halfAngle / (totalSegments / 2f));
                }
                else
                {
                    return baseAngle + (segmentIndex - totalSegments / 2) * (halfAngle / (totalSegments / 2f));
                }
                
            default:
                return baseAngle + segmentIndex * (totalAngle / totalSegments);
        }
    }

    private void DrawInnerCircle(VertexHelper vh, float radius)
    {
        int startIndex = vh.currentVertCount;
        
        // 添加圆心
        UIVertex center = new UIVertex();
        center.color = innerColor;
        center.position = Vector3.zero;
        center.uv0 = new Vector2(0.5f, 0.5f);
        vh.AddVert(center);
        
        // 添加边缘顶点(完整圆形)
        for (int i = 0; i <= segments; i++)
        {
            float angle = i * 360f / segments;
            float radian = angle * Mathf.Deg2Rad;
            
            float x = Mathf.Cos(radian) * radius;
            float y = Mathf.Sin(radian) * radius;
            
            Vector2 uv = GetUVForSprite(x, y, radius * 2);
            
            UIVertex vertex = new UIVertex();
            vertex.color = innerColor;
            vertex.position = new Vector3(x, y, 0);
            vertex.uv0 = uv;
            vh.AddVert(vertex);
        }
        
        // 创建三角形
        for (int i = 1; i < segments; i++)
        {
            vh.AddTriangle(startIndex, startIndex + i, startIndex + i + 1);
        }
        vh.AddTriangle(startIndex, startIndex + segments, startIndex + 1);
    }

    private Vector2 GetUV(float x, float y, float diameter)
    {
        return new Vector2(
            (x / diameter) + 0.5f,
            (y / diameter) + 0.5f
        );
    }

    private Vector2 GetUVForSprite(float x, float y, float diameter)
    {
        if (innerSprite != null)
        {
            Rect spriteRect = innerSprite.rect;
            Texture2D texture = innerSprite.texture;
            
            float u = (x / diameter + 0.5f) * (spriteRect.width / texture.width);
            float v = (y / diameter + 0.5f) * (spriteRect.height / texture.height);
            
            return new Vector2(u + spriteRect.x / texture.width, v + spriteRect.y / texture.height);
        }
        
        return GetUV(x, y, diameter);
    }

    // 公开方法
    public void SetFillAmount(float amount)
    {
        fillAmount = Mathf.Clamp01(amount);
        SetVerticesDirty();
    }

    public void SetRingColor(Color color)
    {
        ringColor = color;
        SetVerticesDirty();
    }

    public void SetStartAngle(float angle)
    {
        startAngle = Mathf.Repeat(angle, 360f);
        SetVerticesDirty();
    }

    public void SetFillDirection(FillDirection direction)
    {
        fillDirection = direction;
        SetVerticesDirty();
    }

    public void SetInnerSprite(Sprite sprite)
    {
        innerSprite = sprite;
        SetVerticesDirty();
    }

    public void SetRingThickness(float thickness)
    {
        ringThickness = Mathf.Clamp(thickness, 0.1f, 0.9f);
        SetVerticesDirty();
    }

    protected override void OnValidate()
    {
        base.OnValidate();
        SetVerticesDirty();
    }
}

CircleEnergyBarEditor.cs

#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.UI;
using UnityEngine;

[CustomEditor(typeof(CircleEnergyBar))]
public class CircleEnergyBarEditor : Editor
{
    private CircleEnergyBar energyBar;

    public override void OnInspectorGUI()
    {
        energyBar = (CircleEnergyBar)target;

        DrawDefaultInspector();

        EditorGUILayout.Space();
        EditorGUILayout.LabelField("Editor Testing", EditorStyles.boldLabel);

        // 填充量测试
        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("0%", GUILayout.Height(25))) SetFillAmount(0f);
        if (GUILayout.Button("25%", GUILayout.Height(25))) SetFillAmount(0.25f);
        if (GUILayout.Button("50%", GUILayout.Height(25))) SetFillAmount(0.5f);
        if (GUILayout.Button("75%", GUILayout.Height(25))) SetFillAmount(0.75f);
        if (GUILayout.Button("100%", GUILayout.Height(25))) SetFillAmount(1f);
        EditorGUILayout.EndHorizontal();

        // 颜色测试
        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("Red", GUILayout.Height(25))) SetRingColor(Color.red);
        if (GUILayout.Button("Green", GUILayout.Height(25))) SetRingColor(Color.green);
        if (GUILayout.Button("Blue", GUILayout.Height(25))) SetRingColor(Color.blue);
        if (GUILayout.Button("Yellow", GUILayout.Height(25))) SetRingColor(Color.yellow);
        EditorGUILayout.EndHorizontal();

        // 厚度测试
        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("Thin", GUILayout.Height(25))) SetRingThickness(0.1f);
        if (GUILayout.Button("Medium", GUILayout.Height(25))) SetRingThickness(0.3f);
        if (GUILayout.Button("Thick", GUILayout.Height(25))) SetRingThickness(0.6f);
        EditorGUILayout.EndHorizontal();

        // 起始角度测试
        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("0°", GUILayout.Height(25))) SetStartAngle(0f);
        if (GUILayout.Button("90°", GUILayout.Height(25))) SetStartAngle(90f);
        if (GUILayout.Button("180°", GUILayout.Height(25))) SetStartAngle(180f);
        if (GUILayout.Button("270°", GUILayout.Height(25))) SetStartAngle(270f);
        EditorGUILayout.EndHorizontal();

        // 方向测试
        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("Clockwise", GUILayout.Height(25))) SetFillDirection(CircleEnergyBar.FillDirection.Clockwise);
        if (GUILayout.Button("Counter CW", GUILayout.Height(25))) SetFillDirection(CircleEnergyBar.FillDirection.CounterClockwise);
        if (GUILayout.Button("Both Sides", GUILayout.Height(25))) SetFillDirection(CircleEnergyBar.FillDirection.BothSides);
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.Space();
        if (GUILayout.Button("Refresh", GUILayout.Height(30))) Refresh();
    }

    private void SetFillAmount(float amount)
    {
        energyBar.SetFillAmount(amount);
        EditorUtility.SetDirty(energyBar);
    }

    private void SetRingColor(Color color)
    {
        energyBar.SetRingColor(color);
        EditorUtility.SetDirty(energyBar);
    }

    private void SetRingThickness(float thickness)
    {
        energyBar.SetRingThickness(thickness);
        EditorUtility.SetDirty(energyBar);
    }

    private void SetStartAngle(float angle)
    {
        energyBar.SetStartAngle(angle);
        EditorUtility.SetDirty(energyBar);
    }

    private void SetFillDirection(CircleEnergyBar.FillDirection direction)
    {
        energyBar.SetFillDirection(direction);
        EditorUtility.SetDirty(energyBar);
    }

    private void Refresh()
    {
        energyBar.SetVerticesDirty();
        EditorUtility.SetDirty(energyBar);
    }
}
#endif