In games it is quite common to need to determine if a point on the stage is free of obstacles in order to place a character or another game element. Think, for example, of an RTS: when building a building, you have to choose a free section of land, but how can your code know if a site already has a building or some other type of object?
In a 3D game, the most common solution is to project a ray from the camera's viewpoint, passing through the point where the mouse cursor is located on the screen plane, until it hits a collider. If the collider is the ground, that point is free, and if not, there is an obstacle.
Of course, if the object we want to place is larger than a point, projecting a simple ray falls short. Imagine we want to place a rectangular building, and the point where its center would go is free, but the corner area is not. Fortunately, for those cases, Unity allows us to project complex shapes beyond a mere point. For example, the SphereCast methods allow an invisible sphere to be moved along a line, returning the first collider it hits. Another method, BoxCast, would solve the problem of the rectangular building by projecting a rectangular base box along a line. We would only have to make that projection along a vertical line to the ground position we want to check.
In 2D, there are also projection methods, BoxCast and CircleCast, but they only work when the projection takes place in the XY plane (the screen plane). That is, they are equivalent to moving a box or a circle in a straight line along the screen to see if they touch a collider. Of course, that has its utility. Imagine you are making a top-down game and want to check if the character will be able to pass through an opening in a wall. In that case, you would only need to do a CircleCast of a circle, with a diameter like the width of our character's shoulders, projecting through the opening to see if the circle touches the wall's colliders.
![]() |
A CircleCast, projecting a circle along a vector. |
But what happens when you have to project on the Z-axis in a 2D game? For example, for a 2D case equivalent to the 3D example we mentioned earlier. In that case, neither BoxCast nor CircleCast would work because those methods define the projection vector using a Vector2 parameter, limited to the XY plane. In those cases, a different family of methods is used: the "Overlap" methods.
The Overlap methods place a geometric shape at a specific point in 2D space and, if the shape overlaps with any collider, they return it. Like projections, there are methods specialized in different geometric shapes: OverlapBox, OverlapCapsule, and OverlapCircle, among others.
Let's suppose a case like the following figure. We want to know if a shape the size of the red circle would touch any obstacle (in black) if placed at the point marked in the figure.
![]() |
Example of using OverlapCircle. |
In that case, we would use OverlapCircle to "draw" an invisible circle at that point (the circle seen in the figure is just a gizmo) and check if the method returns any collider. If not, it would mean that the chosen site is free of obstacles.
A method calling OverlapCircle could be as simple as the following:
![]() |
Call to OverlapCircle Call to OverlapCircle |
The method in the figure returns true if there is no collider within a radius (MinimumCleanRadius) of the candidateHidingPoint position. If there is any collider, the method returns false. For that, the IsCleanHidingPoint method simply calls OverlapCircle, passing the following parameters:
- candidateHidingPoint (line 224): A Vector2 with the position of the center of the circle to be drawn.
- MinimumCleanRadius (line 225): A float with the circle's radius.
- NotEmpyGroundLayers (line 226): A LayerMask with the layers of the colliders we want to detect. It serves to filter out colliders we don't want to detect. OverlapCircle will discard a collider that is not in one of the layers we passed in the LayerMask.
If the area is free of colliders, OverlapCircle will return null. If there are any, it will return the first collider it finds. If you are interested in getting a list of all the colliders that might be in the area, you could use the OverlapCircleAll variant, which returns a list of all of them.
We could end here, but I don't want to do so without warning you about a headache you will undoubtedly encounter in 2D. Fortunately, it can be easily solved if you are warned.
The problem can occur if you use tilemaps. These are very common for shaping 2D scenarios. The issue is that to form the colliders of a tilemap, it is normal to use a "Tilemap Collider 2D" component, and it is also quite common to add a "Composite Collider 2D" component to sum all the individual colliders of each tile into one to improve performance. The problem is that by default, the "Composite Collider 2D" component generates a hollow collider, only defined by its outline. I suppose it does this for performance reasons. This happens when the "Geometry Type" parameter has the value Outlines.
![]() |
Possible values of the Geometry Type parameter. |
Why is it a problem that the collider is hollow? Because in that case, the call to OverlapCircle will only detect the collider if the circle it draws intersects with the collider's edge. If, on the other hand, the circle fits neatly inside the collider without touching any of its edges, then OverlapCircle will not return any collider, and we would mistakenly assume that the area is clear. The solution is simple once it has been explained to you. You need to change the default value of "Geometry Type" to Polygons. This value makes the generated collider "solid," so OverlapCollider will detect it even if the drawn circle fits inside without touching its edges.
It seems silly because it is, but it was a silly thing that took me a couple of hours to solve until I managed to find the key. I hope this article helps you avoid the same issue.