Hello World and my dungeon generation
- Filip Walczak

- Mar 27, 2020
- 4 min read
Woah, my first post ever. First of all - "Hello World" and a warm welcome to anyone who got there, you're awesome! I want to use this blog as my "programming diary", so I can go back and see what I did in the past or just write about various things. If it turns out that someone's reading this and would like to provide some feedback - go ahead. I would love to hear about some better solution, other approach, or even just talk about what I did. The first thing I'll show here is my dungeon generation. Although I've been doing this game for a while now, I won't write about stuff that I did before, because im sure I'll redo it at some point and then it will be a perfect opportunity to share with the world.
At the moment, the algorithm works like that:
Instantiate room at position (0,0)
Randomize direction. Repeat if new direction collides with the previously created room.
Add an offset to the previous position based on the direction (Direction.Up = y + offset, Direction.Left = x - offset)
Set the new position and repeat the steps
public static IEnumerator GenerateLevel(int roomAmount, float distance) {
RoomPosition currentPosition;
RoomPosition previousPosition = RoomPosition.Zero();
RoomPosition nextPosition = RoomPosition.Zero();
for (int i = 0; i < roomAmount - 1; i++) {
currentPosition = nextPosition;
if (i < roomAmount - 2)
nextPosition = GetNextRoomPosition(currentPosition, distance);
var room = RoomGenerator.CreateRoom(previousPosition, currentPosition, nextPosition);
room.transform.SetParent(levelParent);
roomPositions.Add(currentPosition.vector2);
if (i < roomAmount - 2)
RoomPosition.positions.Add(nextPosition);
previousPosition = currentPosition;
yield return new WaitForSeconds(0.3f);
}
foreach (var position in RoomPosition.positions) {
var corridor = CreateCorridor(position);
corridor.transform.SetParent(levelParent);
yield return new WaitForSeconds(0.3f);
}
yield return null;
GenerationCompleted?.Invoke();
public static GameObject CreateRoom(RoomPosition previousPosition, RoomPosition currentPosition, RoomPosition nextPosition)
// If both previous and current positions are equal it means that this is the first room to create
if (previousPosition == currentPosition) {
switch (nextPosition.direction) {
case Direction.Top: return Object.Instantiate(Prefabs.Top.RandomObject(), currentPosition.vector2, Quaternion.identity);
case Direction.Right: return Object.Instantiate(Prefabs.Right.RandomObject(), currentPosition.vector2, Quaternion.identity);
case Direction.Down: return Object.Instantiate(Prefabs.Down.RandomObject(), currentPosition.vector2, Quaternion.identity);
case Direction.Left: return Object.Instantiate(Prefabs.Left.RandomObject(), currentPosition.vector2, Quaternion.identity);
default:
throw new ArgumentOutOfRangeException();
}
}
// If both next and current positions are equal it means that this is the last room to create
if (nextPosition == currentPosition) {
switch (currentPosition.direction) {
case Direction.Top: return Object.Instantiate(Prefabs.Down.RandomObject(), currentPosition.vector2, Quaternion.identity);
case Direction.Right: return Object.Instantiate(Prefabs.Left.RandomObject(), currentPosition.vector2, Quaternion.identity);
case Direction.Down: return Object.Instantiate(Prefabs.Top.RandomObject(), currentPosition.vector2, Quaternion.identity);
case Direction.Left: return Object.Instantiate(Prefabs.Right.RandomObject(), currentPosition.vector2, Quaternion.identity);
default:
throw new ArgumentOutOfRangeException();
}
}
switch (currentPosition.direction) {
case Direction.Top:
switch (nextPosition.direction) {
case Direction.Top: return Object.Instantiate(Prefabs.DownTop.RandomObject(), currentPosition.vector2, Quaternion.identity);
case Direction.Right:return Object.Instantiate(Prefabs.DownRight.RandomObject(), currentPosition.vector2, Quaternion.identity);
case Direction.Left:return Object.Instantiate(Prefabs.DownLeft.RandomObject(), currentPosition.vector2, Quaternion.identity);
default:
throw new ArgumentOutOfRangeException();
}
case Direction.Right:
switch (nextPosition.direction) {
case Direction.Top:return Object.Instantiate(Prefabs.LeftTop.RandomObject(), currentPosition.vector2, Quaternion.identity);
case Direction.Right:return Object.Instantiate(Prefabs.LeftRight.RandomObject(), currentPosition.vector2, Quaternion.identity);
case Direction.Down:return Object.Instantiate(Prefabs.DownLeft.RandomObject(), currentPosition.vector2, Quaternion.identity);
default:
throw new ArgumentOutOfRangeException();
}
case Direction.Down:
switch (nextPosition.direction) {
case Direction.Right:return Object.Instantiate(Prefabs.TopRight.RandomObject(), currentPosition.vector2, Quaternion.identity);
case Direction.Down:return Object.Instantiate(Prefabs.DownTop.RandomObject(), currentPosition.vector2, Quaternion.identity);
case Direction.Left:return Object.Instantiate(Prefabs.LeftTop.RandomObject(), currentPosition.vector2, Quaternion.identity);
default:
throw new ArgumentOutOfRangeException();
}
case Direction.Left:
switch (nextPosition.direction) {
case Direction.Top:return Object.Instantiate(Prefabs.TopRight.RandomObject(), currentPosition.vector2, Quaternion.identity);
case Direction.Down:return Object.Instantiate(Prefabs.DownRight.RandomObject(), currentPosition.vector2, Quaternion.identity);
case Direction.Left:return Object.Instantiate(Prefabs.LeftRight.RandomObject(), currentPosition.vector2, Quaternion.identity);
default:
throw new ArgumentOutOfRangeException();
}
default:
throw new ArgumentOutOfRangeException();
}So many switches because I wanted the rooms to be open only on the sides where the corridors are and made 10 different prefabs, because before there was only one prefab with all 4 walls open. (Yes, I know its a spaghetti, I'll take care of it one day)
private static RoomPosition GetNextRoomPosition(RoomPosition currentPosition, float distance = 6) {
RoomPosition positionToReturn;
do {
var randomDirection = (Direction)Random.Range(1, 4);
switch (randomDirection) {
case Direction.Top:
positionToReturn = new RoomPosition(new Vector2(currentPosition.vector2.x, currentPosition.vector2.y + roomOffset),
Direction.Top);
break;
case Direction.Right:
positionToReturn = new RoomPosition(new Vector2(currentPosition.vector2.x + roomOffset, currentPosition.vector2.y),
Direction.Right);
break;
case Direction.Down:
positionToReturn = new RoomPosition(new Vector2(currentPosition.vector2.x, currentPosition.vector2.y - roomOffset),
Direction.Down);
break;
case Direction.Left:
positionToReturn = new RoomPosition(new Vector2(currentPosition.vector2.x - roomOffset, currentPosition.vector2.y),
Direction.Left);
break;
default:
throw new ArgumentOutOfRangeException();
}
} while (roomPositions.Contains(positionToReturn.vector2));
return positionToReturn;
}With the corridors it's almost the same as with rooms, but instead of randomized directions it takes a list of rooms, starting from the second created (I exclude the first because it doesn't have a previous room to connect to) and instantiates a corridor connecting current room with the previous.
private static GameObject CreateCorridor(RoomPosition roomPosition) {
Vector2 corridorPosition;
float corridorOffset = roomOffset / 2;
switch (roomPosition.direction) {
case Direction.Top: corridorPosition = new Vector2(roomPosition.vector2.x, roomPosition.vector2.y - corridorOffset);
break;
case Direction.Right: corridorPosition = new Vector2(roomPosition.vector2.x - corridorOffset, roomPosition.vector2.y);
break;
case Direction.Down: corridorPosition = new Vector2(roomPosition.vector2.x, roomPosition.vector2.y + corridorOffset);
break;
case Direction.Left: corridorPosition = new Vector2(roomPosition.vector2.x + corridorOffset, roomPosition.vector2.y);
break;
default:
throw new ArgumentOutOfRangeException();
}
switch (roomPosition.direction) {
case Direction.Down:
case Direction.Top:
return Object.Instantiate(verticalCorridorPrefab, corridorPosition, Quaternion.identity);
case Direction.Left:
case Direction.Right:
return Object.Instantiate(horizontalCorridorPrefab, corridorPosition, Quaternion.identity);
}
return null;And lastly a RoomPosition struct and a Direction enum used in all methods
public struct RoomPosition {
public static List<RoomPosition> positions = new List<RoomPosition>();
public Vector2 vector2;
public Direction direction;
public RoomPosition(Vector2 _vector2, Direction _direction) {
vector2 = _vector2;
direction = _direction;
}
public RoomPosition(Vector2 _vector2) {
vector2 = _vector2;
direction = Direction.None;
}
public static RoomPosition Zero() {
return new RoomPosition(Vector2.zero, Direction.None);
}
public static bool operator== (RoomPosition a, RoomPosition b) {
return (a.vector2 == b.vector2) && (a.direction == b.direction);
}
public static bool operator !=(RoomPosition a, RoomPosition b) {
return !(a == b);
}
}public enum Direction {
None = 0,
Top = 1,
Right = 2,
Down = 3,
Left = 4
}Next thing I would like to do with this generator is to improve the algorithm so it can go in multiple directions. It will make the dungeon less linear and can open new possibilites, like locked rooms which need a key to open or rooms with optional events.
Hope you enjoyed this little post and till the next time ;)



Thank you, kar.chmaj!
Well done, you're awesom too!❤️