Tilesets are a mainstay of 2D games, and their basic principles can be applied to 3D games in a variety of ways. Perhaps you want a 2.5D environment with 3D modelled tilesets? Maybe you want a dynamic path or road system? Does creating a tile based puzzle game strike your fancy? Do you just really love Pipe Dream?
I originally needed a solution for the placeable paths in my upcoming sim game, but I’m sure this method will lend itself to many implementations. I couldn’t have put this together without the help of my good friend Ethan McQuarrie and Salt Games’s article on 2D neighbour aware tilesets. If you do make any weird and wonderful systems with this method, tweet them at me.
Making The Tiles
First we need some tiles. This implementation has 16 tile variations, but many of these are rotated repeats. This means we just need 6 meshes – a single standalone tile, an end tile, a corner tile, a bridge tile, a T-junction tile and a middle tile that will be surrounded on all sides. Make these in your 3D modelling program of choice, to match any grid sizes you have in your game.
The end tile, corner tile and T-junction tile will need to be exported four times – once in each direction. The bridge tile only needs to be exported twice, once running left to right and once running up and down. All together you should have 16 tile meshes that line up with each other. When exporting each tile, be sure to keep the pivot in the same corner and on the 0,0 point. Don’t forget to move it when you rotate your corners and ends! It doesn’t matter where your tiles lie in relation to the 0,0 point, so long as you remember their orientation for later and keep it consistent.
I exported my tiles in the same order that Salt Games has their array ordered. Tile 0 is the standalone mesh, tile 1 is the end tile facing up and so on. I’ve added a cheat sheet below. Once all your tiles are sorted and exported, we can start to build the dynamic tiling system.
How It All Works
If you haven’t already, read through Salt Game’s article on neighbour aware tiling. We’re going to be using a similar system – putting all of our meshes in an array, and then changing the mesh depending on what it detects around itself.
Once you have imported your meshes into Unreal, shrink your collision box slightly. This is just a precaution, as the tiles are right next to each other we don’t want to accidentally detect a tile we shouldn’t be joining with. As we’re going to be making an array, I’ve named my meshes accordingly to keep track of everything easier.
Next we’re going to create an Object Blueprint that will hold all of our dynamic tiling code. Add a static mesh component and select tile 0 as the mesh. This will be the default tile when nothing else is around it. Now we need to add collision boxes to detect the tiles next to it, so add four Box Collision components. Resize them so they aren’t touching your mesh component and only extend a small way out. We don’t want to detect anything two or three tiles over!
This is where remembering the orientation of your tiles comes in. Match the axis in Unreal’s viewport to the axis in your 3D modelling program, and then rename the collision boxes accordingly. This ensures that the left collision box is to the left of your tile, and so on.
It’s Code Time
Each position around the tile is going to be assigned a number, and then that number in going to be added to a sum total. This total will then be used as the index number in our array of tiles. Salt Games’s article has a wonderful interactive demonstration of this partway down the page. The top collision box will be worth 1, the left box worth 2, the bottom box 4 and the right box 8. Depending on how many collision boxes are ‘active’, the sum total will conveniently add up to the correct tile our mesh should be to connect with those around it.
Let’s start by making our array. Create 16 Static Mesh Actor Reference variables. They should be named from 0 to 15, and each one should point to the matching tile static mesh. Then create one last variable, which will be an integer called Sum Total. In your Event Graph, put all of the static mesh references into an array.
Spawn the Event Tick node as a starting point, and feed it into a Branch node. Off of the True path, create a Set Static Mesh node, and get a reference to the Blueprints’s own static mesh component. This will be the target mesh that we’re going to be changing.
Now, the condition for changing the static mesh will be if the sum total changes. From the array, create a Get node that will fetch the new mesh we need. The index we want to fetch will be whatever number the sum total is, so get a reference to that variable and feed it into the integer slot.
Then check if the Get node’s output is valid, and then make that the condition of the Branch node. The last thing we need to do is take the Get node’s output and make that the new mesh on the Set Static Mesh node. The final Blueprint should look something like this. We’ve made an array of off of our mesh’s and set this Blueprint’s static mesh component to match the sum total. At the moment the sum total equals 0, so the mesh is the single tile.
Now that we can change the mesh, we need to start adjusting the sum total depending on which of the four collision boxes are active. Create an Event Begin Play node, and join that up to a sequence. From the first sequence pin, create a Component Overlap Actors node, and make a reference to Collision Top as the component. Get a second reference to the Collision Top component and get it’s World Transform, and then add this to the Component Transform slot. Finally, get a reference to Self, add it to an array and then and add that to the Actors To Ignore slot, as we don’t want to be hitting ourselves.
Now, we only want to detect other tiles around us, so we need to make out tiles into a custom Object Type. In the main Unreal Engine Editor, navigate to Project Settings > Engine > Collision. Here you can create a custom Object Channel and assign that to all of your tiles. Remember to set your path to that object type, or nothing will be detected! In your Blueprint viewport, select the static mesh component and change the Object Type to your custom one, under Collision.
After you’ve done this, go back into your tile Blueprint and drag off of the Object Types input and choose Make Array. From the dropdown list, select the custom Object Channel you just made – mine is ThemeParkPath.
Everything is set up to detect an another tile above us, so now we just need to change the Sum Total. From the Component Overlap Actors node output, create a Branch with the condition set to the Return Value from the overlap. If this is True and something is above us, set the Sum Total to be itself plus one.
This chunk of code will work for each of the collision boxes, the amount added to the sum total is the only thing that needs to be changed. Repeat this off of the other three sequence nodes, adding 2 for the left box, 4 for the bottom box and 8 for the right box. You should end up with something like this.
We’re almost at the finish line! Now, our tile mesh will change to the correct shape depending on what is next to it, but we also need our tile to influence the tiles around it so they can match up.
Going back to the code for the top collision box, drag off of the Componant Overlap Actors Out Actors output and create a Get node. This will get all of the tiles above us, and we can then manipulate them. From the Get node’s output, create a Cast To node that points to whatever your Blueprint is called, in my case it’s BP_path_sand. Join this up to your Set Sum Total to continue the chain. Acting as the other tile, add to its Sum Total the opposite amount. So if the collision top adds 1 to the Sum total, the other tile’s Sum Total will need to have 4 added to it.
Repeat this for the other three collision boxes, remembering to add the opposite amount. Your Event Graph should look something like this.
Now your tiles will dynamically line up to each other! I have a much more complex placement system for all my objects, but this dynamic tiling code can easily be added to your own project. When you’re texturing your tiles, remember that they’ll need to be seamless in every direction. Here are some of my path textures, which vaguely resemble delicious biscuits.
And here is a quick video of the paths in action!