C#三大迷宫生成算法
今天介绍一下很经典的三大迷宫算法的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#三大迷宫生成算法相关推荐
- 随机迷宫生成算法——深度优先算法
迷宫是我们小时候经常玩的游戏,如何用代码来快速生成上面这种迷宫呢? 迷宫算法有三大算法:深度优先算法.prim算法和递归分割算法.这里用的是深度优先算法,在此说一下算法思路,希望对各位有所帮助. 首先 ...
- c语言 迷宫深度遍历 算法,图的遍历迷宫生成算法浅析
1. 引言 在平常的游戏中,我们常常会碰到随机生成的地图.这里我们就来看看一个简单的随机迷宫是如何生成. 2. 迷宫描述随机生成一个m * n的迷宫,可用一个矩阵maze[m][n]来表示,如图: ...
- [迷宫中的算法实践]迷宫生成算法——Prim算法
普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)), ...
- 迷宫生成算法和迷宫寻路算法
迷宫生成算法和迷宫寻路算法 大学二年级的时候,作为对栈这个数据结构的复习,我制作了一个迷宫生成算法的小程序,当时反响十分好,过了几天我又用自己已经学的DirectX技术制作了DirectX版的程序.这 ...
- 迷宫生成算法---深度优先算法(基于python)
迷宫生成算法---深度优先算法 总体的目录 版权及协议声明 更加舒服的阅读方式 一. 深度优先算法的原理与思路 二.迷宫的制作 迷宫的总体的创建. 三.代码的实现 总体的目录 版权及协议声明 本文章遵 ...
- 随机迷宫生成算法——prime算法
本以为Prime迷宫生成算法和图论的Prime算法有什么关联,貌似并没有. Prime迷宫生成算法的原理: (1)初始地图所有位置均设为墙 (2)任意插入一个墙体进墙队列 (3)判断此时墙体是否可以设 ...
- 随机迷宫生成算法浅析
摘要 本文对随机迷宫生成进行了初步的研究和分析,并给出了两种不同的生成算法.最终的算法结合了图的深度优先遍历.通过对比两种算法之间,可发现,在实际问题中,结合了离散数学的方法往往非更有效率且效果更佳. ...
- HTML+CSS+JavaScript 迷宫生成算法 【建议收藏】
最近发现很多人都在写算法类的博客,今天就献丑了使用HTML,CSS和JavaScript制作一个简单的迷宫生成小代码.证明了自己对一些简单的小算法还是可以驾驭的,基本功没有荒废. 迷宫生成有很多种算法 ...
- android迷宫生成算法,【Unity算法实现】简单回溯法随机生成 Tile Based 迷宫
算法学习自 作者eastecho 在IndieNova上发表的文章 简单的使用回溯法生成 Tile Based 迷宫 , 我只是简单做了一下Unity的实现. 基础算法的简单说明 简单说明一下算法步骤 ...
最新文章
- 重视隐私信息安全,笑迎人脸识别百亿蓝海
- 理解mipi协议【转】
- 用node批量压缩html页面
- jsp/servlet上传
- 简化从Win32到Windows 10的迁移之路
- 分析称地图服务将成移动行业未来
- Java生成和操作Excel文件
- 移动web中的幻灯片切换效果
- python抓取qq群消息,python 爬取qq群员信息
- php美颜滤镜,美颜滤镜的虚幻不如一支玻尿酸来的真实
- 液晶屏接口定义_简介TFT-LCD液晶屏接口类型之LVDS接口
- 农夫山泉,它欺骗我们了吗?
- python2.7 BeautifulSoup 爬QQ空间说说-含源码-第一天
- 如何设置maya的Render.exe程序来mayabatch批量渲染
- python print时清除上一行_python3,如何用print清除行覆盖?
- 模拟设计的100条圣经(汉化版)
- Dell R710 iDRAC6 远程控制卡设置
- python汉字排序_Python实现针对中文排序的方法
- 韶关学院计算机科学与技术代码,2009年韶关学院各专业代码
- 【智能车学习】电磁循迹中的基本控制算法