Generating dungeons, Part 3: A square room
If you've missed any earlier parts of this series, then you can find them here.
This part of the article talks about the rooms in a dungeon. There are many possibilities when it comes to constructing a room and I will show you the most basic principle, the square room. In the real world, most rooms are square or built by connecting squares to form other shapes like an L or a T. When you have a square room, the visibility calculation is very simple, as long as you don't have and blocking objects within that room. A cavelike room can also be defined with a lot of sqares, acting like lines, one for each row (or column).
Bounds

The rooms are made up with a lot of small tiles. I call the collection of all these tiles the bounds of the room. The bounds are the tiles that in no way can be overwritten by something else (in most cases, i'll come to this later on when I discuss the different room generators). In a square room, the bounds are set as a rectangle enclosing all passable tiles.
The rectangle of the bounds has a width and a height that is the same as the room and is offset at 0,0 from the rooms position. The bounds only keep track of it's offset and not the position on the map itself. Why does it only keep track of the offset? Because if you want to change the position of the room, the bounds flow with it without having to be interfered with.
And for other shapes, such as an L- or T-shape room, the bounds would be made with two rectangles right next to each other.
Perimiter

As I said before, there's a perimiter around the bounds of the room. This perimiter is the enclosure of the room, in most cases it a wall.
When placing a new room, it's bounds may not collide with this perimiter unless you're merging two rooms together. But, when we are connecting rooms, the perimiter may well be intruded upon. Doors and Corridors should penetrate the perimiter, otherwise there would be no way to get from one room to another. (I don't think your game will be fun/sell very well if your dungeons only have one small accessable room. Feel free to prove me wrong! :)
The perimiter can also be defined as a rectangle, offset at -1,-1 and has a width and height which both are 2 tiles bigger than the bounds of the square room.
Edges

Around the bound of the room, inside the perimiter, we find the edges. The edges are tiles that can are in direct contact with the room, either horizontally or vertically. From these edges, we can compute where to place our corridors and doors. The edges, as with the perimiter and bounds, only contain the offset from the rooms starting point. If we make a lot of calculations, this will speed up the generation.
Putting it together
What all entities on the map share is that they have perimiters, bounds and egdes. They also have a position where they are placed. The MapEntity class defines all of those properties and Room, Door and Corridor inherits from this class.
public class MapEntity
{
public Position Position { get; set; }
public List<PathItem> Edges { get; internal set; }
public List<rectangle> Bounds { get; set; }
public List<rectangle> Perimiter { get; set; }
public virtual List<PathItem> GetEdges(int padding)
{
return Edges;
}
}
public class Room : MapEntity
{
public int Width { get; set; }
public int Height { get; set; }
public RoomSettings Settings { get; set; }
private List<PathItem> edges = new List<PathItem>();
public override List<PathItem> GetEdges(int padding)
{
if (edgeCheck != Position.ToString())
{
edgeCheck = Position.ToString();
edges.Clear();
foreach (var edge in Edges)
edges.Add(new PathItem() { Direction = edge.Direction, Position = edge.Position + Position, NearestEdge = edge.NearestEdge });
}
if (padding == 0)
return edges;
else
return (from x in edges where x.NearestEdge >= padding select x).ToList();
}
}