How can I draw a circle in Unity3D?
Asked Answered
J

11

18

How to draw circle in Unity 3d? I want to draw a circle around different objects. The radiuses of the circles are different and the circles have textures - squares.

Jalousie answered 4/12, 2012 at 17:19 Comment(0)
A
17

I found a big error with this code. The number of points (Size) shouldn't be "(2 * pi / theta_scale) + 1" because this causes the circle to draw 6.28 times. The size should be "1 / theta_scale + 1". So for a theta_scale of 0.01 it needs to draw 100 points, and for a theta_scale of 0.1 it needs to draw 10 points. Otherwise it would draw 62 times and 628 times respectively. Here is the code I used.

using UnityEngine;
using System.Collections;

public class DrawRadar: MonoBehaviour {
    public float ThetaScale = 0.01f;
    public float radius = 3f;
    private int Size;
    private LineRenderer LineDrawer;
    private float Theta = 0f;

    void Start() {
        LineDrawer = GetComponent<LineRenderer>();
    }

    void Update() {
        Theta = 0f;
        Size = (int)((1f / ThetaScale) + 1f);
        LineDrawer.SetVertexCount(Size);
        for (int i = 0; i < Size; i++) {
            Theta += (2.0f * Mathf.PI * ThetaScale);
            float x = radius * Mathf.Cos(Theta);
            float y = radius * Mathf.Sin(Theta);
            LineDrawer.SetPosition(i, new Vector3(x, y, 0));
        }
    }
}

If you modify the number in "Size" that is divided by ThetaScale, you can make a sweeping gauge/pie chart type graphic.

Anatolia answered 2/8, 2015 at 1:18 Comment(2)
This should be the correct answer, because @Jerdak's code draws so many unnecessary lines, but this code just passes through once and draws each corner in a single pass. Though, please note that for an imperforate line, you should do this instead: Size = (int)((1f / ThetaScale) + 2f)Vincent
SetVertexCount is deprecated now. Also for anyone wondering where their circle is, this creates a vertical ring at the world origin if you don't have use world space off. I added an answer for newer versions of unity.Daphinedaphna
B
10

See Unity Answers for a similar question.

Alternatively:

float theta_scale = 0.1;  // Circle resolution

LineRenderer lineRenderer = gameObject.AddComponent<LineRenderer>();
lineRenderer.material = new Material(Shader.Find("Particles/Additive"));
lineRenderer.SetColors(c1, c2);
lineRenderer.SetWidth(0.2F, 0.2F);
lineRenderer.SetVertexCount(size);

int i = 0;
for(float theta = 0; theta < 2 * PI; theta += theta_scale) {
    x = r*cos(theta);
    y = r*sin(theta);

    Vector3 pos = new Vector3(x, y, 0);
    lineRenderer.SetPosition(i, pos);
    i+=1;
}

The LineRenderer requires continuous points. You can modify this code slightly to use cylinder game objects instead of a line renderer. I find the LineRenderer to be a bit hideous.

Lastly, similar to the first link, you could attach a circle texture to a unit plane. Make any part of the texture that isn't part of the circle transparent. Then just scale and align the plane to fit your object. Unfortunately this method isn't great if someone is looking almost parallel to the plane.

Biddie answered 4/12, 2012 at 20:18 Comment(0)
G
5

Jerdak's solution is good, but the code is messy so I had to tweak a little. Here's the code for a class, where I use i in the loop to avoid a bug.

It also updates the circle's position with its gameObject position.

using UnityEngine;
using System.Collections;

public class CircleDraw : MonoBehaviour {   
  float theta_scale = 0.01f;        //Set lower to add more points
  int size; //Total number of points in circle
  float radius = 3f;
  LineRenderer lineRenderer;

  void Awake () {       
    float sizeValue = (2.0f * Mathf.PI) / theta_scale; 
    size = (int)sizeValue;
    size++;
    lineRenderer = gameObject.AddComponent<LineRenderer>();
    lineRenderer.material = new Material(Shader.Find("Particles/Additive"));
    lineRenderer.SetWidth(0.02f, 0.02f); //thickness of line
    lineRenderer.SetVertexCount(size);      
  }

  void Update () {      
    Vector3 pos;
    float theta = 0f;
    for(int i = 0; i < size; i++){          
      theta += (2.0f * Mathf.PI * theta_scale);         
      float x = radius * Mathf.Cos(theta);
      float y = radius * Mathf.Sin(theta);          
      x += gameObject.transform.position.x;
      y += gameObject.transform.position.y;
      pos = new Vector3(x, y, 0);
      lineRenderer.SetPosition(i, pos);
    }
  }
}
Garganey answered 25/5, 2015 at 15:13 Comment(0)
T
4

Using Shader Graph we can now draw pixel perfect circle.

Unlit Shader Graph

Once you created this graph, create a new material based on this shader.

result

Then create a new gameobject with a sprite renderer and set the material you just created.

You can scale the circle using the "scale" parameter of the material.

Tiphany answered 5/10, 2018 at 1:40 Comment(1)
Thanks for an alternative solution. As a coder I found this the most helpful.Tressa
D
3

The linerenderer method in the top answers is really simple and exactly what I was looking for. I updated it for newer versions of Unity and some small tweaks to make it a bit more beginner/user friendly.

Specifically:

  • LineRenderer.SetVertexCount() is deprecated in newer versions of Unity, replaced with positionCount
  • Replaced theta scale with an actual segment count to remove guesswork
  • Added loop setting - not sure if this was in older versions of Unity, it can be set in the LineRenderer's inspector
  • Removed unnecessary Update function - the rendered line is a persistent gameobject
using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class DrawRing : MonoBehaviour
{
    public LineRenderer lineRenderer;
    [Range(6,60)]   //creates a slider - more than 60 is hard to notice
    public int lineCount;       //more lines = smoother ring
    public float radius;
    public float width;

    void Start()
    {
        lineRenderer = GetComponent<LineRenderer>();
        lineRenderer.loop = true;
        Draw();
    }

    void Draw() //Only need to draw when something changes
    {
        lineRenderer.positionCount = lineCount;
        lineRenderer.startWidth = width;
        float theta = (2f * Mathf.PI) / lineCount;  //find radians per segment
        float angle = 0;
        for (int i = 0; i < lineCount; i++)
        {
            float x = radius * Mathf.Cos(angle);
            float y = radius * Mathf.Sin(angle);
            lineRenderer.SetPosition(i, new Vector3(x, 0, y));
            //switch 0 and y for 2D games
            angle += theta;
        }
    }
}

Note this is assumed to be attached to the gameobject you want the ring around. So the Use World Space option in LineRenderer should be unchecked. Also remember that the scale of the gameobject will affect the position of the points and the width of the line.

To put this on the ground (as in a unit selection circle):

  1. Put the script on a separate gameobject
  2. Rotate the gameobject X to 90
  3. Check use world space on the linerenderer
  4. Set the linerenderer Alignment to Transform Z
  5. Add the position of the thing you want to circle to x and y in SetPosition. Possibly along with replacing 0 with 0.1f or a yOffset variable to avoid z-fighting with terrain.
Daphinedaphna answered 11/1, 2022 at 19:31 Comment(0)
J
0

Circle can draw using shader - draw pixel if it on radius from center.

Jalousie answered 25/2, 2015 at 7:15 Comment(0)
C
0

Did the following with a Sprite. Chan is flying in the scene, so she's slightly above the plane. I had her flying so I could get a good screenshot, not because it wouldn't play well with the plane.

I used a low-resolution circle sprite. X rotation 90 Scale X 15, Y 15, Z 1

Then I set the Sorting Layer, so it will render above the Default Layer. I was testing this out when I came across this post. It doesn't handle shadows well. I'd have to figure out what layer shadows are drawn on to make sure they get rendered onto the sprite.

enter image description here

Cletacleti answered 1/10, 2016 at 6:27 Comment(0)
M
0

I have a shader from which I usually start making effects like lens flares, and it makes a circle. Using shader is the best choice because you will get perfectly smooth and round circle.

Also it's easy to experiment with and tune the shader since shader changes don't require recompile and re-entering of play mode.

Megavolt answered 14/10, 2018 at 19:12 Comment(0)
A
0

I recommend ti create extension method to GameObject. Worked good to me.

public static class GameObjectExtension
{
    const int numberOfSegments = 360;
    public static void DrawCircle(this GameObject go, float radius, 
float lineWidth, Color startColor, Color endColor, bool lineRendererExists=true)
    {
        LineRenderer circle = lineRendererExists ? go.GetComponent<LineRenderer>() : go.AddComponent<LineRenderer>();
        circle.useWorldSpace = false;
        circle.startWidth = lineWidth;
        circle.endWidth = lineWidth;
        circle.endColor = endColor;
        circle.startColor = startColor;
        circle.positionCount = numberOfSegments + 1;
        Vector3 [] points = new Vector3[numberOfSegments + 1];

        for (int i = 0; i < numberOfSegments + 1; i++)
        {
            float rad = Mathf.Deg2Rad * i;
            points[i] = new Vector3(Mathf.Sin(rad) * radius, 0, Mathf.Cos(rad) * radius);
        }
        circle.SetPositions(points);
    }
}

One More thing to note: If LineRenderer component is not applied last parameter has to be false

Appleby answered 22/9, 2019 at 15:25 Comment(0)
W
0
  • create a static class to reuse the code for different game objects. player, enemies... when the class is static, you cannot create the instance of it

    public static class CircleGameObject
    
    {       
      // in static class methods have to be static as well
      // "this" refers to the context that we are calling DrawCircle
      public static async void DrawCircle(this GameObject container,float radius,float lineWidth)
      {
           // I provide 360 points because circle is 360 degrees and we will connect them with line
          var segments=360;
          //  LineRenderer is used to draw line
          var lineRenderer=container.AddComponent<LineRenderer>();
           // now you can use position system relative to the parent game object. 
          lineRenderer.useWorldSpace=false;
          lineRenderer.startWidth=lineWidth;
          lineRenderer.endWidth=lineWidth;
          lineRenderer.positionCount=segments+1;
          // reserve empty array in memory with a size of lineRenderer.positionCount
          var points=new Vector3[lineRenderer.positionCount];
    
          // draw all of those points
          for(int i=0;i<points.Length;i++)
          {
              // converting degree to radian because Mathf.Cos and Mathf.Sin expects radian 
              var radian=Mathf.Deg2Rad*i;
              // y direction needs to be 0
              // Mathf.Cos(radiant) will give the x position on the circle if the angle size is "radian"
              // Mathf.Sin(radiant) will give the y position on the circle if the angle size is "radian"
              // after for loop completes we would be getting 360 points
              points[i]=new Vector3(Mathf.Cos(radian)*radius,0,Mathf.Sin(radian)*radius);
          }
          lineRenderer.SetPositions(points);
      }
    }
    
  • then call it in Awake of the context

     public class PlayerController : MonoBehaviour
      {
           private void Awake() 
            {
              GameObject go=new GameObject{
                  name="Circle"
              };
              Vector3 circlePosition=Vector3.zero;
              go.transform.parent=transform;
              // localPosition is relative to the parent
              go.transform.localPosition=circlePosition;
              go.DrawCircle(2.0f,0.03f);
              ....
          }
       }
    
Whipple answered 23/7, 2022 at 23:51 Comment(0)
O
0

The best solution for me.:

public float radius = 5.0f;
public int segments = 32;
public Color color = Color.yellow;

private LineRenderer lineRenderer;

private void Start()
{
    // Set up line renderer
    lineRenderer = GetComponent<LineRenderer>();
    lineRenderer.positionCount = segments + 1;
    lineRenderer.useWorldSpace = false;
    lineRenderer.startWidth = 0.1f;
    lineRenderer.endWidth = 0.1f;

    // color of line
    lineRenderer.material = new Material(Shader.Find("Sprites/Default"));
    lineRenderer.material.color = color;

    // positions in the circle
    Vector3[] positions = new Vector3[segments + 1];
    float angle = 0f;
    float angleStep = 2f * Mathf.PI / segments;

    for (int i = 0; i < segments + 1; i++)
    {
        positions[i] = new Vector3(radius * Mathf.Cos(angle), 0f, radius * Mathf.Sin(angle));
        angle += angleStep;
    }

    // set up positions to renderer
    lineRenderer.SetPositions(positions);
}

Dont forget about [RequireComponent(typeof(LineRenderer))] above class name.

Onym answered 10/3, 2023 at 20:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.