Object placement
In the previous tutorial we extended our map graph to output some nice cave-like maps.
In this tutorial we're going to populate our maps by adding a player character and some enemies.
Preparing assets
We need to create two more tiles, one for the player character and one for the enemies.
Let's start by importing two images for those tiles. Like before, you can download the ones I doodled up here or find and import some prettier ones. Just make sure they are perfectly square for the purposes of this tutorial.
Now use these textures to:
- Create a Player and Enemy tile.
- Add Player and Enemy tile types to the tileset and add the tiles to those tile types.
- Add colors named Player and Enemy to the Named Color Set.
If you need a reminder on how to do all this, check out this section of the first tutorial.
If you've linked the tileset to the named color set, you can skip step 3, it should be done automatically. Though you might want to assign a different color.
Next, we need another tilemap to layer our player and enemy tiles on top of our existing floor tiles.
- In the hierarchy window, select the Grid object that contains the existing tilemap.
- Add another tilemap, by selecting GameObject/2D Object/Tilemap.
Depending on your version of Unity, it might be under GameObject/2D Object/Tilemap/Rectangular instead.
- Give it a more descriptive name, something like Object Layer.
- Select the new tilemap.
- In the inspector, find the Order in Layer field and set it to 1.
This makes sure that everything in this tilemap/layer is drawn on top of the other tilemap containing our floor and wall tiles.
Create a new input parameter, called Objects, for the new tilemap on the Map Graph asset and assign the new tilemap to it on the Map Graph Runner component.
If you need a reminder on how to do this, check out this section of the first tutorial
Adding the player character
Ok, now that all the assets are in order. Open up the Map Graph again, so we can start randomly adding a player character to our map.
- Add a new Empty Texture node.
- Connect the existing Vector2Int constant node to the Size input port.
We want it to be of the same size as the other Empty Texture node.
- Leave the Default Color port unconnected, so that the default color will be transparent.
- Add a Randomly Fill Texture node.
- Connect its Texture input port to the new Empty Texture node's Empty Texture port.
- Add a Named Color node, set its value to Player and connect it to the Draw Color port on the new Randomly Fill Texture node.
- Add a int constant node, set its value to 1 and connect it to the Max. placements port on the new Randomly Fill Texture node.
This will make sure we only place one player character.
- Add a Texture To Tilemap Data node and connect its Texture input port to the new Randomly Fill Texture node's output port.
- Add a node for the Tiles input parameter and assign it to the Tileset input port.
- Add a Copy Tilemap Data node.
- Connect its Data Source port to the Tilemap port of the new Texture To Tilemap Data node.
- Add a node for the Objects input parameter and connect it to the Copy To Target port.
Run the graph and you'll see our player character being placed at a random position. However, there's a problem. If you run the graph a couple of times, you'll probably notice that the player gets placed on wall tiles as well as floor tiles, but we want to restrict the player character placement to the floor tiles. We can restrict placement to certain areas, by using a Mask. Let's do this now:
- Add a Replace Color node, by selecting Colors/Replace Color.
- Connect its Texture input port to the Texture output port of the Draw Connections (Shortest Path) node.
- Create a Named Color, set its value to Floor and connect it to the Find port on the Replace Color node.
- Leave the Replace port empty.
This will replace the Find color with the Replace color. In this case the Floor color will be replace with transparency (because the Replace port is unconnected.)
- Add a Texture To Mask node, by selecting Masks/Texture To Mask.
- Connect its Texture input port to the Texture output port of the Replace Color node.
This will generate a Mask, masking all pixels that are not transparent.
- Connect the Texture To Mask node's Mask output port to the new Randomly Fill Texture node's Mask input port.
Now run the graph a couple of times and you'll notice that the player character no longer gets spawned inside the walls!
Reference Nodes
This is probably a good time to explain how to create reference nodes.
During the last section you'll probably have noticed that it's becoming increasingly harder to connect nodes to each other without crossing lines or having lines that are very long, making the graph look very messy and hard to read. This will get worse as the graph gets bigger and more complex. To help you keep your graphs tidy and readable, you can make reference nodes.
Reference nodes represent the output part of the node that it's referring to, so you can connect it to nodes that would be hard to connect to the original node in a way that's not messy.
Other methods for keeping your graphs organized are by using groups or sub graphs.
Let's create a reference node of the Texture To Mask node:
- Right-click the Texture To Mask node.
- Select Create Reference Node.
The reference node will appear on top of the original one.
- Drag the the reference node to a new position, so that it's easier to connect it to the new Randomly Fill Texture node.
- Connect the reference node to the Mask port on the new Randomly Fill Texture node.
Reference nodes always have a * at the end of their name.
If you hover the mouse pointer over the reference nodes, its original node will be highlighted.
If you hover the mouse pointer over the original node, its reference nodes will be highlighted.
Adding enemies
Let's move on to placing enemies:
- Add another Randomly Fill Texture node.
- Connect its Texture input port to the Texture output port of the previously created Randomly Fill Texture node.
- Connect its Mask input port to the Mask output port of the previously created Randomly Fill Texture node.
The Randomly Fill Texture outputs a Mask that masks the pixels it fills, in addition to the pixels masked by the Mask that is connected to the Mask input port (if any). This will make sure that the enemies don't spawn in the wall and don't overwrite the player character placement.
- Add a float constant node, set its value to 100 (for now) and connect it to Fill Percentage on the new Randomly Fill Texture node.
- Add a Named Color node, set its value to Enemy and connect it to the Draw Color input port of the new Randomly Fill Texture node.
- Connect its Texture output port to the newest Texture To Tilemap Data node's Texture input port.
Run the graph and now your level is completely filled with enemies!
Ok, that's probably a little much, but lets use this opportunity to spot a possible issue. The enemies can spawn right next to the player, which depending on your game may or may not be what you want. Let's assume that this creates an unfair situation here and solve this problem by creating a little bit of a buffer zone around the the player:
- Add a Draw Circles node (under Drawing/Draw Circles).
- Connect its Center Points input port to the Placements output port of the Randomly Fill Texture used to place the player character.
The Placements port outputs all the points that its Randomly Fill Texture node has filled. In this case, a single point, representing the player's position.
- Connect its Texture input port to the Texture output port of the Replace Color node.
You'll probably want to use another reference node here.
- Add an int constant node and set its value to 5.
This will create a buffer zone of 5 pixels between the player character and the enemies.
- Add a constant color node and set it to any color you want.
You can also use a Named Color node if you want. Any color will do really as long as it's not transparent.
- Add another Texture To Mask node.
- Connect its Texture input port to the Draw Circles node's Texture output port.
- Connect its Mask output port to the Mask input port to the Randomly Fill Texture used to for enemy placement.
If you now run the graph again, you'll notice the buffer zone around the player where enemies won't be placed anymore.
Now that that's fixed, set the Fill Percentage of the Randomly Fill Texture node responsible for placing enemies, to something more practical, such as 1.
Conclusion
Great, you're now familiar with Map Graph's basics!
Obviously, this is just the tip of the iceberg of what you can achieve with Map Graph.
Next steps
You can take a look at the "Basic map types" section, to learn more about creating different types of maps.
You can also take a look at the examples in the sample project, most of which use (or a combination of) those map types, among other things, such as generating (3D) GameObjects from prefabs instead of tilemaps.
You can also check out some tutorials on more specific topics. You can find those in the left side menu under "More tutorials".
If you want a complete overview of all the available nodes, please check out the node index.
But most importantly, start experimenting! You can take what we've created here and simply start changing the numbers on some of the constants or add some more nodes, connect them in different ways to see what happens. Or you can create a new graph and try to create something completely different!
If you need help or want to request a tutorial about a certain subject, please don't hesitate to reach out using one of the channels mentioned in the support section.