- Home /
Draw 2d circle with Gizmos
Hi!
I'm creating a script that draws gizmos for all 2D colliders in my 2D scene. My question is: how do I draw a gizmo for a 2D circle the same way you see gizmos when selecting an object? The only function in Gizmos that I find close to this is "DrawWireSphere()" but it draws a sphere and i can only give it a position and radius and not a rotation.
Here's what I want (from a 3D perspective):
Here's what I have right now (from a 3D perspective):
So I simply just want to draw the circle in the xy-plane and have the options to rotate on any axis.
To see the Z-rotation from a 2D-perspective I will just draw lines like a cross over the circle. I can do that now aswell but it will be confusing to see all the sphere circles at the same time.
EDIT: The circle i want to draw should be for all game objects in the scene and not only targeted game objects.
SOLUTION
As suggested by buckius82 below, a solution for this is to make a monobehavior script that retrieves all CircleCollider2D's in the scene and then draw wire discs (UnityEdtior.Handles.DrawWireDisc) under the OnDrawGizmos() method.
Example:
private void OnDrawGizmos(){
UnityEditor.Handles.color = Color.yellow;
CircleCollider2D[] colliders = FindObjectsOfType (typeof(CircleCollider2D)) as Collider2D[];
foreach(CircleCollider2D collider in colliders){
UnityEditor.Handles.DrawWireDisc(collider.transform.position ,Vector3.back, collider.radius);
}
}
Note that this may take some performance if there are alot of colliders in the scene.
Answer by buckius82 · Jan 31, 2015 at 04:47 PM
I wanted to make 2d circle in 3d to represent spread in the editor.
Look At editor.handles
using UnityEditor;
using UnityEngine;
public class customclassEditor: MonoBehaviour
{
public float variance; // This much variance
public float maxdistance; // at this distance
}
using UnityEditor;
using UnityEngine;
using System.Collections;
[CustomEditor(typeof(customclass))]
public class customclassEditor: Editor
{
private customclass c;
public void OnSceneGUI ()
{
c = this.target as customclass;
Handles.color = Color.red;
Handles.DrawWireDisc(c.transform.position +(c.transform.forward *c.Range) // position
, c.transform.forward // normal
, c.variance); // radius
}
}
i hop this can get you started
@buckius82, Thanks for answer. I can see your example looks much like the one from the documentation of drawWireDisc, I have tried this but as mentioned in the comments to yashpal response, this only makes the circle of the targeted game object visible and not all game objects in the scene with a CircleCollider2D attached. I forgot to mention this in the question so I will edit the question and add that it needs to be drawn for all game objects and not only targeted game objects.
you ready, i wanted to be able to see all turret spread patterns on a GO. onSceneGUI only works if the OBJ is slelected. so if i had multple turrets as children of a single parent i can't see the arcs and spread. but i can using OnDrawGizmosSelected()
here is a class for you using UnityEngine; using System.Collections;
public class CircleAroundEachGameObject : $$anonymous$$onoBehaviour
{
void OnDrawGizmosSelected()
{
GameObject[] objs = FindObjectsOfType(typeof(GameObject))as GameObject[];
foreach (GameObject g in objs)
{
UnityEditor.Handles.color = Color.green;
UnityEditor.Handles.DrawWireDisc(g.transform.position , g.transform.up, 2f);
}
}
}
now if you attach this to a obj in the scene it will show a circle around every game object.
now lets say you want it to always show it then use OnDrawGizmos() ins$$anonymous$$d.
now i'm asu$$anonymous$$g these represent 2d coliders try
using UnityEngine;
using System.Collections;
public class CircleAroundEachGameObject : $$anonymous$$onoBehaviour
{
public float radius;
public Vector3 direction;
void OnDrawGizmosSelected()
{
direction = Vector3.Normalize(direction);
GameObject[] objs = FindObjectsOfType(typeof(GameObject))as GameObject[];
foreach (GameObject g in objs)
{
float r = radius;
CircleCollider2D c2d = g.GetComponent<CircleCollider2D>();
if(c2d != null)
{ r = c2d.radius;
UnityEditor.Handles.color = Color.green;
UnityEditor.Handles.DrawWireDisc(g.transform.position ,direction, r);
}
}
}
}
if you want to be able to modify the colliders using the these circles through drag and drop you'll have to use onscene gui and create the editor controls look here http://catlikecoding.com/unity/tutorials/curves-and-splines/
@buckius82, right that solves it, I didn't think of using Handles inside the OnDrawGizmos method as it's mainly meant for drawing gizmos but this solves the problem well enough to satisfy me :p Will edit the post and add a short description for the solution for people to find it easier. cheers
The problem with using Handles inside OnDrawGizmos is that it won't compile to a binary. You have to enclose it in #if UNITY_EDITOR / #endif in order to make a build.
Answer by xibanya · Dec 21, 2019 at 02:32 AM
I also wanted to draw gizmo disks. Here's the solution I came up with, which doesn't require using an editor script. You can stick it into a static utility class, then call it with transform.DrawGizmoDisk(radius: 0.5f)
or whatever.
private const float GIZMO_DISK_THICKNESS = 0.01f;
public static void DrawGizmoDisk(this Transform t, float radius)
{
Matrix4x4 oldMatrix = Gizmos.matrix;
Gizmos.color = new Color(0.2f, 0.2f, 0.2f, 0.5f); //this is gray, could be anything
Gizmos.matrix = Matrix4x4.TRS(t.position, t.rotation, new Vector3(1, GIZMO_DISK_THICKNESS, 1));
Gizmos.DrawSphere(Vector3.zero, radius);
Gizmos.matrix = oldMatrix;
}
Bit hacky (it's just a squished sphere) but good enough for visualizing a radius!
You can also easily modify this to draw a wire sphere instead, or both at the same time (as pictured here), but since this is a squished 3d gizmo, if you draw the wire sphere, there's no avoiding the "cross" that shows up with it. Still, it's probably the closest you can get without having to use the custom editor APIs (I consider that a plus if I am otherwise not gonna need a custom editor!)
Here's an example of drawing the squished wire sphere that takes a position as an argument rather than being an extension method of a transform.
public static void DrawWireDisk(Vector3 position, float radius, Color color)
{
Color oldColor = Gizmos.color;
Gizmos.color = color;
Matrix4x4 oldMatrix = Gizmos.matrix;
Gizmos.matrix = Matrix4x4.TRS(position, Quaternion.identity, new Vector3(1, GIZMO_DISK_THICKNESS, 1));
Gizmos.DrawWireSphere(Vector3.zero, radius);
Gizmos.matrix = oldMatrix;
Gizmos.color = oldColor;
}
I really love this solution! Also, it $$anonymous$$ches how to use the matrix field in a very practical way.
In particular, it makes it clear how to re-orient the facing just by changing Quaternion.identity to your favourite "up" rotation (or, the rotation of the transform)
too bad the number of subdivisions of the sphere can't be increased! :)
Answer by yashpal · Jan 31, 2015 at 12:55 PM
@davidisak, I think you need to make your custom editor script to do this.(correct me if i am wrong.). You want to use Handles.DrawWireDisc. use Handles for more customization.
@yashpal, Thanks for the answer, this sounds just like what I need. I will try it out when I get the time and verify your answer as correct if it works well!
@yashpal, I tried it out but realized that the OnSceneGUI in my customeditor is only called when I select the game object. What I want is to draw the circles for all CircleCollider2D's in the scene without having to select them. Any idea on this?
@davidisak: in this case you have two options:
attach a script to each collider with implements OnDrawGizmos
use an editorwindow and register an onsceneview callback in the SceneView class. In this case you have to draw all gizmos in that one callback
@Bunny83, I currently have a script that retrieves all colliders in the scene and draws gizmos for them in onDrawGizmos(), however my problem is that there are no such function in Gizmos that lets you draw a simple 2d circle such as the DrawWireDisc in Handles. So now I am now wondering if I can use the Handles draw-functions for all colliders in the scene (and not only for selected objects) ins$$anonymous$$d of gizmos, as suggested by yashpal.
Answer by James2Games · Nov 05, 2019 at 09:50 PM
Here's a solution using Gizmos. Only tested this so it displays in a world space canvas in the Z direction but can be adjusted in the Euler method to display on any angle.
float corners = 11; // How many corners the circle should have
float size = 10; // How wide the circle should be
Vector3 origin = transform.position; // Where the circle will be drawn around
Vector3 startRotation = transform.right * size; // Where the first point of the circle starts
Vector3 lastPosition = origin + startRotation;
float angle = 0;
while (angle <= 360)
{
angle += 360 / corners;
Vector3 nextPosition = origin + (Quaternion.Euler(0, 0, angle) * startRotation);
Gizmos.DrawLine(lastPosition, nextPosition);
Gizmos.DrawSphere(nextPosition, 1);
lastPosition = nextPosition;
}
Answer by Owen9047 · Feb 09 at 03:44 AM
I wanted a version of @James2Games solution that takes a normal and position instead of a full transform. Here's what I came up with:
public static void DrawGizmosCircle(Vector3 pos, Vector3 normal, float radius, int numSegments)
{
// I think of normal as conceptually in the Y direction. We find the
// "forward" and "right" axes relative to normal and I think of them
// as the X and Z axes, though they aren't in any particular direction.
// All that matters is that they're perpendicular to each other and on
// the plane defined by pos and normal.
Vector3 temp = (normal.x < normal.z) ? new Vector3(1f, 0f, 0f) : new Vector3(0f, 0f, 1f);
Vector3 forward = Vector3.Cross(normal, temp).normalized;
Vector3 right = Vector3.Cross(forward, normal).normalized;
Vector3 prevPt = pos + (forward * radius);
float angleStep = (Mathf.PI * 2f) / numSegments;
for (int i = 0; i < numSegments; i++)
{
// Get the angle for the end of this segment. If it's the last segment,
// use the angle of the first point so the last segment meets up with
// the first point exactly (regardless of floating point imprecision).
float angle = (i == numSegments - 1) ? 0f : (i + 1) * angleStep;
// Get the segment end point in local space, i.e. pretend as if the
// normal was (0, 1, 0), forward was (0, 0, 1), right was (1, 0, 0),
// and pos was (0, 0, 0).
Vector3 nextPtLocal = new Vector3(Mathf.Sin(angle), 0f, Mathf.Cos(angle)) * radius;
// Transform from local to world coords. nextPtLocal's x,z are distances
// along its axes, so we want those as the distances along our right and
// forward axes.
Vector3 nextPt = pos + (right * nextPtLocal.x) + (forward * nextPtLocal.z);
Gizmos.DrawLine(prevPt, nextPt);
prevPt = nextPt;
}
}