30分钟简易复刻元气骑士地图生成系统

本文转自Unity Connect博主 超级汽水

一个简易的房间类 Roguelike 游戏地图生成系统

如果你还不知道《元气骑士》是什么游戏,也许你可以去看看这个视频

哔哩哔哩 ( ゜- ゜)つロ 乾杯~ Bilibili

地图显示方式

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 .添加墙壁

原文链接:https://connect.unity.com/p/30fen-zhong-jian-yi-fu-ke-yuan-qi-qi-shi-di-tu-sheng-cheng-xi-tong?app=true

更多干货,戳上方链接下载Unity官方app,在线互动答疑技术社区,学习交友两不误!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • 一. Java基础部分.................................................
    wy_sure阅读 3,785评论 0 11
  • SwiftDay011.MySwiftimport UIKitprintln("Hello Swift!")var...
    smile丽语阅读 3,826评论 0 6
  • 小编费力收集:给你想要的面试集合 1.C++或Java中的异常处理机制的简单原理和应用。 当JAVA程序违反了JA...
    八爷君阅读 4,571评论 1 114
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young阅读 3,771评论 1 10
  • 又是一个满怀期待的一个月到尾巴了,实现的,距离目标又相差很远。 也是预料之中的事情,但当事实摆在眼前还...
    油布雨伞阅读 954评论 3 0