您的位置:首页 > 产品设计 > UI/UE

随机生成Roguelike游戏地图算法

2016-05-10 00:00 477 查看
摘要: 考虑做一个RogueLike类的游戏 第一步就是要能够随机生成地图。网络上面相关的算法很多 有心的话可以查到很多资料。
我这找了一个大概适合自己的算法研究了下

参考资料
http://www.roguebasin.com/index.php?title=Dungeon-Building_Algorithm
http://www.roguebasin.com/index.php?title=Grid_Based_Dungeon_Generator

www.roguebasin.com 有很多相关的roguelike资料 相当有价值

首先随机生成一套地图世界有几个概念。
数据结构
1.world
2.map
3.room
4.corridor

地图的类型也有很多种
1.maze
2.dungeon
3.island
4.cave
5.castle

生成房间的算法也分为很多类别
我先实现了 最简单的一个地图类型 类似于dungeon的地图类型

生成的数据大概是这样



算法逻辑
1.在world创建room
2.判断room是否碰撞 碰撞重新生成
3.房间生成门
4.查找最近的房间 使用astar 生成房间直接连通的街道。

一些c#代码

public void generatePCGBasic(byte[,] g) {
base.generatePCG(g);
// Init grid
this.initRooms();
// Initialize rooms
//this.initCorridors();
// Initialize corridors
}

public void initRooms()
{
this.rooms = new ArrayList();
//room_num = 1;
//  New room arraylist

//Log.info("room_num",this.room_num);
for (int n = 0; n < this.room_num; n++)
{
this.room_blocked = false;
//  Unblock
Room rm = new Room(pcgrid_width, pcgrid_height, this.room_base, this.room_radix, this.corridor_num);
//  Create new room
this.room_blocked = this.blockRoom(rm);

//  Check if room is blocked
if (this.room_blocked)
{
n--;
//  Remake room
this.redo--;
//  Stops if taking too long
if ((this.redo == 0))
{
this.room_num--;
this.redo = 1000;
//  Recursion limit
}

}
else {

//Log.info("room_blocked",this.room_blocked);
this.rooms.Add(rm);
//  Create room
for (int j = rm.room_y1; (j <= rm.room_y2); j++)
{
for (int i = rm.room_x1; (i <= rm.room_x2); i++)
{
pcgrid[i, j] = 1;
}

}

//  Create room walls
for (int i = rm.wall_x1; (i <= rm.wall_x2); i++)
{
if (pcgrid[i, rm.wall_y1] != 1)
{
pcgrid[i, rm.wall_y1] = 2;
}

if (pcgrid[i, rm.wall_y2] != 1)
{
pcgrid[i, rm.wall_y2] = 2;
}

}

for (int j = rm.wall_y1; (j <= rm.wall_y2); j++)
{
if (pcgrid[rm.wall_x1, j] != 1)
{
pcgrid[rm.wall_x1, j] = 2;
}

if (pcgrid[rm.wall_x2, j] != 1)
{
pcgrid[rm.wall_x2, j] = 2;
}

}

//  Place openings
for (int k = 0; (k < rm.opening_num); k++)
{
if ((pcgrid[rm.opening[k, 0], rm.opening[k, 1]] != 1))
{
pcgrid[rm.opening[k, 0], rm.opening[k, 1]] = 3;
}

}

/*
var sb = new StringBuilder();
sb.Append("\n");
for (int i = 0; i < pcgrid.GetLength(0); i++)
{
for (int j = 0; j < pcgrid.GetLength(1); j++)
{
sb.Append( pcgrid[i, j] );
}
sb.Append("\n");
}
Log.info(sb.ToString());

*/
}

}

}

public bool blockRoom(Room rm)
{
//  If outside of grid
if ((!bounded(rm.wall_x1, rm.wall_y1)
|| (!bounded(rm.wall_x2, rm.wall_y1)
|| (!bounded(rm.wall_x1, rm.wall_y2)
|| !bounded(rm.wall_x2, rm.wall_y2)))))
{
return true;
}

//  If blocked by another room
if ((this.room_type != 3))
{
for (int i = (rm.wall_x1 - 1); (i
< (rm.wall_x2 + 1)); i++)
{
//  Check upper and lower bound
if ((bounded(i, (rm.wall_y1 - 1))
&& !blocked(i, (rm.wall_y1 - 1), 0)))
{
return true;
}

if ((bounded(i, (rm.wall_y2 + 1))
&& !blocked(i, (rm.wall_y2 + 1), 0)))
{
return true;
}

}

for (int j = (rm.wall_y1 - 1); (j
< (rm.wall_y2 + 1)); j++)
{
//  Check left and right bound
if ((bounded((rm.wall_x1 - 1), j)
&& !blocked((rm.wall_x1 - 1), j, 0)))
{
return true;
}

if ((bounded((rm.wall_x2 + 1), j)
&& !blocked((rm.wall_x2 + 1), j, 0)))
{
return true;
}

}

}

return false;
}

public void initCorridors() {
if ((this.room_type != 3)) {
for (int i = 0; (i < this.rooms.Count); i++) {
//  Go through each room and connect its first opening to the first opening of the next room
Room rm1 = ((Room)(this.rooms[i]));
Room rm2;
if ((i
== (this.rooms.Count - 1))) {
rm2 = ((Room)(this.rooms[0]));
}
else {
rm2 = ((Room)(this.rooms[(i + 1)]));
}

//  If not last room
//  Connect rooms
//basicAStar(pcgrid, rm1.opening[0,0], rm1.opening[0,1], rm2.opening[0,0], rm2.opening[0,1], this.corridor_weight, this.turning_weight);
//  Random tunneling
for (int j = 1; (j < rm1.opening_num); j++) {
this.tunnelRandom(rm1.opening[j,0], rm1.opening[j,1], rm1.opening[j,2], 3);
}

}

}
else {
//  If complex
Room rm1 = ((Room)(this.rooms[0]));
for (int i = 1; (i < this.rooms.Count); i++) {
//  Go through each room and connect its first opening to the first opening of the first room
Room rm2 = ((Room)(this.rooms[i]));
//  Connect rooms
//basicAStar(pcgrid, rm1.opening[0,0], rm1.opening[0,1], rm2.opening[0,0], rm2.opening[0,1], this.corridor_weight, this.turning_weight);
}

//  Random tunneling
for (int i = 0; (i < this.rooms.Count); i++) {
Room rm3 = ((Room)(this.rooms[i]));
for (int j = 1; (j < rm3.opening_num); j++) {
this.tunnelRandom(rm3.opening[j,0], rm3.opening[j,1], rm3.opening[j,2], 3);
}

}

}

}

public void tunnel(int x, int y, int dir)
{
if (((pcgrid[x, y] == 2)
|| (pcgrid[x, y] == 3)))
{
pcgrid[x, y] = 3;
}

//  If on top of wall or door
pcgrid[x, y] = 4;
//  Set cell to corridor
this.tunnelRandom(x, y, this.shuffleDir(dir, 85), 3);
//  Randomly choose next cell to go to
}

public void tunnelRandom(int x, int y, int dir, int iteration)
{
if ((iteration == 0))
{
return;
}

//  End of recursion iteration
//  Choose a random direction and check to see if that cell is occupied, if not, head in that direction
switch (dir)
{
case 0:
if (!this.blockCorridor(x, (y - 1), 0))
{
this.tunnel(x, (y - 1), dir);
}

//  North
this.tunnelRandom(x, y, this.shuffleDir(dir, 0), (iteration - 1));
//  Try again
break;
case 1:
if (!this.blockCorridor((x + 1), y, 1))
{
this.tunnel((x + 1), y, dir);
}

//  East
this.tunnelRandom(x, y, this.shuffleDir(dir, 0), (iteration - 1));
//  Try again
break;
case 2:
if (!this.blockCorridor(x, (y + 1), 0))
{
this.tunnel(x, (y + 1), dir);
}

//  South
this.tunnelRandom(x, y, this.shuffleDir(dir, 0), (iteration - 1));
//  Try again
break;
case 3:
if (!this.blockCorridor((x - 1), y, 1))
{
this.tunnel((x - 1), y, dir);
}

//  West
this.tunnelRandom(x, y, this.shuffleDir(dir, 0), (iteration - 1));
//  Try again
break;
}
}吃的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Roguelike pcg dungeon map world