Notifications
Article
30分钟简易复刻元气骑士地图生成系统
Published 10 months ago
3.9 K
25
😎 一个简易的房间类 Roguelike 游戏地图生成系统 (文末附代码文件)
如果你还不知道《元气骑士》是什么游戏,也许你可以去看看这个视频 元气骑士官方介绍

地图显示方式

1. 随机大小的矩形房间。
2. 随机的房间数量。
3. 通过走廊连接每个房间。
4. 每个图都有 俩个特殊房间:“出生房”、“传送房”。

地图生成思路梳理

- 因为在原版游戏中房间并不是完全的正方形,所以我们在设计时要考虑到边长的问题。
- 虽然游戏中的房间数量是随机的,但是也是有一个范围的。所以需要按照生成地图的最终面积来做一个约束。
- 走廊连接算是游戏中的一个难点,因为走廊的长度是不确定的要在生成房间后根据房间的位置和大小来确定走廊的生成位置以及长度。
- 在游戏中,每一个大关卡包含多个小关卡,每个小关卡中又包含了多张地图,每个地图中又有多个小房间。这样说可能有点不太好理解我画了张图来帮忙理解:

实现思路以及简化

在之前已经发过俩篇文章介绍tilemap生成随机地图的方法,如果对tilemap不太了解可以先去看看:
使用tilemap制作随机地图(1)
使用tilemap制作随机地图(2)
整体的实现流程如下:
1 .根据用户定义创建一张足够大的地图 (这里的地图指每个小关所有房间所在的大地图)。
2 .假设大地图中所有位置布满大小相同的房间 (这里的房间边长一定要奇数,比较好操作),然后根据所需的房间总数来删掉多余的房间。
3 .根据规定的长宽比,来随机修改每个房间的大小。
4 .生成过道连通所有房间,(这里黄色的点是原先没有改变大小时的正方形的中心点)
5 .确定特殊房间的位置。

Tilemap中单独房间效果图:

Unity中详细操作

1. 创建一个普通的Tilemap。
2. 创建一个空物体,用来挂载我们的地图生成脚本。
3. 创建所需要的变量
public class MapManager : MonoBehaviour { [Header("地图种子")] public int seed = 123456; [Header("每行房间数")] public int mapMaxW; [Header("每列房间数")] public int mapMaxH; [Header("总生成房间数")] public int mapCount; [Header("最大房间宽")] public int roomMaxW; [Header("最大房间高")] public int roomMaxH; [Header("最小房间宽")] public int roomMinW; [Header("最小房间高")] public int roomMinH; [Header("房间的间隔距离")] public int distance; [Header("地板")] public TileBase floor; [Header("墙")] public TileBase wall; [Header("地图")] public Tilemap tilemap; private int[,] _roomMap; private List<Vector3Int> _centerPoint; private Dictionary<Vector3Int,int> _mapPoint; }
4. 列出所需函数
//画出地图 private void DrawMap(){} //画出房间 private void DrawRoom(int roomX,int roomY){} //画出路 private void DrawRoad(){} //画出地板和墙壁 private void DrawFloor(){} //生成房间 (用二维 int 数组表示) private int[,] RandomRoom(int maxW, int maxH, int minW, int minH, out int width, out int height){} //生成一个地图 (用二维 int 数组表示) private int[,] GetRoomMap(int mapW, int mapH, int roomCount) //获取一个范围内的随机奇数 private int GetOddNumber(int min, int max){} //获取下一个房间的位置 private Vector2Int GetNextPoint(Vector2Int nowPoint, int maxW, int maxH)
5 .实现各个函数 (详细代码在文末附件)
代码已经放在文末的附件,这里我讲一下我的大体思路以及各个函数的作用 首先是使用,我在 Update 函数中检测按下 R 键重新生成地图,生成很简单只要调用 DrawMap 函数就可以 private void Update() { if (Input.GetKeyDown(KeyCode.R)) { DrawMap(); } } 在 DrawMap 中 会调用 DrawRoad DrawFloor 俩个函数来分别画出道路和地板、墙壁。
部分函数解析:
//取一定范围内的一个奇数 private int GetOddNumber(int min, int max) { while (true) { var temp = Random.Range(min, max); if ((temp & 1) != 1) continue; return temp; } } 这个函数主要用来在房间生成的时候取一个随机的奇数出来,因为我们的房间连接需要一个中心点所以奇数更方便使用。
//生成一个房间,用二维 int 数组表示 private int[,] RandomRoom(int maxW, int maxH, int minW, int minH, out int width, out int height) { width = GetOddNumber(minW, maxW); height = GetOddNumber(minH, maxH); var room = new int[width, height]; //方便以后扩展使用了二维数组,这里为了演示方便对房间内生成其他物体 for (var i = 0; i < width; i++) { for (var j = 0; j < height; j++) { room[i, j] = 1; } } return room; } 这个函数主要用来生成一个房间,选用二维int数组主要是为了扩展、存储时方便。本案例中 普通地板为 1墙壁为 0。同时为了后续步骤计算方便这里把生成的房间的长宽也返回了。
private int[,] GetRoomMap(int mapW, int mapH, int roomCount) { //第一个房间的坐标点 var nowPoint = Vector2Int.zero; //当前生成的房间数 var mCount = 1; //当前地图 var map = new int[mapW, mapH]; //第一个格子总有房间,作为出生房间 map[nowPoint.x, nowPoint.y] = 1; while (mCount < roomCount) { nowPoint = GetNextPoint(nowPoint, mapW, mapH); if (map[nowPoint.x, nowPoint.y] == 1) continue; map[nowPoint.x, nowPoint.y] = 1; mCount ++; } return map; } 这个函数用来生成总体的地图,也就是用来查看哪里需要生成房间。需要生成房间的地方为 1 ,空白的地方为 0。与GetNextPoint 连用获取下一个房间的坐标点。因为房间总是相连所以不存在间隔。 private Vector2Int GetNextPoint(Vector2Int nowPoint, int maxW, int maxH) { while (true) { var mNowPoint = nowPoint; switch (Random.Range(0, 4)) { case 0: mNowPoint.x += 1; break; case 1: mNowPoint.y += 1; break; case 2: mNowPoint.x -= 1; break; default: mNowPoint.y -= 1; break; } if (mNowPoint.x >= 0 && mNowPoint.y >= 0 && mNowPoint.x < maxW && mNowPoint.y < maxH) { return mNowPoint; } } } 这个函数大体的思想还是参照我前俩篇文章中讲到的随机游走法的思路。

最终效果 :

1 .生成房间
2 .添加横向过道
3 .添加纵向过道
4 .添加墙壁
代码附件(手机如果看不到请使用网页版):
超级汽水
超级热爱 - Student
14
Comments
刚刚进unity圈
汽水,我不会函数,你能教教我吗?
0
TAKU.Wang
7 months ago
TAKU.Wang感谢大佬的分享,希望您能继续优化tilemap随机地图系列的教程。对于独立游戏制作来说使用最少的素材实现较高的可玩性,随机地图系统真的太重要了。目前这个方式的通路太工整了,有没有可以有转折的通路和不是完全正方形房间的解决方案供小弟参考一下?
还有就是boss的出现位置该如何分配。如果随机出来的房间可以被记录,可能可以帮助随机物体的生成来实现功能房的效果。在下是美工出生编程还处于摸索阶段,能看到大佬通俗易懂的教程真的很感激,所以提出了一些不成熟的要求,希望大佬能帮助我。
0
TAKU.Wang
7 months ago
感谢大佬的分享,希望您能继续优化tilemap随机地图系列的教程。对于独立游戏制作来说使用最少的素材实现较高的可玩性,随机地图系统真的太重要了。目前这个方式的通路太工整了,有没有可以有转折的通路和不是完全正方形房间的解决方案供小弟参考一下?
0
栗山未来
8 months ago
超级汽水要先设置好瓦片
设置瓦片是确定地板跟墙的瓦片对吧 然后瓦片地图就挂tilemap
0
栗山未来
8 months ago
超级汽水你试试用我提供的代码。?
试了你给的代码 还是不行 难道是每行每列房间数设置的不对?
0