At 7/31/13 04:32 PM, Thegluestickman wrote:
Any tips or comments from the masters?
Also, I would to get more square rooms, not just a mess of tiles. Any suggestion on the 1000s of algorithms out there for generation? (Preferably with a diagram)
Here's the code for a BSP based dungeon generator I've made a while ago, it's written in C++, but the code itself should be pretty readable I guess
#define MIN_ROOM_AREA 25
#define MAX_ROOM_AREA 90
inline float MaxVal(float a, float b, float c, float d)
{
float m1 = a > b ? a : b;
float m2 = c > d ? c : d;
return m1 > m2 ? m1 : m2;
}
inline float MinVal(float a, float b, float c, float d)
{
float m1 = a < b ? a : b;
float m2 = c < d ? c : d;
return m1 < m2 ? m1 : m2;
}
struct BTreeNode
{
bool isLeaf;
BTreeNode* left;
BTreeNode* right;
Vector2 m_boxMin;
Vector2 m_boxMax;
BTreeNode(){left = NULL; right = NULL;}
~BTreeNode(){delete left; delete right;}
};
void BuildLeafList(BTreeNode* root, std::vector<BTreeNode*>& outLeafList)
{
if(root == NULL)
return;
if(root->isLeaf)
outLeafList.push_back(root);
BuildLeafList(root->left, outLeafList);
BuildLeafList(root->right, outLeafList);
}
void FillMatrixArea(int startX, int startY, int endX, int endY, int width, MazeStorageType* maze)
{
startX += 1;
startY += 1;
endX -= 1;
endY -= 1;
for (int i = startY; i < endY; ++i)
{
for(int j = startX; j < endX; ++j)
{
maze[j + i * width] = 0;
}
}
}
void GenerateMazeMatrix(int startX, int startY, int endX, int endY, int width, BTreeNode& crNode, MazeStorageType* maze)
{
crNode.m_boxMin.X = startX;
crNode.m_boxMin.Y = startY;
crNode.m_boxMax.X = endX;
crNode.m_boxMax.Y = endY;
int xInterval = endX - startX;
int yInterval = endY - startY;
int area = xInterval * yInterval;
crNode.isLeaf = false;
if(xInterval < 4 || yInterval < 4)
return;
if(area < MIN_ROOM_AREA)
return;
if(area < MAX_ROOM_AREA)
{
crNode.m_boxMax.X -= 1;
crNode.m_boxMax.Y -= 1;
crNode.m_boxMin.X += 1;
crNode.m_boxMin.Y += 1;
FillMatrixArea(startX, startY, endX, endY, width, maze);
crNode.isLeaf = true;
return;
}
crNode.left = new BTreeNode();
crNode.right = new BTreeNode();
float aspect = (float)xInterval / yInterval;
if(aspect > 1)
{
int splitPoint = (qrand() % (int)(xInterval * 0.9f)) + startX;
GenerateMazeMatrix(startX, startY, splitPoint, endY, width, *crNode.left, maze);
GenerateMazeMatrix(splitPoint, startY, endX, endY, width, *crNode.right, maze);
}
else
{
int splitPoint = (qrand() % (int)(yInterval * 0.9f)) + startY;
GenerateMazeMatrix(startX, startY, endX, splitPoint, width, *crNode.left, maze);
GenerateMazeMatrix(startX, splitPoint, endX, endY, width, *crNode.right, maze);
}
}
void CreateTunnel(BTreeNode* room1, BTreeNode* room2, MazeStorageType* maze, unsigned int mazeWidth)
{
bool overlapX = room1->m_boxMax.X >= room2->m_boxMin.X && room1->m_boxMin.X <= room2->m_boxMax.X;
bool overlapY = room1->m_boxMax.Y >= room2->m_boxMin.Y && room1->m_boxMin.Y <= room2->m_boxMax.Y;
if(overlapX)
{
unsigned int overlapEnd = room2->m_boxMax.X > room1->m_boxMax.X ? room1->m_boxMax.X : room2->m_boxMax.X;
unsigned int overlapStart = room2->m_boxMin.X > room1->m_boxMin.X ? room2->m_boxMin.X : room1->m_boxMin.X;
for(unsigned int i = room1->m_boxMax.Y; i < room2->m_boxMin.Y; ++i)
{
for(unsigned int j = overlapStart; j < overlapEnd; ++j)
{
maze[i * mazeWidth + j] = 0;
}
}
}
else if(overlapY)
{
unsigned int overlapEnd = room2->m_boxMax.Y > room1->m_boxMax.Y ? room1->m_boxMax.Y : room2->m_boxMax.Y;
unsigned int overlapStart = room2->m_boxMin.Y > room1->m_boxMin.Y ? room2->m_boxMin.Y : room1->m_boxMin.Y;
for(unsigned int i = overlapStart; i < overlapEnd; ++i)
{
for(unsigned int j = room1->m_boxMax.X; j < room2->m_boxMin.X; ++j)
{
maze[i * mazeWidth + j] = 0;
}
}
}
else
{
int lineStartY = room2->m_boxMax.Y > room1->m_boxMax.Y ? room1->m_boxMax.Y : room2->m_boxMax.Y;
int lineEndY = room2->m_boxMin.Y > room1->m_boxMin.Y ? room2->m_boxMin.Y : room1->m_boxMin.Y;
int lineStartX = room2->m_boxMax.X > room1->m_boxMax.X ? room1->m_boxMax.X : room2->m_boxMax.X;
int lineEndX = room2->m_boxMin.X > room1->m_boxMin.X ? room2->m_boxMin.X : room1->m_boxMin.X;
for(unsigned int i = lineStartX; i <= lineEndX; ++i)
{
unsigned int mazePosition = (unsigned int )(lineEndY * mazeWidth + i);
maze[mazePosition] = 0;
}
for(unsigned int i = lineStartY; i <= lineEndY; ++i)
{
unsigned int mazePosition = (unsigned int)(i * mazeWidth + lineEndX);
maze[mazePosition] = 0;
}
}
}
void CreateMazeMatrix(int sizeX, int sizeY)
{
BTreeNode root;
int area = sizeX * sizeY;
std::vector<BTreeNode*> leafList;
std::vector<MazeStorageType> mazeStorage;
mazeStorage.resize(area, 1);
GenerateMazeMatrix(0, 0, sizeX-1, sizeY-1, sizeX, root, &mazeStorage[0]);
BuildLeafList(&root, leafList);
size_t nrLeafIterations = leafList.size() - 1;
for(size_t leafIt = 0; leafIt < nrLeafIterations; ++leafIt)
{
CreateTunnel(leafList[leafIt], leafList[leafIt + 1], mazeStorage, sizeX);
}
}
It basically subdivides a linear matrix into rooms building a BSP tree along the way if you need it. I've biased the algorithm to try and equalize the aspect ratio of the rooms as much as possible, so it's still random, but at least it gives some not so boring square rooms. I've also used a minimum and maximum area for the rooms, but you can remove them if you want to and set it to generate a leaf when it reaches a certain or random level if you want to.
Pic of the generated dungeon attached