今天介绍一下很经典的三大迷宫算法的C#实现,即随机普利姆算法,深度优先算法和十字分割(也就是递归分割算法)。实现参考了[ActionScript 3] 三大迷宫生成算法一文(生成的迷宫预览图也使用的该文中的示意图),并且讲三种方法进行分装,方便游戏调用。

1、设计基类Maze类

为了方便我们游戏逻辑去调用三种迷宫算法,我们设计一个基类供继承,基类是一个抽象类,其中包括一些迷宫地图的必要属性和生成迷宫的抽象方法。
Maze.cs:

public abstract class Maze
{internal int[,] _map;internal Vector2 _originPos;internal int _hight;internal int _width;public int[,] map {get{ return _map;}}public Vector2 originPos {get{return _originPos;}}public int height {get{return _hight;}}public int width{get {return _width;}}public abstract void GenerateMaze(int width, int height, Vector2 originPos);
}

2、随机普利姆算法

随机普里姆法生成的迷宫岔路较多,其生成方法通俗来说就是在全是墙的迷宫里打通路,所有可以走的地方上随机挖洞,创造出新的可以走的地方。

随机prim算法的核心是:
1. 让迷宫全是墙
2. 选一个格作为迷宫的通路,然后把它的邻墙放入列表
3. 当列表里还有墙时:
——3.1.从列表里随机选一个墙,如果它对面的格子不是迷宫的通路 :
————把墙打通,让对面的格子成为迷宫的通路
————把那个格子的邻墙加入列表
——3.2.如果对面的格子已经是通路了,那就从列表里移除这面墙

RPMaze.cs:

public class RPMaze : Maze
{/// <summary>/// 生成随机普利姆算法迷宫/// 地图尺寸必须为奇数,坑!/// </summary>public override void GenerateMaze(int width, int height, Vector2 originPos){// 设置迷宫数据_map = new int[height, width];_width = width;_hight = height;_originPos = originPos;for (int i = 0; i < height; i++){for (int j = 0; j < width; j++){_map[i, j] = 0;}}// 设置起点为目标个var targetX = (int)originPos.x;var targetY = (int)originPos.y;map[targetX, targetY] = 1;// 记录邻墙var rpBlockPos = new List<MazeBlock>();// 如果上方有临墙,将临墙加入列表if (targetX > 1){var block = new MazeBlock(targetX - 1, targetY, BlockDirection.Up);rpBlockPos.Add(block);}// 如果右方有临墙,将临墙加入列表if (targetY < width - 2){var block = new MazeBlock(targetX, targetY + 1, BlockDirection.Right);rpBlockPos.Add(block);}// 如果下方有临墙,将临墙加入列表if (targetX < height - 2){var block = new MazeBlock(targetX + 1, targetY, BlockDirection.Down);rpBlockPos.Add(block);}// 如果左方有临墙,将临墙加入列表if (targetY>1){var block = new MazeBlock(targetX, targetY - 1, BlockDirection.Left);rpBlockPos.Add(block);}while (rpBlockPos.Count > 0) {// 随机选一面墙long tick = System.DateTime.Now.Ticks;System.Random ran = new System.Random((int)(tick & 0xffffffffL) | (int)(tick >> 32));int blockIndex = ran.Next(0, rpBlockPos.Count);switch (rpBlockPos[blockIndex].direction){case BlockDirection.Up:targetX = rpBlockPos[blockIndex].x - 1;targetY = rpBlockPos[blockIndex].y;break;case BlockDirection.Down:targetX = rpBlockPos[blockIndex].x + 1;targetY = rpBlockPos[blockIndex].y;break;case BlockDirection.Left:targetX = rpBlockPos[blockIndex].x;targetY = rpBlockPos[blockIndex].y - 1;break;case BlockDirection.Right:targetX = rpBlockPos[blockIndex].x ;targetY = rpBlockPos[blockIndex].y + 1;break;}//如果目标墙尚未料筒if (_map[targetX, targetY] == 0){// 连通目标墙_map[rpBlockPos[blockIndex].x, rpBlockPos[blockIndex].y] = 1;_map[targetX, targetY] = 1;// 添加目标墙的临墙if (rpBlockPos[blockIndex].direction != BlockDirection.Down && targetX > 1 && _map[targetX - 1, targetY] == 0 && _map[targetX - 2, targetY] == 0){var block = new MazeBlock(targetX - 1, targetY, BlockDirection.Up);rpBlockPos.Add(block);}if (rpBlockPos[blockIndex].direction != BlockDirection.Left && targetY < width - 2 && _map[targetX, targetY + 1] == 0 && _map[targetX, targetY + 2] == 0){var block = new MazeBlock(targetX, targetY + 1, BlockDirection.Right);rpBlockPos.Add(block);}if (rpBlockPos[blockIndex].direction != BlockDirection.Up && targetX < height - 2 && _map[targetX + 1, targetY] == 0 && _map[targetX + 2, targetY] == 0){var block = new MazeBlock(targetX + 1, targetY, BlockDirection.Down);rpBlockPos.Add(block);}if (rpBlockPos[blockIndex].direction != BlockDirection.Right && targetY > 1 && _map[targetX, targetY - 1] == 0 && _map[targetX, targetY - 2] == 0){var block = new MazeBlock(targetX, targetY - 1, BlockDirection.Left);rpBlockPos.Add(block);}}// 移除目标墙rpBlockPos.RemoveAt(blockIndex);}}
}

预览图:

3、深度优先算法

深度优先法生成的迷宫有着一条明显的主路,看起来比较简单。其生成方法其实就是深度遍历法。

深度优先算法的核心是:
1. 将起点作为当前格并标记
2. 当还存在未标记的格时:
——2.1.如果当前格有未标记的邻格:
————随机选择一个未标记的邻格
————将当前格入栈
————移除当前格与邻格的墙
————标记邻格并用它作为当前格
——2.2.反之,如果栈不空:
————栈顶的格子出栈
————令其成为当前格
——2.3.反之,随机选择一个格子为当前格并标记

RBMaze.cs:

public class RBMaze : Maze
{public override void GenerateMaze(int width, int height, Vector2 originPos) {// 设置迷宫数据_map = new int[height, width];_width = width;_hight = height;_originPos = originPos;for (int i = 0; i < height; i++){for (int j = 0; j < width; j++){_map[i, j] = 0;}}// 设置起点为目标格var targetX = (int)originPos.x;var targetY = (int)originPos.y;map[targetX, targetY] = 1;var rbDirection = new List<BlockDirection>();var blockStack = new List<MazeBlock>();do{// 检测周围有没有未连通的格子rbDirection.Clear();// 检测上方if(targetX > 1 && map[targetX - 2, targetY] == 0)rbDirection.Add(BlockDirection.Up);// 检测右方if(targetY < width - 2 && map[targetX, targetY + 2] == 0)rbDirection.Add(BlockDirection.Right);// 检测下方if(targetX < height - 2 && map[targetX + 2, targetY] == 0)rbDirection.Add(BlockDirection.Down);// 检测左方if(targetY > 1 && map[targetX, targetY - 2] == 0)rbDirection.Add(BlockDirection.Left);// 选出下一个当前格if(rbDirection.Count > 0) {long tick = System.DateTime.Now.Ticks;System.Random ran = new System.Random((int)(tick & 0xffffffffL) | (int)(tick >> 32));int blockIndex = ran.Next(0, rbDirection.Count);// 将当前格入栈var block = new MazeBlock(targetX, targetY);blockStack.Add(block);// 连通邻格,并将邻格指定为当前格switch(rbDirection[blockIndex]) {case BlockDirection.Up:map[targetX - 1, targetY] = 1;targetX -= 2;break;case BlockDirection.Down:map[targetX + 1, targetY] = 1;targetX += 2;break;case BlockDirection.Left:map[targetX, targetY - 1] = 1;targetY -= 2;break;case BlockDirection.Right:map[targetX, targetY + 1] = 1;targetY += 2;break;}// 标记当前格if(targetX > 0 && targetX < height - 1 && targetY > 0 && targetY < width - 1)map[targetX, targetY] = 1;}else if(blockStack.Count > 0) {// 讲栈顶作为当前格,并移除栈顶var index = blockStack.Count - 1;targetX = blockStack[index].x;targetY = blockStack[index].y;blockStack.RemoveAt(index);}} while(blockStack.Count > 0);}
}

预览图:

4、十字(递归)分割算法:

十字分割法生成的迷宫会形成一个一个大小不一的“房间”,适合制作RPG游戏地图。起生成原理及递归法,先画一个十字分成四个部分,在三面墙上打洞,再在每个子部分中重复这一步骤,直至空间不够分割(这个值需要我们自行设置)

RDMaze.cs:

public class RDMaze : Maze
{public void  InitRDMaze() {}public override void GenerateMaze(int width, int height, Vector2 originPos){// 设置迷宫数据_map = new int[height, width];_width = width;_hight = height;_originPos = originPos;for (int i = 0; i < height; i++){for (int j = 0; j < width; j++){if (i == 0 || i == height - 1 || j == 0 || j == width - 1)_map[i, j] = 0;else _map[i, j] = 1;}}RecursiveRDMaze((int)originPos.y, (int)originPos.x, width - 2, height - 2);}private void RecursiveRDMaze(int left, int top, int right, int bottom) {// 分各区域的坐标和面积int rdHeight = bottom - top;int rdWidth = right - left;int area = (rdHeight + 1) * (rdWidth + 1);if (area < 10 || (rdHeight <= 1 && rdWidth <= 1))return;// 计算分割点坐标并在分割方向上补上墙int recursiceX = -1;int recursiceY = -1;if (rdWidth > 1){recursiceY= left + 1 + Random.Range(0, rdWidth / 2) * 2;for (int i = top; i < bottom; i++)map[i, recursiceY] = 0;}if (rdHeight > 1){recursiceX = top + 1 + Random.Range(0, rdHeight / 2) * 2;for (int i = left; i <= right; i++)map[recursiceX, i] = 0;}if (rdWidth > 1 && rdHeight > 1){// 选择要打通的墙,确保连通性,打通三面// 0:上,1:下,2:左,3:右int side = Random.Range(0, 4);if (side != 0){var upIndex = Random.Range(0, (recursiceX - 1 - top) / 2 + 1) * 2;map[top + upIndex, recursiceY] = 1;}if (side != 1){var downIndex = Random.Range(0, (bottom - recursiceX - 1) / 2 + 1) * 2;map[recursiceX + 1 + downIndex, recursiceY] = 1;}if (side != 2){var leftIndex = Random.Range(0, (recursiceY - 1 - left) / 2 + 1) * 2;map[recursiceX, left + leftIndex] = 1;}if (side != 3){var rightIndex = Random.Range(0, (right - recursiceY - 1) / 2 + 1) * 2;map[recursiceX, recursiceY + 1 + rightIndex] = 1;}// 递归RecursiveRDMaze(left, top, recursiceY - 1, recursiceX - 1);RecursiveRDMaze(recursiceY + 1, top, right, recursiceX - 1);RecursiveRDMaze(left, recursiceX + 1, recursiceY - 1, bottom);RecursiveRDMaze(recursiceY + 1, recursiceX + 1, right, bottom);}else{if (rdWidth <= 1){var index = Random.Range(0, rdWidth / 2 + 1) * 2;map[recursiceX, left + index] = 1;RecursiveRDMaze(left, top, right, recursiceX - 1);RecursiveRDMaze(left, recursiceX + 1, right, bottom);}if (rdHeight <= 1){var index = Random.Range(0, rdHeight / 2 + 1) * 2;map[top + index, recursiceY] = 1;RecursiveRDMaze(left, top, recursiceY - 1, bottom);RecursiveRDMaze(recursiceY + 1, top, right, bottom);}}}
}

预览图:

By:蒋志杰

C#三大迷宫生成算法相关推荐

  1. 随机迷宫生成算法——深度优先算法

    迷宫是我们小时候经常玩的游戏,如何用代码来快速生成上面这种迷宫呢? 迷宫算法有三大算法:深度优先算法.prim算法和递归分割算法.这里用的是深度优先算法,在此说一下算法思路,希望对各位有所帮助. 首先 ...

  2. c语言 迷宫深度遍历 算法,图的遍历迷宫生成算法浅析

    1. 引言 在平常的游戏中,我们常常会碰到随机生成的地图.这里我们就来看看一个简单的随机迷宫是如何生成. 2. 迷宫描述随机生成一个m * n的迷宫,可用一个矩阵maze[m][n]来表示,如图:   ...

  3. [迷宫中的算法实践]迷宫生成算法——Prim算法

    普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)), ...

  4. 迷宫生成算法和迷宫寻路算法

    迷宫生成算法和迷宫寻路算法 大学二年级的时候,作为对栈这个数据结构的复习,我制作了一个迷宫生成算法的小程序,当时反响十分好,过了几天我又用自己已经学的DirectX技术制作了DirectX版的程序.这 ...

  5. 迷宫生成算法---深度优先算法(基于python)

    迷宫生成算法---深度优先算法 总体的目录 版权及协议声明 更加舒服的阅读方式 一. 深度优先算法的原理与思路 二.迷宫的制作 迷宫的总体的创建. 三.代码的实现 总体的目录 版权及协议声明 本文章遵 ...

  6. 随机迷宫生成算法——prime算法

    本以为Prime迷宫生成算法和图论的Prime算法有什么关联,貌似并没有. Prime迷宫生成算法的原理: (1)初始地图所有位置均设为墙 (2)任意插入一个墙体进墙队列 (3)判断此时墙体是否可以设 ...

  7. 随机迷宫生成算法浅析

    摘要 本文对随机迷宫生成进行了初步的研究和分析,并给出了两种不同的生成算法.最终的算法结合了图的深度优先遍历.通过对比两种算法之间,可发现,在实际问题中,结合了离散数学的方法往往非更有效率且效果更佳. ...

  8. HTML+CSS+JavaScript 迷宫生成算法 【建议收藏】

    最近发现很多人都在写算法类的博客,今天就献丑了使用HTML,CSS和JavaScript制作一个简单的迷宫生成小代码.证明了自己对一些简单的小算法还是可以驾驭的,基本功没有荒废. 迷宫生成有很多种算法 ...

  9. android迷宫生成算法,【Unity算法实现】简单回溯法随机生成 Tile Based 迷宫

    算法学习自 作者eastecho 在IndieNova上发表的文章 简单的使用回溯法生成 Tile Based 迷宫 , 我只是简单做了一下Unity的实现. 基础算法的简单说明 简单说明一下算法步骤 ...

最新文章

  1. 重视隐私信息安全,笑迎人脸识别百亿蓝海
  2. 理解mipi协议【转】
  3. 用node批量压缩html页面
  4. jsp/servlet上传
  5. 简化从Win32到Windows 10的迁移之路
  6. 分析称地图服务将成移动行业未来
  7. Java生成和操作Excel文件
  8. 移动web中的幻灯片切换效果
  9. python抓取qq群消息,python 爬取qq群员信息
  10. php美颜滤镜,美颜滤镜的虚幻不如一支玻尿酸来的真实
  11. 液晶屏接口定义_简介TFT-LCD液晶屏接口类型之LVDS接口
  12. 农夫山泉,它欺骗我们了吗?
  13. python2.7 BeautifulSoup 爬QQ空间说说-含源码-第一天
  14. 如何设置maya的Render.exe程序来mayabatch批量渲染
  15. python print时清除上一行_python3,如何用print清除行覆盖?
  16. 模拟设计的100条圣经(汉化版)
  17. Dell R710 iDRAC6 远程控制卡设置
  18. python汉字排序_Python实现针对中文排序的方法
  19. 韶关学院计算机科学与技术代码,2009年韶关学院各专业代码
  20. 【智能车学习】电磁循迹中的基本控制算法

热门文章

  1. NEUQ—ACM实验班2.4小测试反思总结
  2. Oracle 12c 读书笔记——筑梦之路
  3. Halcon读图出错怎么办?ReadImage常见错误与处理方法(C++)
  4. oracle重命名表空间
  5. 水星路由器wan口ip显示0_路由器WAN口获取不到IP地址怎么办?
  6. windows与ipad互联传文件
  7. 浮云绘图编辑器之直线、方块、圆等基础图元操作和接口源码
  8. 网络编程学习——名字与地址转换(一)
  9. 会说话的艾米 拔萝卜
  10. 关于麦克风的参数介绍 - 驻极体麦克风(ECM)和硅麦(MEMS)