Search Unity

Drawing Capsule gizmo ?

Discussion in 'Editor & General Support' started by Vagabond_, Sep 13, 2015.

  1. Vagabond_

    Vagabond_

    Joined:
    Aug 26, 2014
    Posts:
    1,148
    Does anybody know a way of drawing capsule gizmo in editor?

    There are many shapes in the Gizmos class but no capsule. I assume there is a way while the Capsule primitive and the standard Capsule collider do it.
     
  2. Vagabond_

    Vagabond_

    Joined:
    Aug 26, 2014
    Posts:
    1,148
    Anyone ?
     
  3. Flavelius

    Flavelius

    Joined:
    Jul 8, 2012
    Posts:
    945
  4. Vagabond_

    Vagabond_

    Joined:
    Aug 26, 2014
    Posts:
    1,148
    This is good enough. Thanks a ton.
     
  5. Shushustorm

    Shushustorm

    Joined:
    Jan 6, 2014
    Posts:
    1,084
    When I go into the scene or game tab, I can check "Capsule collider" in the gizmos menu.
    Isn't that what you are asking for?
     
  6. Flavelius

    Flavelius

    Joined:
    Jul 8, 2012
    Posts:
    945
    That only enables unitys drawing of the capsule of existing colliders in your scene.
     
  7. Vagabond_

    Vagabond_

    Joined:
    Aug 26, 2014
    Posts:
    1,148
    It seems the only way to draw a custom capsule is to draw different elements in combination - "lines", "circles" and "arcs", which is achievable.
     
  8. Shushustorm

    Shushustorm

    Joined:
    Jan 6, 2014
    Posts:
    1,084
    Alright, I guess I misunderstood the question!
     
  9. Orr10c

    Orr10c

    Joined:
    Sep 11, 2016
    Posts:
    45
  10. toomasio

    toomasio

    Joined:
    Nov 19, 2013
    Posts:
    198
    Code (CSharp):
    1.  
    2. public static void DrawWireCapsule(Vector3 _pos, Quaternion _rot, float _radius, float _height, Color _color = default(Color))
    3.     {
    4.         if (_color != default(Color))
    5.             Handles.color = _color;
    6.         Matrix4x4 angleMatrix = Matrix4x4.TRS(_pos, _rot, Handles.matrix.lossyScale);
    7.         using (new Handles.DrawingScope(angleMatrix))
    8.         {
    9.             var pointOffset = (_height - (_radius * 2)) / 2;
    10.  
    11.             //draw sideways
    12.             Handles.DrawWireArc(Vector3.up * pointOffset, Vector3.left, Vector3.back, -180, _radius);
    13.             Handles.DrawLine(new Vector3(0, pointOffset, -_radius), new Vector3(0, -pointOffset, -_radius));
    14.             Handles.DrawLine(new Vector3(0, pointOffset, _radius), new Vector3(0, -pointOffset, _radius));
    15.             Handles.DrawWireArc(Vector3.down * pointOffset, Vector3.left, Vector3.back, 180, _radius);
    16.             //draw frontways
    17.             Handles.DrawWireArc(Vector3.up * pointOffset, Vector3.back, Vector3.left, 180, _radius);
    18.             Handles.DrawLine(new Vector3(-_radius, pointOffset, 0), new Vector3(-_radius, -pointOffset, 0));
    19.             Handles.DrawLine(new Vector3(_radius, pointOffset, 0), new Vector3(_radius, -pointOffset, 0));
    20.             Handles.DrawWireArc(Vector3.down * pointOffset, Vector3.back, Vector3.left, -180, _radius);
    21.             //draw center
    22.             Handles.DrawWireDisc(Vector3.up * pointOffset, Vector3.up, _radius);
    23.             Handles.DrawWireDisc(Vector3.down * pointOffset, Vector3.up, _radius);
    24.  
    25.         }
    26.     }
    27.  
     
    Last edited: Jan 14, 2019
    ButtCleaves, fdz_, ElasticSea and 8 others like this.
  11. Deleted User

    Deleted User

    Guest

    Thanks, nice snippet!

    Here's my version between two points
    Code (CSharp):
    1.     public static void DrawWireCapsule(Vector3 _pos, Vector3 _pos2, float _radius, float _height, Color _color = default)
    2.     {
    3.         if (_color != default) Handles.color = _color;
    4.  
    5.         var forward = _pos2 - _pos;
    6.         var _rot = Quaternion.LookRotation(forward);
    7.         var pointOffset = _radius/2f;
    8.         var length = forward.magnitude;
    9.         var center2 = new Vector3(0f,0,length);
    10.        
    11.         Matrix4x4 angleMatrix = Matrix4x4.TRS(_pos, _rot, Handles.matrix.lossyScale);
    12.        
    13.         using (new Handles.DrawingScope(angleMatrix))
    14.         {
    15.             Handles.DrawWireDisc(Vector3.zero, Vector3.forward, _radius);
    16.             Handles.DrawWireArc(Vector3.zero, Vector3.up, Vector3.left * pointOffset, -180f, _radius);
    17.             Handles.DrawWireArc(Vector3.zero, Vector3.left, Vector3.down * pointOffset, -180f, _radius);
    18.             Handles.DrawWireDisc(center2, Vector3.forward, _radius);
    19.             Handles.DrawWireArc(center2, Vector3.up, Vector3.right * pointOffset, -180f, _radius);
    20.             Handles.DrawWireArc(center2, Vector3.left, Vector3.up * pointOffset, -180f, _radius);
    21.            
    22.             DrawLine(_radius,0f,length);
    23.             DrawLine(-_radius,0f,length);
    24.             DrawLine(0f,_radius,length);
    25.             DrawLine(0f,-_radius,length);
    26.         }
    27.     }
    28.  
    29.     private static void DrawLine(float arg1,float arg2,float forward)
    30.     {
    31.         Handles.DrawLine(new Vector3(arg1, arg2, 0f), new Vector3(arg1, arg2, forward));
    32.     }
     
  12. Wasserwecken

    Wasserwecken

    Joined:
    Oct 9, 2017
    Posts:
    6
    Thanks for the base, heres my version for drawing the capsule with two centers and radius, like CapsuleCast() / CheckCapsule() /

    Code (CSharp):
    1.     /// <summary>
    2.     ///
    3.     /// </summary>
    4.     private static void DrawWireCapsule(Matrix4x4 space, Vector3 upper, Vector3 lower, float radius)
    5.     {
    6.         using (new Handles.DrawingScope(space))
    7.         {
    8.             var offsetCenter = Vector3.Distance(upper, lower);
    9.             var offsetX = new Vector3(radius, 0f, 0f);
    10.             var offsetZ = new Vector3(0f, 0f, radius);
    11.  
    12.             //draw frontways
    13.             Handles.DrawWireArc(upper, Vector3.back, Vector3.left, 180, radius);
    14.             Handles.DrawLine(lower + offsetX, upper + offsetX);
    15.             Handles.DrawLine(lower - offsetX, upper - offsetX);
    16.             Handles.DrawWireArc(lower, Vector3.back, Vector3.left, -180, radius);
    17.             //draw sideways
    18.             Handles.DrawWireArc(upper, Vector3.left, Vector3.back, -180, radius);
    19.             Handles.DrawLine(lower + offsetZ, upper + offsetZ);
    20.             Handles.DrawLine(lower - offsetZ, upper - offsetZ);
    21.             Handles.DrawWireArc(lower, Vector3.left, Vector3.back, 180, radius);
    22.             //draw center
    23.             Handles.DrawWireDisc(upper, Vector3.up, radius);
    24.             Handles.DrawWireDisc(lower, Vector3.up, radius);
    25.         }
    26.     }
     
    ajtwoddl0408 and dshimmyo like this.
  13. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,307
    Maybe I missed something, but your version could not be rotated by using
    Gizmos.matrix = transform.localToWorldMatrix;

    so I made my custom version working exactly like other wire gizmos (except zTest)
    Code (CSharp):
    1.  
    2.     public static void DrawWireCapsule(Vector3 p1, Vector3 p2, float radius)
    3.     {
    4.         #if UNITY_EDITOR
    5.         // Special case when both points are in the same position
    6.         if (p1 == p2)
    7.         {
    8.             // DrawWireSphere works only in gizmo methods
    9.             Gizmos.DrawWireSphere(p1, radius);
    10.             return;
    11.         }
    12.         using (new UnityEditor.Handles.DrawingScope(Gizmos.color, Gizmos.matrix))
    13.         {
    14.             Quaternion p1Rotation = Quaternion.LookRotation(p1 - p2);
    15.             Quaternion p2Rotation = Quaternion.LookRotation(p2 - p1);
    16.             // Check if capsule direction is collinear to Vector.up
    17.             float c = Vector3.Dot((p1 - p2).normalized, Vector3.up);
    18.             if (c == 1f || c == -1f)
    19.             {
    20.                 // Fix rotation
    21.                 p2Rotation = Quaternion.Euler(p2Rotation.eulerAngles.x, p2Rotation.eulerAngles.y + 180f, p2Rotation.eulerAngles.z);
    22.             }
    23.             // First side
    24.             UnityEditor.Handles.DrawWireArc(p1, p1Rotation * Vector3.left,  p1Rotation * Vector3.down, 180f, radius);
    25.             UnityEditor.Handles.DrawWireArc(p1, p1Rotation * Vector3.up,  p1Rotation * Vector3.left, 180f, radius);
    26.             UnityEditor.Handles.DrawWireDisc(p1, (p2 - p1).normalized, radius);
    27.             // Second side
    28.             UnityEditor.Handles.DrawWireArc(p2, p2Rotation * Vector3.left,  p2Rotation * Vector3.down, 180f, radius);
    29.             UnityEditor.Handles.DrawWireArc(p2, p2Rotation * Vector3.up,  p2Rotation * Vector3.left, 180f, radius);
    30.             UnityEditor.Handles.DrawWireDisc(p2, (p1 - p2).normalized, radius);
    31.             // Lines
    32.             UnityEditor.Handles.DrawLine(p1 + p1Rotation * Vector3.down * radius, p2 + p2Rotation * Vector3.down * radius);
    33.             UnityEditor.Handles.DrawLine(p1 + p1Rotation * Vector3.left * radius, p2 + p2Rotation * Vector3.right * radius);
    34.             UnityEditor.Handles.DrawLine(p1 + p1Rotation * Vector3.up * radius, p2 + p2Rotation * Vector3.up * radius);
    35.             UnityEditor.Handles.DrawLine(p1 + p1Rotation * Vector3.right * radius, p2 + p2Rotation * Vector3.left * radius);
    36.         }
    37.         #endif
    38.     }

    EDIT:
    If you want to draw capsule based on CapsuleCollider, then you can do this:
    Code (CSharp):
    1. // collider is CapsuleCollider
    2. Gizmos.matrix = collider.transform.localToWorldMatrix;
    3.  
    4. Vector3 offset = Vector3.zero;
    5. offset[collider.direction] = collider.height * 0.5f - collider.radius;
    6. DrawWireCapsule(collider.center + offset, collider.center - offset, collider.radius);
     
    Last edited: Sep 25, 2022
  14. tcz8

    tcz8

    Joined:
    Aug 20, 2015
    Posts:
    504
    Works great! Until...

    upload_2022-10-19_22-34-42.png

    Changing the rotation on the Y or Z axis very slightly corrects it.
     
  15. joshrs926

    joshrs926

    Joined:
    Jan 31, 2021
    Posts:
    115
    Well I decided to go hard mode before I realized there was Handles.DrawWireArc XD. One nice thing about Gizmos is that it darkens the lines if they are behind a mesh. Handles doesn't appear to do that out of the box. But I may just scrap it in my own project and use Handles.DrawWireArc since the code is simpler. Here it is anyway in case someone needs to use Gizmos over Handles.

    Code (CSharp):
    1. public static void DrawWireCapsule(Vector3 point1, Vector3 point2, float radius)
    2. {
    3.     const int circleVertexCount = 32;
    4.     const int semiVertCount = circleVertexCount / 2 + 1;
    5.     Span<Vector3> verts = stackalloc Vector3[circleVertexCount * 4 + 4];
    6.     Span<Vector3> upperZVerts = verts.Slice(0, semiVertCount);
    7.     Span<Vector3> lowerZVerts = verts.Slice(semiVertCount * 1, semiVertCount);
    8.     Span<Vector3> upperXVerts = verts.Slice(semiVertCount * 2, semiVertCount);
    9.     Span<Vector3> lowerXVerts = verts.Slice(semiVertCount * 3, semiVertCount);
    10.     Span<Vector3> upperYVerts = verts.Slice(semiVertCount * 4, circleVertexCount);
    11.     Span<Vector3> lowerYVerts = verts.Slice(semiVertCount * 4 + circleVertexCount, circleVertexCount);
    12.     Vector3 up = NormalizeSafe(point2 - point1);
    13.     Quaternion orientation = Quaternion.FromToRotation(Vector3.up, up);
    14.     AddVerts(upperZVerts, point2, radius, orientation);
    15.     AddVerts(lowerZVerts, point1, radius, orientation * Quaternion.LookRotation(Vector3.forward, Vector3.down));
    16.     AddVerts(upperXVerts, point2, radius, orientation * Quaternion.LookRotation(Vector3.right, Vector3.up));
    17.     AddVerts(lowerXVerts, point1, radius, orientation * Quaternion.LookRotation(Vector3.right, Vector3.down));
    18.     AddVerts(upperYVerts, point2, radius, orientation * Quaternion.LookRotation(Vector3.down, Vector3.forward));
    19.     AddVerts(lowerYVerts, point1, radius, orientation * Quaternion.LookRotation(Vector3.down, Vector3.forward));
    20.     Span<Vector3> lines = stackalloc Vector3[(circleVertexCount * 4 + 4) * 2];
    21.     Span<Vector3> upperZLines = lines.Slice(0, semiVertCount * 2);
    22.     Span<Vector3> lowerZLines = lines.Slice(semiVertCount * 2, semiVertCount * 2);
    23.     Span<Vector3> upperXLines = lines.Slice(semiVertCount * 4, semiVertCount * 2);
    24.     Span<Vector3> lowerXLines = lines.Slice(semiVertCount * 6, semiVertCount * 2);
    25.     Span<Vector3> upperYLines = lines.Slice(semiVertCount * 8, circleVertexCount * 2);
    26.     Span<Vector3> lowerYLines = lines.Slice(semiVertCount * 8 + circleVertexCount * 2, circleVertexCount * 2);
    27.     AddLines(upperZVerts, upperZLines, false);
    28.     upperZLines[^2] = upperZVerts[^1];
    29.     upperZLines[^1] = lowerZVerts[0];
    30.     lowerZLines[^2] = lowerZVerts[^1];
    31.     lowerZLines[^1] = upperZVerts[0];
    32.     upperXLines[^2] = upperXVerts[^1];
    33.     upperXLines[^1] = lowerXVerts[0];
    34.     lowerXLines[^2] = lowerXVerts[^1];
    35.     lowerXLines[^1] = upperXVerts[0];
    36.     AddLines(lowerZVerts, lowerZLines, false);
    37.     AddLines(upperXVerts, upperXLines, false);
    38.     AddLines(lowerXVerts, lowerXLines, false);
    39.     AddLines(upperYVerts, upperYLines, true);
    40.     AddLines(lowerYVerts, lowerYLines, true);
    41.     Gizmos.DrawLineList(lines);
    42.     return;
    43.    
    44.    
    45.     static void AddVerts(Span<Vector3> verts, Vector3 center, float radius, Quaternion orientation)
    46.     {
    47.         for (int i = 0; i < verts.Length; i++)
    48.         {
    49.             float angle = Mathf.PI * 2 * ((float)i / circleVertexCount);
    50.             Vector3 point = new Vector3(Mathf.Cos(angle), Mathf.Sin(angle), 0);
    51.             point *= radius;
    52.             point = orientation * point;
    53.             point += center;
    54.             verts[i] = point;
    55.         }
    56.     }
    57.  
    58.     static void AddLines(Span<Vector3> verts, Span<Vector3> lines, bool loop)
    59.     {
    60.         for (int i = 0; i < verts.Length - 1; i++)
    61.         {
    62.             lines[i * 2 + 0] = verts[i];
    63.             lines[i * 2 + 1] = verts[i + 1];
    64.         }
    65.         if (loop)
    66.         {
    67.             lines[^2] = verts[^1];
    68.             lines[^1] = verts[0];
    69.         }
    70.     }
    71.    
    72.     static Vector3 NormalizeSafe(Vector3 value, Vector3 defaultValue = default)
    73.     {
    74.         if (value.Equals(default)) return defaultValue;
    75.         return value.normalized;
    76.     }
    77. }
    78.  
     
  16. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,433
    Kudos for stackalloc, but if you're going to calculate all those arc coordinates, you should cache them so you don't have to calculate them on every frame going forward.

    I also prefer Gizmo over Handle over Debug.Draw. They all have differences like the ztest you discovered, or the ability to queue and draw things at other parts of the editor loop, as they all serve slightly different use cases. I wish that beyond those differences, they all had the full complement of line, ray, circle, arc, disc, sphere, and so on. Each of the three drawing APIs has a completely different subset. Since they're all static classes, you can't even write extensions cleanly.
     
  17. joshrs926

    joshrs926

    Joined:
    Jan 31, 2021
    Posts:
    115
    I was actually just running into the issue where I really wanted to draw lines from FixedUpdate. Any advice on the best way to do so? I've been caching the shapes I want to draw in FixedUpdate and then drawing them inside OnDrawGizmo but I'd love a better way. Does Debug.DrawLine perhaps work? If so is there any nice way to draw an arc or a series of line segments?
     
    Last edited: Apr 17, 2024 at 5:11 AM
  18. joshrs926

    joshrs926

    Joined:
    Jan 31, 2021
    Posts:
    115
    And here's my Handles version for completeness sake. It's so much simpler with the use of DrawWireArc.
    Code (CSharp):
    1. public static void DrawWireCapsule(Vector3 point1, Vector3 point2, float radius)
    2. {
    3.     Vector3 upOffset = point2 - point1;
    4.     Vector3 up = upOffset.Equals(default) ? Vector3.up : upOffset.normalized;
    5.     Quaternion orientation = Quaternion.FromToRotation(Vector3.up, up);
    6.     Vector3 forward = orientation * Vector3.forward;
    7.     Vector3 right = orientation * Vector3.right;
    8.     // z axis
    9.     Handles.DrawWireArc(point2, forward, right, 180, radius);
    10.     Handles.DrawWireArc(point1, forward, right, -180, radius);
    11.     Handles.DrawLine(point1 + right * radius, point2 + right * radius);
    12.     Handles.DrawLine(point1 - right * radius, point2 - right * radius);
    13.     // x axis
    14.     Handles.DrawWireArc(point2, right, forward, -180, radius);
    15.     Handles.DrawWireArc(point1, right, forward, 180, radius);
    16.     Handles.DrawLine(point1 + forward * radius, point2 + forward * radius);
    17.     Handles.DrawLine(point1 - forward * radius, point2 - forward * radius);
    18.     // y axis
    19.     Handles.DrawWireDisc(point2, up, radius);
    20.     Handles.DrawWireDisc(point1, up, radius);
    21. }
     
  19. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,307
    Not the most performant solution, but an easy one - in my project there is component with various parameters to draw gizmos, plus static utility class with several functions like
    GizmoUtility.DrawSphere()
    or
    GizmoUtility.DrawCube()
    , each call creates new game object and adds component mentioned before with proper setup. It also destroys it with provided duration, so it works similarly to
    Debug.DrawLine()
    .