Skip to main content
Version: 2.0.5

Example 1

In this tutorial, we will use this tileset by @pixel_poem. Be sure to check out their work if you like the tileset. We will not care about room decorations - we will use just basic walls, floor and door tiles.

image
Simple example
image
Real-life example

Note: All files from this example can be found at Examples/Grid2D/Example1.

List of used features
Below is a list of features that are used in this example.
Custom post-processingA custom post-processing task is used to spawn enemies after a level is generated

Simple example​

The goal is to create two basic rectangular room templates of different sizes and a room template for both horizontal and vertical corridors. We will use the smaller room template for our dead-end rooms and the bigger room template for other rooms.

Basic rooms templates​

There should be nothing hard about the design of the two rectangular room templates. We use the simple door mode configured to door length 1 and corner distance 2. We need corner distance 2 in order to easily connect corridors.

image
Bigger room
image
Smaller room

Corridors​

Corridors are very simple with this tileset. We use the specific positions doors mode to choose the two possible door positions. And because corridors are by default placed after non-corridor rooms, these room templates just work without the need of any scripting.

image
Horizontal corridor
image
Vertical corridor

We just need to make sure that we do not allow door positions of non-corridor rooms that are closer than 2 tiles from corners. Below you can see what would happen otherwise. It is possible to allow that, but we would have to implement some post-processing logic that would fix such cases.

Incorrect corridor connection

Level graph​

With only two room templates for non-corridor rooms, we must think about which level graphs are possible to lay out and which are not. For example, using only the bigger room template, the algorithm is not able to lay out cycles of lengths 3 and 5 because there simply is not any way to form these cycles with such room templates. But cycles of length 4 are possible, so that is what we do here.

Level graph

Results​

image
Example result
image
Example result

Real-life example​

To create something that is closer to a real-life example, we will:

  • add spawn room template that includes a player
  • add boss room that contains a ladder to the next level
  • add doors to corridors
  • add two more corridor room templates
  • add enemies
  • add more rooms to the level graph

Spawn room​

Our spawn room will look different from our basic rooms. We will also want the generator to spawn our player prefab inside the room. This can be simply achieved by placing our prefab inside the room template, next to the GameObject that holds our tilemaps.

Spawn room with player prefab

Note: A basic script for player movement is included in the example scene.

Boss room​

Our boss room will also have a special look. We also created a simple Exit prefab that looks like a ladder a generates a new level when interacted with. And similarly to placing our player prefab, we can also let the generator spawn a mighty ogre that will guard the exit.

Boss room template with exit prefab

Note: There is no enemy AI, so the ogre is really not that mighty.

Additional room template​

Even for ordinary rooms, we can have non-rectangular room templates.

image
Additional room tempalte

Doors​

We can easily add doors to our corridors. We created a simple door prefab that has a collider and also a trigger that lets the player open the door.

Corridor with doors

Longer corridors​

image
Longer horizontal corridor
image
Longer vertical corridor

Enemies​

We can easily add enemies to our levels. In this tutorial, we will add enemies directly to room templates and then implement a post-processing task that spawns each enemy with a configurable chance.

We will start by creating a GameObject called "Enemies" in all the room templates that will contain enemies a make all the enemies children of this GameObject.

Enemies added to the room template

Note: We must make sure to always name the root GameObject "Enemies" as we will use that name to work with the enemies.

If we now generate the dungeon, we will see that it contains all the enemies that we added to individual room templates.

Dungeon with monsters

If we are happy with the results, we can stop here. However, to showcase how we can add some post-processing logic to the generator, we will try to spawn each monster with some predefined probability so that different monsters spawn every time. The result can be found below.

Note: Since version 2.0.0-beta.0, the easiest way to implement a post-processing logic is with a MonoBehaviour rather than a ScriptableObject. So we will showcase that here.

We have to create a class that inherits from DungeonGeneratorPostProcessingComponentGrid2D. After a level is generated, the Run method is called and that is the place where we call our post-processing logic.

public class Example1PostProcessingComponent : DungeonGeneratorPostProcessingComponentGrid2D
{
[Range(0, 1)]
public float EnemySpawnChance = 0.5f;

public override void Run(DungeonGeneratorLevelGrid2D level)
{
HandleEnemies(level);
}

private void HandleEnemies(DungeonGeneratorLevelGrid2D level)
{
// Iterate through all the rooms
foreach (var roomInstance in level.RoomInstances)
{
// Get the transform that holds all the enemies
var enemiesHolder = roomInstance.RoomTemplateInstance.transform.Find("Enemies");

// Skip this room if there are no enemies
if (enemiesHolder == null)
{
continue;
}

// Iterate through all enemies (children of the enemiesHolder)
foreach (Transform enemyTransform in enemiesHolder)
{
var enemy = enemyTransform.gameObject;

// Roll a dice and check whether to spawn this enemy or not
// Use the provided Random instance so that the whole generator uses the same seed and the results can be reproduced
if (Random.NextDouble() < EnemySpawnChance)
{
enemy.SetActive(true);
}
else
{
enemy.SetActive(false);
}
}
}
}
}

With the implementation ready, we now have to attach this component to the game object where we have our generator component attached.

Attach the component to the game object with the generator

Level graph​

So the goal is to have more rooms than in the simple example and also a spawn room and a boss room. You can see one such level graph below.

Level graph

You can also see that with corridors of different lengths and more room templates to choose from, we can now have cycles of various sizes. The boss and spawn rooms have assigned only a single room template.

Results​

image
Example result
image
Example result
image
Example result with enemies
image
Example result with enemies