What needed fixing?

When using Unity’s built in Handles over Gizmos, you’ll notice some features missing.

One of these features is the ability to draw a solid cube/box. In fact, when it comes to drawing a cube they only give access to a DrawWireCube function.

private void SceneGUI(SceneView obj)
{
Handles.color = Color.yellow
Handles.DrawWireCube(Position, Size);
}

This is fine in most use cases, but I’m deciding to be a little selfish. I want solid colors.

Thankfully, Handles does have a Draw function we can use to achieve this.

Handles.DrawAAConvexPolygon(a, b, c, d);

This little code bit is what we will be using to display a set of vector points into quads which will make our box.

The Process

First, we want to define our 8 corners using a center and size-

    void DrawSolidCube(Vector3 center, Vector3 size)
    {
        Vector3 half = size * 0.5f;

        // 8 corners of the cube
        Vector3[] verts = new Vector3[8]
        {
            center + new Vector3(-half.x, -half.y, -half.z),
            center + new Vector3( half.x, -half.y, -half.z),
            center + new Vector3( half.x,  half.y, -half.z),
            center + new Vector3(-half.x,  half.y, -half.z),
            center + new Vector3(-half.x, -half.y,  half.z),
            center + new Vector3( half.x, -half.y,  half.z),
            center + new Vector3( half.x,  half.y,  half.z),
            center + new Vector3(-half.x,  half.y,  half.z),
        };
    }

Coordinates explanation:

  • half is half the size of the cube in each direction (so you build it centered on center).
  • Each vertex is offset along X, Y, Z from the center:
    • -half.x or half.x is left or right
    • -half.y or half.y is down or up
    • -half.z or half.z is back or forward

Each corner is basically one possible combination of ±X, ±Y, ±Z.

How this connects to the faces:

Each cube face is a rectangle (quad) made from 4 of these points.

For example:

  • Back face uses verts 0 → 1 → 2 → 3.
  • Front face uses verts 5 → 4 → 7 → 6.
  • Left face uses verts 4 → 0 → 3 → 7.
  • Right face uses verts 1 → 5 → 6 → 2.
  • Top face uses verts 3 → 2 → 6 → 7.
  • Bottom face uses verts 4 → 5 → 1 → 0.

In simpler terms:
You build a cube by picking four of the eight points for each flat side.

How do we fill in the faces?

We will make a new function that uses the pre-built Handles functions to build our faces using the above face points.

public static void DrawQuad(Vector3 a, Vector3 b, Vector3 c, Vector3 d)
        {
            Handles.DrawAAConvexPolygon(a, b, c, d);
        }

What’s happening:

  • DrawQuad is just a helper function that takes four points (corners of one face of the cube).
  • It uses Handles.DrawAAConvexPolygon, a Unity Editor function that draws a solid (filled) polygon between the points you give it.

So DrawQuad is simply saying:

“Hey Unity, please fill in a surface (polygon) between these four 3D points.”

Since Unity doesn’t give us a built in function to Draw our Cube, we can now use DrawQuad to make our cube.

    void DrawSolidCube(Vector3 center, Vector3 size)
    {
        Vector3 half = size * 0.5f;

        // 8 corners of the cube
        Vector3[] verts = new Vector3[8]
        {
            center + new Vector3(-half.x, -half.y, -half.z),
            center + new Vector3( half.x, -half.y, -half.z),
            center + new Vector3( half.x,  half.y, -half.z),
            center + new Vector3(-half.x,  half.y, -half.z),
            center + new Vector3(-half.x, -half.y,  half.z),
            center + new Vector3( half.x, -half.y,  half.z),
            center + new Vector3( half.x,  half.y,  half.z),
            center + new Vector3(-half.x,  half.y,  half.z),
        };

        // Define each face with 4 vertices
        DrawQuad(verts[0], verts[1], verts[2], verts[3]); // Back
        DrawQuad(verts[5], verts[4], verts[7], verts[6]); // Front
        DrawQuad(verts[4], verts[0], verts[3], verts[7]); // Left
        DrawQuad(verts[1], verts[5], verts[6], verts[2]); // Right
        DrawQuad(verts[3], verts[2], verts[6], verts[7]); // Top
        DrawQuad(verts[4], verts[5], verts[1], verts[0]); // Bottom
    }

Please keep in mind:

  • The order you send the points (a, b, c, d) matters because it affects the direction of the face’s “normal” (which way it’s facing).
  • If the points are not in the right order (clockwise or counterclockwise), sometimes the polygon won’t render correctly or could be invisible from certain angles.

Let’s test it out

All we have to do now is switch our Handles code from earlier with-

private void SceneGUI(SceneView obj)
{
Handles.color = Color.yellow;			DrawExtensions.DrawSolidCube(Position, Size);
}

As you can see above, we’re drawing our solid box now.

Honestly though, this thing not optimized yet. We can actually cache our verts to help relieve some of the burden. While we’re at it, I’m also going to give it a color property.

public  static class DrawExtensions
    {
        static Vector3 cachedCenter;
        static Vector3 cachedSize;
        static Vector3[] cachedVerts = new Vector3[8];
        public static void DrawSolidCube(Vector3 center, Vector3 size, Color color)
        {
            Handles.color = color;

            Vector3 half = size * 0.5f;

            if (center != cachedCenter || size != cachedSize)
            {
                cachedVerts[0] = center + new Vector3(-half.x, -half.y, -half.z);
                cachedVerts[1] = center + new Vector3(half.x, -half.y, -half.z);
                cachedVerts[2] = center + new Vector3(half.x, half.y, -half.z);
                cachedVerts[3] = center + new Vector3(-half.x, half.y, -half.z);
                cachedVerts[4] = center + new Vector3(-half.x, -half.y, half.z);
                cachedVerts[5] = center + new Vector3(half.x, -half.y, half.z);
                cachedVerts[6] = center + new Vector3(half.x, half.y, half.z);
                cachedVerts[7] = center + new Vector3(-half.x, half.y, half.z);

                cachedCenter = center;
                cachedSize = size;
            }

            // Define each face with 4 vertices
            DrawQuad(cachedVerts[0], cachedVerts[1], cachedVerts[2], cachedVerts[3]); // Back
            DrawQuad(cachedVerts[5], cachedVerts[4], cachedVerts[7], cachedVerts[6]); // Front
            DrawQuad(cachedVerts[4], cachedVerts[0], cachedVerts[3], cachedVerts[7]); // Left
            DrawQuad(cachedVerts[1], cachedVerts[5], cachedVerts[6], cachedVerts[2]); // Right
            DrawQuad(cachedVerts[3], cachedVerts[2], cachedVerts[6], cachedVerts[7]); // Top
            DrawQuad(cachedVerts[4], cachedVerts[5], cachedVerts[1], cachedVerts[0]); // Bottom
        }

        public static void DrawQuad(Vector3 a, Vector3 b, Vector3 c, Vector3 d)
        {
            Handles.DrawAAConvexPolygon(a, b, c, d);
        }
    }

What’s the difference:

  • We established “static Vector3[] cachedVerts = new Vector3[8];”
  • We now check for size and center as well before recalculating
  • We now set the color in the function instead of outside of it

Using the above, we stop trying to create a new set of verts every frame. Not only this, but we don’t change the already established verts unless the cachedSize or cashedCenter are different.

Now let’s just decide on a color-

private void SceneGUI(SceneView obj)
{
Color fadedYellow = Color.yellow;
fadedYellow.a = 0.3f;		DrawExtensions.DrawSolidCube(Position, Size, fadedYellow);
}

In Conclusion

A lot of the time what Unity doesn’t provide us out of the box, we can end up providing ourselves.