unity 做一个圆形分比图
// 在其他脚本中控制多段进度
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public MultiCircleProgress circleProgress;
void Start()
{
// 初始化数据
circleProgress.segments = new List<MultiCircleProgress.ProgressSegment>{
new MultiCircleProgress.ProgressSegment{
name = "",
progress = 0.2f,
color = Color.red
},
new MultiCircleProgress.ProgressSegment{
name = "",
progress = 0.3f,
color = Color.blue
},
new MultiCircleProgress.ProgressSegment{
name = "",
progress = 0.3f,
color = Color.green
},
new MultiCircleProgress.ProgressSegment{
name = "",
progress = 0.1f,
color = Color.black
},
new MultiCircleProgress.ProgressSegment{
name = "",
progress = 0.1f,
color = Color.cyan
}
};
circleProgress.CreateSegments();
}
void UpdatePlayerStats(float health, float mana, float stamina)
{
circleProgress.UpdateSegment(0, health);
circleProgress.UpdateSegment(1, mana);
circleProgress.UpdateSegment(2, stamina);
}
}```
脚本挂到场景中一个模型上,脚本可以设置生成得比例
```csharp
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
public class MultiCircleProgress : MonoBehaviour
{
[System.Serializable]
public class ProgressSegment
{
public string name;
[Range(0, 1)] public float progress;
public Color color;
[HideInInspector] public Image fillImage;
[HideInInspector] public Text textComponent;
}
[Header("UI Elements")]
public List<ProgressSegment> segments = new List<ProgressSegment>();
public GameObject segmentPrefab;
public Text percentTextTemplate;
public Transform textContainer;
[Header("Text Positioning")]
public float textDistanceFromCenter = 0.7f; // 基于半径的比例 (0.5=边缘, 1.0=两倍半径)
public float textOffsetAngle = 0f;
public float textMinDistance = 30f;
public float textMaxDistance = 200f;
private RectTransform baseRectTransform;
private float baseRadius;
void Start()
{
InitializeComponents();
CreateSegments();
}
void InitializeComponents()
{
baseRectTransform = GetComponent<RectTransform>();
baseRadius = Mathf.Min(baseRectTransform.rect.width, baseRectTransform.rect.height) / 2f;
}
public void CreateSegments()
{
ClearExistingSegments();
ClearExistingTexts();
float currentRotation = 0f;
for (int i = 0; i < segments.Count; i++)
{
CreateSegment(i, ref currentRotation);
}
}
void ClearExistingSegments()
{
foreach (Transform child in transform)
{
if (child != textContainer) Destroy(child.gameObject);
}
}
void ClearExistingTexts()
{
if (textContainer != null)
{
foreach (Transform child in textContainer)
{
Destroy(child.gameObject);
}
}
}
void CreateSegment(int index, ref float currentRotation)
{
// 创建进度段
GameObject segmentObj = Instantiate(segmentPrefab, transform);
segments[index].fillImage = segmentObj.GetComponent<Image>();
// 配置段属性
ConfigureSegment(index, currentRotation);
// 创建并定位文本
if (percentTextTemplate != null && textContainer != null)
{
CreateSegmentText(index, currentRotation);
}
currentRotation += segments[index].progress * 360f;
}
void ConfigureSegment(int index, float rotation)
{
segments[index].fillImage.color = segments[index].color;
segments[index].fillImage.type = Image.Type.Filled;
segments[index].fillImage.fillMethod = Image.FillMethod.Radial360;
segments[index].fillImage.fillOrigin = (int)Image.Origin360.Top;
segments[index].fillImage.fillClockwise = true;
segments[index].fillImage.transform.rotation = Quaternion.Euler(0, 0, -rotation);
segments[index].fillImage.fillAmount = segments[index].progress;
}
void CreateSegmentText(int index, float segmentStartRotation)
{
Text textObj = Instantiate(percentTextTemplate, textContainer);
textObj.gameObject.SetActive(true);
segments[index].textComponent = textObj;
textObj.text = $"{segments[index].name} {Mathf.RoundToInt(segments[index].progress * 100)}%";
textObj.color = segments[index].color;
PositionTextOutsideCircle(index, segmentStartRotation);
}
void PositionTextOutsideCircle(int index, float segmentStartRotation)
{
if (segments[index].textComponent == null) return;
// 计算当前半径
float currentRadius = Mathf.Min(
Mathf.Max(baseRectTransform.rect.width, baseRectTransform.rect.height) / 2f,
textMaxDistance
);
// 计算有效文本距离 (基于比例但限制在最小/最大值之间)
float effectiveTextDistance = Mathf.Max(
currentRadius * textDistanceFromCenter,
textMinDistance
);
// 计算扇形中心角度
float midAngle = segmentStartRotation + (segments[index].progress * 180f) + textOffsetAngle;
// 计算文本位置 (圆形中心向外延伸)
Vector3 textDirection = Quaternion.Euler(0, 0, -midAngle) * Vector3.up;
segments[index].textComponent.rectTransform.anchoredPosition = textDirection * effectiveTextDistance;
// 保持文本水平
segments[index].textComponent.rectTransform.localRotation = Quaternion.identity;
// 设置文本锚点为中心,方便定位
segments[index].textComponent.rectTransform.anchorMin =
segments[index].textComponent.rectTransform.anchorMax =
segments[index].textComponent.rectTransform.pivot = new Vector2(0.5f, 0.5f);
}
public void UpdateAllProgress()
{
float currentRotation = 0f;
for (int i = 0; i < segments.Count; i++)
{
segments[i].progress = Mathf.Clamp01(segments[i].progress);
// 更新填充和旋转
if (segments[i].fillImage != null)
{
segments[i].fillImage.transform.rotation = Quaternion.Euler(0, 0, -currentRotation);
segments[i].fillImage.fillAmount = segments[i].progress;
}
// 更新文本
if (segments[i].textComponent != null)
{
segments[i].textComponent.text = $"{segments[i].name}: {Mathf.RoundToInt(segments[i].progress * 100)}%";
PositionTextOutsideCircle(i, currentRotation);
}
currentRotation += segments[i].progress * 360f;
}
}
public void UpdateSegment(int index, float progress)
{
if (index >= 0 && index < segments.Count)
{
segments[index].progress = Mathf.Clamp01(progress);
UpdateAllProgress();
}
}
#if UNITY_EDITOR
void OnValidate()
{
if (baseRectTransform == null)
baseRectTransform = GetComponent<RectTransform>();
if (!Application.isPlaying && baseRectTransform != null)
{
UnityEditor.EditorApplication.delayCall += () => {
if (this != null) UpdateAllProgress();
};
}
}
#endif
}
创建UI结构:
创建 Canvas
在 Canvas 下创建空对象 “MultiProgress”
创建空对象 “TextContainer” 作为文本父对象
准备预制体:
创建 Image 对象,命名为 “SegmentPrefab”
设置为 Filled 类型,Fill Method 为 Radial 360
将其做成预制体
准备文本模板:
创建 Text 对象,设置好字体样式
初始设置为非激活状态
命名为 “PercentTextTemplate”
添加组件:
给 “MultiProgress” 对象添加 MultiCircleProgress 脚本
将预制体和文本模板拖拽到脚本对应字段
在 Inspector 中添加和配置各个段