Generating Random Dungeons (part 10)


Introduction

In part 9 of the Generating Random Dungeons article series we implemented the functionality to meet the requirements of step 11 and 12 of the random dungeon generation algorithm.

The requirement was to create random sized rooms and place them within the dungeon. To achieve this we added quite a lot of additional functionality. For instance, we added a new Room class which is very similar to the Map class in its structure and meaning.

In this article I will look at refactoring the Map and Room classes to reduce duplicate code. I hope to also handle placing doors in our rooms and, if there is time, to convert the Map structure from cells to tiles.

Refactoring

Below is a class diagram representation of our current Map and Room classes.

DomainModel1

We can see some commonality in the fields and properties of both the Map and Room classes. To me a Room is also a Map, albeit a special type of Map. I would not just want to inherit Room from Map in its current form as I’d be inheriting a bunch of unnecessary properties and methods used for Dungeon generation. And I guess that’s the key. We need another class that is based on a Map, but encapsulates the functionality for handling dungeon generation.

My first refactoring is therefore renaming the Map class to Dungeon and having the Dungeon class inherit from a new Map class that encapsulates the functionality for working with the Map data structure. The new class diagram is as follows.

DomainModel2 

Our new Map abstract class provides us with functionality to manage the Map data structure. Both Rooms and Dungeons are made up of cells in a rectangular shape and have bounds that need to be checked.

The Room class now only has functionality needed for room creation and placement while the Dungeon class only has functionality needed for creating a dungeon. I find the new structure a bit easier to work with, while the class name Dungeon adds a bit more meaning to what we’re actually trying to achieve.

I added a new CellLocations property which is similar to the old DeadEndCellLocations property on the Dungeon class.  Instead of returning only dead-end cell locations, this IEnumerable will return every location in our Map. The benefit is improved ease of use when having to enumerate each location within a map. Instead of having to write

            for (int x = 0; x < map.Width; x++)

                for (int y = 0; y < map.Height; y++)

we can now enumerate all the locations within our map using the following code.

            foreach (Point cellLocation in dungeon.CellLocations)

I implemented another change to the Map class so that the Map constructor properly initializes our 2-dimensional cell array. In the past the MarkCellsUnvisited and InitializeRoomCells methods performed this function, but I didn’t like the way you could instantiate the Dungeon and Room classes into an invalid state.

I renamed a few of the methods from “Mark” to “Flag” cell as visited. I felt it to be a bit more consistent with the language we’re using within our algorithm.

You will notice that I removed the room placement and scoring functionality from the Dungeon class. I felt that this was a function of Room creation and that the Dungeon class should not be making such decisions.

Just like we have a Dungeon Generator we should also have a Room Generator. The results of these classes should be Dungeons and Rooms respectively. In lieu of this I refactored the Generator class into a more explicit DungeonGenerator class and moved the room creation functionality into a new RoomGenerator class as seen below.

DomainModel3

Our DugeonGenerator is now initialized with the various parameters (these can also be changed via the properties). We offload the room generation functionality through the new RoomGenerator class which is passed to the DungeonGenerator as a parameter.

Our new RoomGenerator class is now responsible for creating and placing rooms. In the future we could possible swap out various RoomGenerators if we wanted to change the way we perform this part of the algorithm. The same holds for our DungeonGenerator class.

While moving the room placement code I found a potential bug. When we create a room we initialize the room with empty cells and cells with walls on the sides of the room boundaries. The problem comes when we place the room by copying the SideTypes of each cell to the map. By copying the SideTypes we aren’t ensuring that the cells adjacent to the room have side of SideType.Wall. The new PlaceRoom code is now as follows.

        public void PlaceRoom(Point location, Room room, Dungeon dungeon)

        {

            // Offset the room origin to the new location

            room.SetLocation(location);

 

            // Loop for each cell in the room

            foreach (Point roomLocation in room.CellLocations)

            {

                // Translate the room cell location to its location in the dungeon

                Point dungeonLocation = new Point(location.X + roomLocation.X, location.Y + roomLocation.Y);

                dungeon[dungeonLocation].NorthSide = room[roomLocation].NorthSide;

                dungeon[dungeonLocation].SouthSide = room[roomLocation].SouthSide;

                dungeon[dungeonLocation].WestSide = room[roomLocation].WestSide;

                dungeon[dungeonLocation].EastSide = room[roomLocation].EastSide;

 

                // Create room walls on map (either side of the wall)

                if ((roomLocation.X == 0) && (dungeon.HasAdjacentCellInDirection(dungeonLocation, DirectionType.West))) dungeon.CreateWall(dungeonLocation, DirectionType.West);

                if ((roomLocation.X == room.Width – 1) && (dungeon.HasAdjacentCellInDirection(dungeonLocation, DirectionType.East))) dungeon.CreateWall(dungeonLocation, DirectionType.East);

                if ((roomLocation.Y == 0) && (dungeon.HasAdjacentCellInDirection(dungeonLocation, DirectionType.North))) dungeon.CreateWall(dungeonLocation, DirectionType.North);

                if ((roomLocation.Y == room.Height – 1) && (dungeon.HasAdjacentCellInDirection(dungeonLocation, DirectionType.South))) dungeon.CreateWall(dungeonLocation, DirectionType.South);

            }

 

            dungeon.AddRoom(room);

        }

The updated PlaceRoom method now includes a step where it uses the CreateWall method to create a wall between the adjacent cells around the room borders.

The above refactoring process resulted in major structural changes to our code base. Fortunately we have a good set of unit tests and I was able to make these changes without any major headaches. Now we can look at adding doors to our dungeon.

Algorithm Step 13

For every place where the room is adjacent to a corridor or a room, add a door. (If you don’t want doors everywhere, add another parameter that determines when a door should be placed, and when an empty doorway [i.e. archway, etc.] should be placed).

For step 13 we need to determine if a room cell has a corridor cell adjacent to it. Obviously we should only evaluate the cells on the boundaries of our room i.e. the cells with walls. Our AdjacentCellInDirectionIsCorridor method on the Dungeon class will give us exactly what we need to meet this requirement.

Next we need some functionality to create a door. We already have two similar methods called CreateWall and CreateCorridor. We can create a similar method called CreateDoor to create our doorways. Let’s write a test to demonstrate this functionality.

        [Test]

        public void TestCreateDoorBetweenAdjacentCells()

        {

            Dungeon dungeon = new Dungeon(10, 10);

 

            // We now have map filled with rock.

            // Test creating doors in each direction

            dungeon.CreateDoor(new Point(0, 0), DirectionType.South);

 

            Assert.IsTrue(dungeon[0, 0].NorthSide == SideType.Wall);

            Assert.IsTrue(dungeon[0, 0].SouthSide == SideType.Door);

            Assert.IsTrue(dungeon[0, 0].WestSide == SideType.Wall);

            Assert.IsTrue(dungeon[0, 0].EastSide == SideType.Wall);

 

            Assert.IsTrue(dungeon[0, 1].NorthSide == SideType.Door);

            Assert.IsTrue(dungeon[0, 1].SouthSide == SideType.Wall);

            Assert.IsTrue(dungeon[0, 1].WestSide == SideType.Wall);

            Assert.IsTrue(dungeon[0, 1].EastSide == SideType.Wall);

 

        }

The above test makes use of a new CreateDoor method on the Dungeon class to create a Door on the South side at location (0, 0). We perform a test for this checking that the South side at (0,0) and the North side at (0, 1) are now of type SideType.Door. I excluded the other directions from this code listing (they are available in the source code download).

The code to make this test compile is as follows.

        public Point CreateDoor(Point location, DirectionType direction)

        {

            return CreateSide(location, direction, SideType.Door);

        }

The CreateDoor method is very simple. All I needed to add was the new “Door” SideType. I compile and run the test with no problems.

        [Test]

        public void TestCanPlaceDoors()

        {

            RoomGenerator roomGenerator = new RoomGenerator();

            Dungeon dungeon = new Dungeon(3, 3);

 

            // Create corridors in + shape

            Point location = new Point(1, 1);

            dungeon.CreateCorridor(location, DirectionType.North);

            dungeon.CreateCorridor(location, DirectionType.South);

            dungeon.CreateCorridor(location, DirectionType.West);

            dungeon.CreateCorridor(location, DirectionType.East);

 

            // Create and place room

            Room room = new Room(1, 1);

            room.InitializeRoomCells();

            roomGenerator.PlaceRoom(location, room, dungeon);

 

            roomGenerator.PlaceDoors(dungeon);

 

            Assert.AreEqual(SideType.Door, dungeon[1, 0].SouthSide);

            Assert.AreEqual(SideType.Door, dungeon[0, 1].EastSide);

            Assert.AreEqual(SideType.Door, dungeon[1, 1].NorthSide);

            Assert.AreEqual(SideType.Door, dungeon[1, 1].SouthSide);

            Assert.AreEqual(SideType.Door, dungeon[1, 1].WestSide);

            Assert.AreEqual(SideType.Door, dungeon[1, 1].EastSide);

            Assert.AreEqual(SideType.Door, dungeon[2, 1].WestSide);

            Assert.AreEqual(SideType.Door, dungeon[1, 2].NorthSide);

        }

In the above test we initialize a small Dungeon and place a 1×1 room in the middle. We create corridors leading from this room in all four directions. We then call a new PlaceDoors method on the RoomGenerator class and pass in our dungeon. The test then asserts that each of the room walls have a doorway in them.

The code to make the above test compile is as follows.

        public void PlaceDoors(Dungeon dungeon)

        {

            foreach (Room room in dungeon.Rooms)

            {

                foreach (Point cellLocation in room.CellLocations)

                {

                    // Translate the room cell location to its location in the dungeon

                    Point dungeonLocation = new Point(room.Bounds.X + cellLocation.X, room.Bounds.Y + cellLocation.Y);

 

                    // Check if we are on the west boundary of our room

                    // and if there is a corridor to the west

                    if ((cellLocation.X == 0) &&

                        (dungeon.AdjacentCellInDirectionIsCorridor(dungeonLocation, DirectionType.West)))

                        dungeon.CreateDoor(dungeonLocation, DirectionType.West);

 

                    // Check if we are on the east boundary of our room

                    // and if there is a corridor to the east

                    if ((cellLocation.X == room.Width – 1) &&

                        (dungeon.AdjacentCellInDirectionIsCorridor(dungeonLocation, DirectionType.East)))

                        dungeon.CreateDoor(dungeonLocation, DirectionType.East);

 

                    // Check if we are on the north boundary of our room

                    // and if there is a corridor to the north

                    if ((cellLocation.Y == 0) &&

                        (dungeon.AdjacentCellInDirectionIsCorridor(dungeonLocation, DirectionType.North)))

                        dungeon.CreateDoor(dungeonLocation, DirectionType.North);

 

                    // Check if we are on the south boundary of our room

                    // and if there is a corridor to the south

                    if ((cellLocation.Y == room.Height – 1) &&

                        (dungeon.AdjacentCellInDirectionIsCorridor(dungeonLocation, DirectionType.South)))

                        dungeon.CreateDoor(dungeonLocation, DirectionType.South);

                }

            }

        }

The CreateDoors method above is not very sophisticated. All it does is loop through each room in the dungeon and then for each room it checks every cell. It first checks if the cell lies on any of the boundaries of the room and then uses the AdjacentCellInDirectionIsCorridor method to check if there is a corridor cell in the given direction.

If the above conditions are true it creates a door in the direction provided. This implementation will create a doorway for every cell that is adjacent to a corridor even if it means that every cell on the boundary of a room will have a door. I recompile the test and it fails.

After some debugging I see that my test case produced a funny result. The test dungeon places a small 1×1 room in the middle of the ‘+’ shape. The new PlaceRoom method now creates a wall between the adjacent outside sides. This is that our definition of a corridor is determined by the number of walls a cell has. Our small 1×1 corridors are being counted as “rock” because they each have 4 walls.

I change the IsCorridor property on the Cell class to an explicit property rather than a calculated result. I updated the CreateCorridor method to explicitly set the IsCorridor property to true and the SparsifyMaze method to set the IsCorridor property to false when it removes a corridor.

I rerun the unit tests and they all pass.

Below is a screen shot of our dungeon generator using the new PlaceDoors functionality.

exampleoutput1

Our new door placement algorithm is definitely creating doors! Too bad the dungeon now looks terrible. The author doesn’t give much leading on how to place doors in his version of the algorithm. I think we can improve our current implementation by only placing one doorway per room wall as follows.

        public void PlaceDoors(Dungeon dungeon)

        {

            foreach (Room room in dungeon.Rooms)

            {

                bool hasNorthDoor = false;

                bool hasSouthDoor = false;

                bool hasWestDoor = false;

                bool hasEastDoor = false;

 

                foreach (Point cellLocation in room.CellLocations)

                {

                    // Translate the room cell location to its location in the dungeon

                    Point dungeonLocation = new Point(room.Bounds.X + cellLocation.X, room.Bounds.Y + cellLocation.Y);

 

                    // Check if we are on the west boundary of our room

                    // and if there is a corridor to the west

                    if ((cellLocation.X == 0) &&

                        (dungeon.AdjacentCellInDirectionIsCorridor(dungeonLocation, DirectionType.West)) &&

                        (!hasWestDoor))

                    {

                        dungeon.CreateDoor(dungeonLocation, DirectionType.West);

                        hasWestDoor = true;

                    }

 

                    // Check if we are on the east boundary of our room

                    // and if there is a corridor to the east

                    if ((cellLocation.X == room.Width – 1) &&

                        (dungeon.AdjacentCellInDirectionIsCorridor(dungeonLocation, DirectionType.East)) &&

                        (!hasEastDoor))

                    {

                        dungeon.CreateDoor(dungeonLocation, DirectionType.East);

                        hasEastDoor = true;

                    }

 

                    // Check if we are on the north boundary of our room

                    // and if there is a corridor to the north

                    if ((cellLocation.Y == 0) &&

                        (dungeon.AdjacentCellInDirectionIsCorridor(dungeonLocation, DirectionType.North)) &&

                        (!hasNorthDoor))

                    {

                        dungeon.CreateDoor(dungeonLocation, DirectionType.North);

                        hasNorthDoor = true;

                    }

 

 

                    // Check if we are on the south boundary of our room

                    // and if there is a corridor to the south

                    if ((cellLocation.Y == room.Height – 1) &&

                        (dungeon.AdjacentCellInDirectionIsCorridor(dungeonLocation, DirectionType.South)) &&

                        (!hasSouthDoor))

                    {

                        dungeon.CreateDoor(dungeonLocation, DirectionType.South);

                        hasSouthDoor = true;

                    }

 

                }

            }

        }

The new PlaceDoors method above keeps track of a Boolean flag for each side of the room. It will only place a door on a particular side if no doors have been placed previously. The new output can be seen in the following screen shot.

exampleoutput2

The new output is much better and looks like something to go adventuring in. This is of course still very basic and we could add additional functionality to create archways, secret doors, etc.

The next step is to convert our dungeon into a tile-based representation.

Converting to tiles

Our current cell-based dungeon representation is unsuitable for tile-based games such as roguelikes. The reason for this is that tiles normally only represent one feature of a dungeon i.e. a wall is only a wall, a door is only a door. In our current cell-based structure one cell represents four sides and can’t be displayed using a tile-based algorithm.

To convert to a tile-based structure we need to “expand” our cells into tiles, with each tile only holding one piece of information. This means that each side will be represented by a separate tile. For this process we are only interested in non-rock cells i.e. corridor or room cells.

We can represent our tiles as a 2-dimensional array of integer values. The specific values for rock, wall, floor and door can be any value we choose. For the moment I will use 0 for rock, 1 for corridor and 2 for doors. [Edit: I updated this to a Enum called TileType after an anonymous comment. I didn’t want to use the Enum originally as I wanted to decouple code using the dungeon generator from having to use my Enum. Still not sure if its a good idea, but when I’ve finished my roguelike I’ll write a post about it.]

The following test illustrates the functionality required to convert our cell-based structure to tiles.

        [Test]

        public void TestExpandWithCorridorTiles()

        {

            RoomGenerator roomGenerator = new RoomGenerator();

            Dungeon dungeon = new Dungeon(4, 4);

 

            dungeon.CreateCorridor(new Point(2, 1), DirectionType.North);

            dungeon.CreateCorridor(new Point(1, 2), DirectionType.South);

            dungeon.CreateCorridor(new Point(1, 1), DirectionType.West);

            dungeon.CreateCorridor(new Point(2, 2), DirectionType.East);

 

            // Create and place room

            Room room = new Room(2, 2);

            room.InitializeRoomCells();

            roomGenerator.PlaceRoom(new Point(1, 1), room, dungeon);

 

            int[,] dungeonTiles = DungeonGenerator.ExpandToTiles(dungeon);

 

            // Assert the dimensions are correct

            Assert.AreEqual(dungeon.Width * 2 + 1, dungeonTiles.GetUpperBound(0) + 1);

            Assert.AreEqual(dungeon.Height * 2 + 1, dungeonTiles.GetUpperBound(1) + 1);

 

            // Assert tiles were expanded correctly

            for(int x = 0; x < dungeon.Width * 2 + 1; x++)

            {

                for(int y = 0; y < dungeon.Height * 2 + 1; y++)

                {

                    if (((x == 5) & (y == 1)) ||

                    ((x == 1) & (y == 3)) ||

                    ((x == 3) & (y == 3)) ||

                    ((x == 4) & (y == 3)) ||

                    ((x == 5) & (y == 3)) ||

                    ((x == 3) & (y == 4)) ||

                    ((x == 4) & (y == 4)) ||

                    ((x == 5) & (y == 4)) ||

                    ((x == 3) & (y == 5)) ||

                    ((x == 4) & (y == 5)) ||

                    ((x == 5) & (y == 5)) ||

                    ((x == 7) & (y == 5)) ||

                    ((x == 3) & (y == 7))

                        )

                        Assert.IsTrue(dungeonTiles[x, y] == (int)TileType.Empty);

                    else

                        Assert.IsTrue(dungeonTiles[x, y] == (int)TileType.Rock);

                }

            }

        }

In the above test we create a small 4×4 dungeon with a 2×2 room in the middle. We then create a corridor in each direction from each corner of the room. We then invoke a new method on the DungeonGenerator class called ExpandToTiles. This method takes a Dungeon instance as input parameter and returns a 2-dimensional array of integer values representing the tiles.

Our test then asserts that the 2-dimensional array is twice the size + 1 of the dungeon in width and height. The reason for this is that there will always be a ring of rock around the dungeon and a tile for each of the sides are shared between the dungeon cells.

The test then loops through each value in the expanded array and tests to see if the value is either rock, corridor or a door. The above test is only testing walls and corridors. I have included a test for doors in the source code download.

The code to make the above test compile is as follows.

        public static int[, ] ExpandToTiles(Dungeon dungeon)

        {

            // Instantiate our tile array

            int[, ] tiles = new int[dungeon.Width * 2 + 1, dungeon.Height * 2 + 1];

 

            // Initialize the tile array to rock

            for (int x = 0; x < dungeon.Width * 2 + 1; x++)

                for (int y = 0; y < dungeon.Height * 2 + 1; y++)

                    tiles[x, y] = (int)TileType.Rock;

 

            // Fill tiles with corridor values for each room in dungeon

            foreach (Room room in dungeon.Rooms)

            {

                // Get the room min and max location in tile coordinates

                Point minPoint = new Point(room.Bounds.Location.X * 2 + 1, room.Bounds.Location.Y * 2 + 1);

                Point maxPoint = new Point(room.Bounds.Right * 2, room.Bounds.Bottom * 2 );

 

                // Fill the room in tile space with an empty value

                for (int i = minPoint.X; i < maxPoint.X; i++)

                    for (int j = minPoint.Y; j < maxPoint.Y; j++)

                        tiles[i, j] = (int)TileType.Empty;

            }

 

            // Loop for each corridor cell and expand it

            foreach (Point cellLocation in dungeon.CorridorCellLocations)

            {

                Point tileLocation = new Point(cellLocation.X*2 + 1, cellLocation.Y*2 + 1);

                tiles[tileLocation.X, tileLocation.Y] = (int)TileType.Empty;

 

                if (dungeon[cellLocation].NorthSide == SideType.Empty) tiles[tileLocation.X, tileLocation.Y – 1] = (int)TileType.Empty;

                if (dungeon[cellLocation].NorthSide == SideType.Door) tiles[tileLocation.X, tileLocation.Y – 1] = (int)TileType.Door;

 

                if (dungeon[cellLocation].SouthSide == SideType.Empty) tiles[tileLocation.X, tileLocation.Y + 1] = (int)TileType.Empty;

                if (dungeon[cellLocation].SouthSide == SideType.Door) tiles[tileLocation.X, tileLocation.Y + 1] = (int)TileType.Door;

 

                if (dungeon[cellLocation].WestSide == SideType.Empty) tiles[tileLocation.X – 1, tileLocation.Y] = (int)TileType.Empty;

                if (dungeon[cellLocation].WestSide == SideType.Door) tiles[tileLocation.X – 1, tileLocation.Y] = (int)TileType.Door;

 

                if (dungeon[cellLocation].EastSide == SideType.Empty) tiles[tileLocation.X + 1, tileLocation.Y] = (int)TileType.Empty;

                if (dungeon[cellLocation].EastSide == SideType.Door) tiles[tileLocation.X + 1, tileLocation.Y] = (int)TileType.Door;

            }

 

            return tiles;

        }

The above ExpandToTiles method instantiates a new 2-dimensional array to the size of our dungeon.Width * 2 + 1 and dungeon.Height * 2 +1. It then loops through each position in the array and sets the value to zero (or rock).

Next the method loops through every map in the dungeon and fills the corresponding tile are with empty tiles.

Next the method loops through every “corridor” cell location in the dungeon. At every location it checks each side to determine if its empty or a door and fills the adjacent tile with appropriate TileType. I compile and run the unit tests and they pass with no problems. Our new tile-based output now looks as follows.

exampleoutput3 

In the above screen shot I made use of the ‘+’ character to indicate doors and the ‘.’ character is the floor area.

Conclusion

In part 10 of the Generating Random Dungeons article series we started by refactoring our code by creating a new Map super class and renaming the old Map class to Dungeon. We also cleaned up some of the naming conventions and moved the Room generation and placement functionality into a new RoomGenerator class.

Next we implemented functionality to add doors to our rooms. This functionality was implemented using a very basic algorithm. A lot more can be added to make door placement a bit more intelligent (or maybe meaningful). I feel that the current implementation, however, is good enough as a prototype.

After that we converted the cell-based dungeon to a tile-based representation. The tile-based representation is important if you’re going to be using the random dungeons to create tile-based games such as roguelikes.

This part concludes the series on Generating Random Dungeons. I hope you all enjoyed it and learnt something through the process. I know I did.

Advertisements

14 thoughts on “Generating Random Dungeons (part 10)

  1. Use an enumerated type instead of saying “0 stands for rock, 1 for corridor and 2 for doors.” You can use this:

    enum square {
    rock,
    corridor,
    door
    };

  2. I have been searching over and over and over on the web and usenet for a decent level/dungeon generator and unless I wanted to dig into some really poorly documented C code, I was pretty much out of luck. I found the article you implemented here and was juuuust about to start implementing it myself when I managed to find your articles referenced on usenet. FINALLY some good code to look through and it couldn’t be better documented as to how you implemented it. Since I’m going to be doing my game in vb.net, this is excellent. Others should take your work as example and do the same.

  3. a couple notes on the expandtotiles…

    It occasionally creates hallways that aren’t connected to anything. I’ve seen this on two maps out of about 10 now. Not that big a deal if you don’t put stairs in hallways, but if you do there’s a chance of a game-ending bug there.

    The other thing is that in your test program you actually display the map with Y and X axes reversed. Didn’t notice it until I made a map that wasn’t symmetrical.

    Just a couple of notes… Otherwise, top notch work.

  4. Thanks for the feedback. I’m glad you found it useful.

    I haven’t noticed the expand to tiles bug. This could be detected by using some kind of solver for the map and checking if all floor cells were traversed.

    I will update the source code to reflect the X-Y axes change.

  5. Pingback: Temple of the Roguelike » Blog Archive » RogueDev update

  6. Great coding, I wonder if you happen to have it already preassembled in C# pages? I am looking to make a dungeon generator for my 2D graphical RPG for use in on-the-fly applications.

  7. THat is awesome, thanks for the source. I am looking to implement this system (assuming I have permission), in a for free RPG game created using C#. Its more of a hobby than anything. What I am wondering is whether you have an email to which I could ask you a few questions about manipulating a few pieces of code to add new features and also allow for it to be run “on-the-fly” so to speak, such that when the user enters a dungeon it gets created at that moment. I have looked through the code myself, but not being all that experienced in C#, I do not know where to begin. You can reach me at tj_pestill@hotmail.com if you are able to assist me.
    Thanks.

  8. Dirk,

    Another possibility with your “rooms are a type of map” idea is to use the Composite DP. This would be particularly useful if you were to extend your map-room relationship by one more level, eg. map-sector-room. Then both sector and room would implement Composite. The advantage of a sectors-based system would obviously be improved spatial hashing for building extremely large levels (I am planning on doing this), with the entire structure being an n-tree.

    I am guessing you may have realised this but opted out, as you were focusing on other matters in this article.

    -Nick

  9. Nick,

    I haven’t really worried about extremely large levels. But you’re right, you could use sectors to join maps or rooms.

    In later versions my rooms are maps. A “room” can therefore be hand crafted to represent any structure.

    -D

  10. Thanks for the expanding code. I was using JBDungeon in my project and didn’t even think I should just expand the map instead of trying to position thin walls.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s