Drawing 2D Polygon Meshes

I-Shaped Polygon

To draw a mesh programmatically, Unity requires an array of its vertices and triangles. Unity allows each vertex to be colored differently, so we will demonstrate this by feeding our mesh random colors. To display the mesh, we apply it to a GameObject’s MeshFilter component.

Below is a script that can be added to any empty object to draw the shape in the image above.

Specifying vertices is easy, as we can list them in an array of Vector2 s. The vertices2D array defines an I-shape. A small complication is that Unity wants an array of Vector3 s, so we need to convert them. System.Array.ConvertAll is handy for this.

To find the triangles in a 2D polygon’s list of points, we can use a triangulation algorithm. I copied the well-suited Triangulator.cs class specified in the Unity3D wiki and dropped it in the project. There’s nothing too fancy here; just give the object a list of points and it will give back the indices of each triangle.

The next step involves creating an array of colors, one for each vertex. Here they are initialized to random colors with Random.ColorHSV() using Linq syntax.

The last step is to create the mesh from the vertices, triangles, and color data and apply the mesh to a MeshFilter to draw it. Note that it is a good practice to call mesh.RecalculateBounds() and mesh.RecalculateNormals() to ensure the mesh behaves as it should.

If we attach the script to an empty GameObject , we should see an I-shaped polygon with lots of colors (see above). Check out commit 1 — draw simple polygon mesh to view the results in Unity.

Specifying Points with the Cursor

To capture input from the mouse and create shapes, we will create a controller that handles this. Its job is to create new shapes if no shapes are being drawn, update shapes if they require additional input, and set shapes loose if they are complete. To keep things simple, a script will collect new vertices whenever the left mouse button is released (click + drag is left as an exercise to the reader).

Let’s start with rectangles. To draw one, we can specify its two opposite corner vertices with mouse input.

Drawing Rectangles

To aid with simple operations on lists of vectors, we will create a Util.cs script defining a few handy operations.

Next, create an empty Rectangle prefab and attach a script called DrawRectangle.cs . The rectangle prefab is expected have a MeshFilter to draw its mesh (make sure to add a material to properly display the mesh), and LineRenderer to draw a rectangle outline.

DrawRectangle.cs defines two public functions:

AddVertex(Vector2 vertex) adds a new vertex to its internal list of two vertices and calls UpdateShape . If two vertices have been added, UpdateShape(Vector2 newVertex) replaces its second vertex with a new one. The four corners are extrapolated from the two vertices to update the mesh and line renderer. The shape is automatically redrawn by Unity every time this function is called.

The third function, RectangleMesh(Vector2 v0, Vector2 v1, Color fillColor) , serves to generate the rectangle mesh similar to the I-shaped polygon in the previous section.

One thing to note is that the rectangle’s transform set at the midpoint (centroid) of the mesh. The mesh’s vertices are relative to this center point. This will make some future calculations nicer (e.g., attaching a box collider). Also note that the script exposes a public FillColor , which fills the generated mesh with the given color.

Draw Algorithm

The basic algorithm for drawing a rectangle is as follows.

If the left mouse button is released and no rectangle is being updated, create a new rectangle with its two opposite corner vertices at the current mouse position. Indicate that the rectangle is being updated. If a rectangle is being updated, update the rectangle’s last corner vertex to be at the current mouse position. If the left mouse button is released and a rectangle is being updated, stop updating it.

Below we define new script called DrawController.cs . Its job is to collect mouse input and send mouse coordinates to the currently active rectangle GameObject . In essence, it specifies the draw algorithm above.

The property CurrentShapeToDraw holds the current rectangle, and IsDrawingShape is the condition that allows vertices to be added to the current shape.

Check out commit 2 — draw rectangle to view the results in Unity.

Adding 2D Physics

From what is implemented, adding physics to these rectangles is simple. First, attach a Rigidbody2D and a BoxCollider2D to the rectangle prefab. The biggest change is in the DrawRectangle.cs script:

Add a public SimulatingPhysics property that when set, simulates physics with the Rigidbody2D . Otherwise, the shape is set to static so that it stops moving and other shapes may still interact with it. Update the BoxCollider2D ’s bounding box after we update the mesh.

Lastly, the DrawController.cs script sets CurrentShapeToDraw.SimulatingPhysics when the current rectangle is complete and ready to be released.

And voila, we have physics! Just make sure to add a static platform to prevent rectangles from falling through the scene. Check out commit 3 — add rectangle physics to view the results in Unity.

Drawing Multiple Shapes

By extension, drawing circles, triangles, and arbitrary polygons should be very similar to drawing rectangles: supply some vertices via mouse input, create and update meshes/colliders, and simulate finished shapes. The DrawController ’s code would largely be unchanged: what we need are scripts defined for each shape on how their mesh and collider should be drawn.

But there’s a small catch: how do we keep track of all the different types of shapes? I opted to use inheritance as it is a tidy way of getting a handle on all shapes and update them by their public interface. First, we create an abstract class inheriting from Monobehaviour called DrawShape.cs , which looks like this:

As we want to manipulate the GameObject properties, we use an abstract class instead of an interface. There may be some code duplication between shapes, but this format enables shapes to be independent of each other in terms of implementation.

The DrawRectangle.cs script should then be updated to inherit from DrawShape , with some additional override keywords for the appropriate methods/properties.

As we will be drawing rectangles, circles, and triangles, we should update DrawController.cs with all prefabs that we will be using and expose them as public members for Unity, on for each shape type. To make shape selection easier, we will create an enum called DrawMode that maps a shape type to a prefab so that the requested shape type will be instantiated upon drawing a new shape. There will also be a handy dropdown in the Unity inspector to change between the shape we want to draw.

Drawing Circles

Unity does not support creating circle meshes or drawing circles out of the box, so we will need to implement our own circle-drawing algorithm. Fortunately, it does not require a large amount of code. The basic idea is to approximate a circle by drawing a regular polygon with enough line segments to be indistinguishable from a circle. To keep this illusion, the number of segments need to increase as the circle’s perimeter increases.

Below is the script for DrawCircle.cs , which inherits from DrawShape . Most of the code is identical to DrawRectangle.cs . Here we use a CircleCollider2D instead of a BoxCollider2D . The biggest difference is in the CircleMesh(Vector2 v0, Vector2 v1, Color fillColor) method.

We can create a manually tuned a linear function called numSegments which increases proportionally to the radius. For each segment, we evenly space Vector2 points around the circle using cosine and sine functions. Then the rest is just a repeat of what we have seen before in DrawRectangle .

Check out commit 4 — draw circle and add physics to view the results in Unity.

Drawing Triangles

With triangles, just change the collider type to PolygonCollider2D and we are halfway there. We can create a triangle mesh directly from the list of vertices specified by the cursor.

Can you guess how to draw arbitrary concave polygons? Hint: redefine the condition for when the shape is finished. Check out commit 5 — draw triangle and add physics to view the results in Unity.

Conclusion

Congratulations on making it this far! As a reward, here’s an easter egg: press the spacebar when your cursor is near some shapes.

I hope you enjoyed the tutorial and have a newfound appreciation for Unity’s simple physics interface. And if anyone wants to collaborate with me on creating a full featured game out of this, contact me.

—

Like my content? Check out my blog at https://hyperparticle.com.