How to best to do a radial progress circle?

For anyone else looking for how to do this, I found Graphs and Charts and used that code with a small refactor.

I made an empty VisualElement with the correct x/y size called “circle-holder”, and then did VisualElement.Add(new CooldownCircle()) into it. This has been working great.

public class CooldownCircle : VisualElement {
  private float _fraction;
  public CooldownCircle() {
    generateVisualContent += OnGenerateVisualContent;
  }
  public void SetCooldownRemaining(float fraction) {
    _fraction = fraction;
  }
  void OnGenerateVisualContent(MeshGenerationContext mgc) {
    Rect rect = contentRect;
    // TODO: It's not clear why height/y is always 0, even after waiting for resolvedStyle, but width/x seems to be correct.
    Vector2 center = new(rect.center.x, rect.center.x);
    // Fill 100% of the square not just a circle.
    float radius = rect.size.x * 1.5f / 2;
    // Vertical up is -PI/2. Ending at 3*PI/2 is the full circle.
    float startAngle = -Mathf.PI / 2.0f + (1 - _fraction) * 2 * Mathf.PI;
    CircleCreator.Circle(center, radius, startAngle, 3*Mathf.PI/2.0f, GameColors.CooldownCircle, mgc);
  }
}

public static class CircleCreator {
  public static readonly int NumSegments = 250;
  public static void Circle(Vector2 pos, float radius, float startAngle, float endAngle, Color color, MeshGenerationContext context) {
    var segments = NumSegments;
    var mesh = context.Allocate(segments + 1, (segments - 1) * 3);
    mesh.SetNextVertex(new Vertex() { position = new Vector2(pos.x, pos.y), tint = color });
    var angle = startAngle;
    var range = endAngle - startAngle;
    var step = range / (segments - 1);
    for (int i = 0; i < segments; i++) {
      Vector2 offset = new(radius * Mathf.Cos(angle), radius * Mathf.Sin(angle));
      mesh.SetNextVertex(new Vertex() { position = new Vector2(pos.x, pos.y) + offset, tint = color });
      angle += step;
    }
    for (ushort i = 1; i < segments; i++) {
      mesh.SetNextIndex(0);
      mesh.SetNextIndex(i);
      mesh.SetNextIndex((ushort)(i + 1));
    }
  }
}
6 Likes