A tutorial on drawing physics shapes with your cursor using Unity3D.
In this tutorial I will show you how to draw and manipulate basic 2D physics shapes with your cursor using Unity, such as rectangles, circles and triangles. No additional external dependencies are required (tested with 2017.2, but should also work with 5.x).
Back in 2008, I had loads of fun playing IncrediBots. For the uninitiated, the game is very similar to Phun (now Algodoo); the game involved drawing basic physics shapes, placing rotating/sliding joints on them, and controlling motor joints with keyboard input. The game featured several puzzles and a rating system for published contraptions.
Here’s a few examples of things that you could make:
So let’s do the same thing in Unity! Conveniently, Unity has excellent support for 2D physics, so implementing a basic prototype is fairly simple to do.
There are 4 parts to this post:
This tutorial will implement drawing rectangles, circles, and triangles.
Drawing 2D Polygon Meshes
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
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
vertices2D array defines an I-shape. A small complication is that Unity wants an array of
Vector3s, 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.RecalculateNormals() to ensure the mesh behaves as it should.
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.
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
- 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.
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.
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
- Add a public
SimulatingPhysicsproperty 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.
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
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.
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.
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
Check out commit 4 — draw circle and add physics to view the results in Unity.
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.
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.