In this series, we are going to attempt to create an object placer developer tool for Unity, with the aim of selling the tool on Unity’s asset store. Think of placing buildings in a Real-time strategy game or placing furniture in The Sims. We want a complete system where developers can simply import the asset, designate prefabs to be placed and immediately have real-time object placement.

To truly dominate the market for this tool, we will need a number of features that provide developers a complete solution to real-time object placement. These are some of the basics we should provide:

  • The ability to place objects on terrain
  • A Grid system with snapping
  • Auto grid detection for prefabs/meshes (Think a 100x200x200 model will auto fit into a 1x2x2 grid)
  • A shader for placement
  • 2D/3D compatibility
  • An option for a smoothness detection system (objects can only be placed on smooth ground/terrain)

I’m sure there are a number of features we will need to develop that I have not thought of yet, hopefully they come to me during development. For now, we’ll focus on these features/objectives.

This will be a vast learning experience, as a number of those features I have no idea how to implement. Namely, I have little experience with shaders.

A shader or shader program, is any program that runs on the GPU. This means they run incredibly fast and, more importantly for us, can produce good looking art/graphics when the user (me) is artistically impaired. I’m aiming to have a green/red shader for the object placement, wherein green denotes an object can be placed at that spot and red denotes they cannot. Since shaders use variables, we could even let the developer choose the two colors in Unity.

So, let’s get to work.

Following this video (“Wilmer Lin GA School’s” Edge noise tutorial), I added some tiling and offset and lerped some of the parameters, resulting in a decent looking shader:

We have a building on the left and a rock on the right. I like the shader, however am unsure whether it be a good fit for the asset. Perhaps something less glowy? Let me know in the comments.

Now we have an OK looking shader, it’s time to start building the placement system. I think my approach will be to get everything working first, then build it into a package later. For now, my code and structure will be messy and ‘bandaged’ together ,so we can quickly prototype and iterate.

Let’s start with simple placing the object upon the mouse cursor. I have created a simple scene with terrain that will serve as the base of the objects.

Crepe

After creating a simple UI that lets me select different objects, I hastily prototyped object placement. We raycast from the camera to the mouse’s world position and mask out anything bar the terrain. This ray returns a point on the terrain where the mouse is positioned, which we can use to place our object. We create a dummy object on the ray position and spawn that object when the mouse is clicked.

This works wonders, however there are a few problems we need to fix. Firstly, the object is not properly rotated. The object is always facing the same direction on all axes, shown to the extreme below:

Secondly, the object will stick into parts of the terrain. I’m not yet sure how to solve this issue, so we will leave it for now.

For the first problem, the ray’s hit grabs the surface normal, which is the direction perpendicular to the surface. We can set the object’s ‘up’ direction to the surface normal (the perpendicular direction), effectively rotating the object based on the terrain.

Viola! Simple and easy! (Not really it took me a while to figure it out)

So now we have made a pretty shader and some basic object placing with rotation based on the surface of the terrain. Next time, we’ll apply the shader by detecting whether the object is touching any others, which may include mesh x mesh intersection algorithms, although a simple collision system will probably be best.

If you don’t want to miss out on the next post, subscribe (in the top right of the page) and i’ll email you when I post!

Cheers,

Rob