- Home /
Draw circle
How to draw a circle at position of an object just like in RTS when you select units u have circles around selected units. Please explain step by step and from A to Z
There's no built-in functionality for drawing circles in Unity. You are left with a bunch of choices that you need to research:
You may be able to use a circle texture. If the objects you are circling don't vary in screen size too much, you can create a circle texture in Photoshop. Depending on the use, you can display the texture using a Quad, GUITexture, or GUI.DrawTexture().
You can create an array of circular points and draw a line using the line renderer or an add-on package like Vectorosity.
You could elect to highlight not by circling but by changing the material or texture in some way, or by using a shader that outlines the object.
You could write a shader that draws a circle mathematically.
You could create a circular (donut-hole) mesh in a modeling program and size and position the mesh to circle an object.
Thank You. I guess the first choice would be the easiest, Writing a shader will be a pain in ... ;)
And another option might be projectors. Drawing circles in Unity has been asked and answered before in a variety of ways. Do some searching to figure out what is the best fit for your specific app.
Answer by Cherno · Apr 22, 2015 at 01:31 PM
The Unity wiki has a script "Drawing.cs", there's also a thread on the forums. I modified it a little:
using System.Reflection;
using UnityEngine;
// Line drawing routine originally courtesy of Linusmartensson:
// http://forum.unity3d.com/threads/71979-Drawing-lines-in-the-editor
//
// Rewritten to improve performance by Yossarian King / August 2013.
//
// This version produces virtually identical results to the original (tested by drawing
// one over the other and observing errors of one pixel or less), but for large numbers
// of lines this version is more than four times faster than the original, and comes
// within about 70% of the raw performance of Graphics.DrawTexture.
//
// Peak performance on my laptop is around 200,000 lines per second. The laptop is
// Windows 7 64-bit, Intel Core2 Duo CPU 2.53GHz, 4G RAM, NVIDIA GeForce GT 220M.
// Line width and anti-aliasing had negligible impact on performance.
//
// For a graph of benchmark results in a standalone Windows build, see this image:
// https://app.box.com/s/hyuhi565dtolqdm97e00
//
// For a Google spreadsheet with full benchmark results, see:
// https://docs.google.com/spreadsheet/ccc?key=0AvJlJlbRO26VdHhzeHNRMVF2UHZHMXFCTVFZN011V1E&usp=sharing
public static class Drawing
{
private static Texture2D aaLineTex = null;
private static Texture2D lineTex = null;
private static Material blitMaterial = null;
private static Material blendMaterial = null;
private static Rect lineRect = new Rect(0, 0, 1, 1);
// Draw a line in screen space, suitable for use from OnGUI calls from either
// MonoBehaviour or EditorWindow. Note that this should only be called during repaint
// events, when (Event.current.type == EventType.Repaint).
//
// Works by computing a matrix that transforms a unit square -- Rect(0,0,1,1) -- into
// a scaled, rotated, and offset rectangle that corresponds to the line and its width.
// A DrawTexture call used to draw a line texture into the transformed rectangle.
//
// More specifically:
// scale x by line length, y by line width
// rotate around z by the angle of the line
// offset by the position of the upper left corner of the target rectangle
//
// By working out the matrices and applying some trigonometry, the matrix calculation comes
// out pretty simple. See https://app.box.com/s/xi08ow8o8ujymazg100j for a picture of my
// notebook with the calculations.
public static void DrawLine(Vector2 pointA, Vector2 pointB, Color color, float width, bool antiAlias)
{
// Normally the static initializer does this, but to handle texture reinitialization
// after editor play mode stops we need this check in the Editor.
#if UNITY_EDITOR
if (!lineTex)
{
Initialize();
}
#endif
// Note that theta = atan2(dy, dx) is the angle we want to rotate by, but instead
// of calculating the angle we just use the sine (dy/len) and cosine (dx/len).
float dx = pointB.x - pointA.x;
float dy = pointB.y - pointA.y;
float len = Mathf.Sqrt(dx * dx + dy * dy);
// Early out on tiny lines to avoid divide by zero.
// Plus what's the point of drawing a line 1/1000th of a pixel long??
if (len < 0.001f)
{
return;
}
// Pick texture and material (and tweak width) based on anti-alias setting.
Texture2D tex;
Material mat;
if (antiAlias)
{
// Multiplying by three is fine for anti-aliasing width-1 lines, but make a wide "fringe"
// for thicker lines, which may or may not be desirable.
width = width * 3.0f;
tex = aaLineTex;
mat = blendMaterial;
}
else
{
tex = lineTex;
mat = blitMaterial;
}
float wdx = width * dy / len;
float wdy = width * dx / len;
Matrix4x4 matrix = Matrix4x4.identity;
matrix.m00 = dx;
matrix.m01 = -wdx;
matrix.m03 = pointA.x + 0.5f * wdx;
matrix.m10 = dy;
matrix.m11 = wdy;
matrix.m13 = pointA.y - 0.5f * wdy;
// Use GL matrix and Graphics.DrawTexture rather than GUI.matrix and GUI.DrawTexture,
// for better performance. (Setting GUI.matrix is slow, and GUI.DrawTexture is just a
// wrapper on Graphics.DrawTexture.)
GL.PushMatrix();
GL.MultMatrix(matrix);
//Graphics.DrawTexture(lineRect, tex, lineRect, 0, 0, 0, 0, color, mat);
//Replaced by:
GUI.color = color;//this and...
GUI.DrawTexture( lineRect, tex );//this
GL.PopMatrix();
}
public static void DrawCircle(Vector2 center, int radius, Color color, float width, int segmentsPerQuarter) {
DrawCircle(center, radius, color, width, false, segmentsPerQuarter);
}
public static void DrawCircle(Vector2 center, int radius, Color color, float width, bool antiAlias, int segmentsPerQuarter) {
float rh = (float)radius / 2;
Vector2 p1 = new Vector2(center.x, center.y - radius);
Vector2 p1_tan_a = new Vector2(center.x - rh, center.y - radius);
Vector2 p1_tan_b = new Vector2(center.x + rh, center.y - radius);
Vector2 p2 = new Vector2(center.x + radius, center.y);
Vector2 p2_tan_a = new Vector2(center.x + radius, center.y - rh);
Vector2 p2_tan_b = new Vector2(center.x + radius, center.y + rh);
Vector2 p3 = new Vector2(center.x, center.y + radius);
Vector2 p3_tan_a = new Vector2(center.x - rh, center.y + radius);
Vector2 p3_tan_b = new Vector2(center.x + rh, center.y + radius);
Vector2 p4 = new Vector2(center.x - radius, center.y);
Vector2 p4_tan_a = new Vector2(center.x - radius, center.y - rh);
Vector2 p4_tan_b = new Vector2(center.x - radius, center.y + rh);
DrawBezierLine(p1, p1_tan_b, p2, p2_tan_a, color, width, antiAlias, segmentsPerQuarter);
DrawBezierLine(p2, p2_tan_b, p3, p3_tan_b, color, width, antiAlias, segmentsPerQuarter);
DrawBezierLine(p3, p3_tan_a, p4, p4_tan_b, color, width, antiAlias, segmentsPerQuarter);
DrawBezierLine(p4, p4_tan_a, p1, p1_tan_a, color, width, antiAlias, segmentsPerQuarter);
}
// Other than method name, DrawBezierLine is unchanged from Linusmartensson's original implementation.
public static void DrawBezierLine(Vector2 start, Vector2 startTangent, Vector2 end, Vector2 endTangent, Color color, float width, bool antiAlias, int segments)
{
Vector2 lastV = CubeBezier(start, startTangent, end, endTangent, 0);
for (int i = 1; i < segments + 1; ++i)
{
Vector2 v = CubeBezier(start, startTangent, end, endTangent, i/(float)segments);
Drawing.DrawLine(lastV, v, color, width, antiAlias);
lastV = v;
}
}
private static Vector2 CubeBezier(Vector2 s, Vector2 st, Vector2 e, Vector2 et, float t)
{
float rt = 1 - t;
return rt * rt * rt * s + 3 * rt * rt * t * st + 3 * rt * t * t * et + t * t * t * e;
}
// This static initializer works for runtime, but apparently isn't called when
// Editor play mode stops, so DrawLine will re-initialize if needed.
static Drawing()
{
Initialize();
}
private static void Initialize()
{
if (lineTex == null)
{
lineTex = new Texture2D(1, 1, TextureFormat.ARGB32, false);
lineTex.SetPixel(0, 1, Color.white);
lineTex.Apply();
}
if (aaLineTex == null)
{
// TODO: better anti-aliasing of wide lines with a larger texture? or use Graphics.DrawTexture with border settings
aaLineTex = new Texture2D(1, 3, TextureFormat.ARGB32, false);
aaLineTex.SetPixel(0, 0, new Color(1, 1, 1, 0));
aaLineTex.SetPixel(0, 1, Color.white);
aaLineTex.SetPixel(0, 2, new Color(1, 1, 1, 0));
aaLineTex.Apply();
}
// GUI.blitMaterial and GUI.blendMaterial are used internally by GUI.DrawTexture,
// depending on the alphaBlend parameter. Use reflection to "borrow" these references.
blitMaterial = (Material)typeof(GUI).GetMethod("get_blitMaterial", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null);
blendMaterial = (Material)typeof(GUI).GetMethod("get_blendMaterial", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null);
}
}
This is great, however for larger circles it becomes obvious that the circle drawn isn't 100% circular. I assume this is an artifact of using BezierCurves to make up the arcs.
I know it's been a year since this question being asked. But for you guys who couldn't find drawing circle in unity editor, just import this script into your project and just simply call Drawing.DrawCircle (new Vector2 (200f, 200f), 100, Color.black, 5, 9);
, for example.
This really save my ass.
@EarthHobbit sorry I just realized I post nothing. https://github.com/kertaskids/UnityExtension/blob/master/Assets/Editor/Drawing.cs
Answer by justaddice83 · Apr 22, 2015 at 10:12 AM
For the context you explained, I would suggest using a projected material:
http://docs.unity3d.com/Manual/class-Projector.html
The texture applied to the material can be a circle and will 'project' over the terrain and other objects. I believe this is what the big RTS games use :)
Your answer
Follow this Question
Related Questions
GUI.DrawTexture not working 2 Answers
How to change rect size? 2 Answers
GUI.DrawTexture not working? (nothing shows up) 1 Answer
Why doesn't my texture show up? 1 Answer