游戏开发论坛

 找回密码
 立即注册
搜索
查看: 3165|回复: 0

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

[复制链接]

1万

主题

1万

帖子

3万

积分

论坛元老

Rank: 8Rank: 8

积分
36572
发表于 2020-3-10 11:10:43 | 显示全部楼层 |阅读模式
对《元气骑士》这款游戏想必大家都不陌生。

元气骑士是由凉屋游戏工作室研发的一款角色扮演类游戏,于2017年2月17日发布。 游戏讲述了外星生物夺走维持世界的魔法石的故事,玩家将扮演骑士、刺客等角色参与游戏,夺回魔法石。

Unity Connect社区博主超级汽水借助Unity对游戏中的地图生成系统进行了复刻。与各位分享在此。

地图显示方式

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


地图生成思路梳理

因为在原版游戏中房间并不是完全的正方形,所以我们在设计时要考虑到边长的问题。

虽然游戏中的房间数量是随机的,但是也是有一个范围的。所以需要按照生成地图的最终面积来做一个约束。

走廊连接算是游戏中的一个难点,因为走廊的长度是不确定的要在生成房间后根据房间的位置和大小来确定走廊的生成位置以及长度。

在游戏中,每一个大关卡包含多个小关卡,每个小关卡中又包含了多张地图,每个地图中又有多个小房间。

这样说可能有点不太好理解我画了张图来帮忙理解:

1.png

实现思路以及简化

在之前博主已经发过两篇文章介绍tilemap生成随机地图的方法,如果对tilemap不太了解可以点击【阅读原文】关注博主,获取往期教程。

整体的实现流程如下:

1、根据用户定义创建一张足够大的地图 (这里的地图指每个小关所有房间所在的大地图)。

2.png

2 、假设大地图中所有位置布满大小相同的房间 (这里的房间边长一定要奇数,比较好操作),然后根据所需的房间总数来删掉多余的房间。

3.png

4.png

5.png

3 .根据规定的长宽比,来随机修改每个房间的大小。

6.png

4 .生成过道连通所有房间,(这里黄色的点是原先没有改变大小时的正方形的中心点)

7.png

5 .确定特殊房间的位置。

8.png

Tilemap中单独房间效果图:

9.png
一个简单的房间以及半个过道

10.png
长方形连接正方形房间,黄色方块为中心点

Unity中详细操作

1、创建一个普通的Tilemap。

2、创建一个空物体,用来挂载我们的地图生成脚本。

11.png

3、 创建所需要的变量

  1. <p>public class MapManager : MonoBehaviour</p><p>{</p><p>    [Header("地图种子")] public int seed = 123456;</p><p>    [Header("每行房间数")] public int mapMaxW;</p><p>    [Header("每列房间数")] public int mapMaxH;</p><p>    [Header("总生成房间数")] public int mapCount;</p><p>    [Header("最大房间宽")] public int roomMaxW;</p><p>    [Header("最大房间高")] public int roomMaxH;</p><p>    [Header("最小房间宽")] public int roomMinW;</p><p>    [Header("最小房间高")] public int roomMinH;</p><p>    [Header("房间的间隔距离")] public int distance;</p><p>    [Header("地板")] public TileBase floor;</p><p>    [Header("墙")] public TileBase wall;</p><p>    [Header("地图")] public Tilemap tilemap;</p><p>    private int[,] _roomMap;</p><p>    private List<Vector3Int> _centerPoint;</p><p>    private Dictionary<Vector3Int,int> _mapPoint;</p><p>}</p>
复制代码

4、列出所需函数

  1. <p>//画出地图</p><p>private void DrawMap(){}</p><p>//画出房间</p><p>private void DrawRoom(int roomX,int roomY){}</p><p>//画出路</p><p>private void DrawRoad(){}</p><p>//画出地板和墙壁</p><p>private void DrawFloor(){}</p><p>//生成房间 (用二维 int 数组表示)</p><p>private int[,] RandomRoom(int maxW, int maxH, int minW, int minH, out int width, out int height){}</p><p>//生成一个地图 (用二维 int 数组表示)</p><p>private int[,] GetRoomMap(int mapW, int mapH, int roomCount)</p><p>//获取一个范围内的随机奇数</p><p>private int GetOddNumber(int min, int max){}</p><p>//获取下一个房间的位置</p><p>private Vector2Int GetNextPoint(Vector2Int nowPoint, int maxW, int maxH)</p><p>
  2. </p>
复制代码

5 、实现各个函数

代码已经放在文末的附件,这里我讲一下我的大体思路以及各个函数的作用。

首先是使用,我在 Update 函数中检测按下 R 键重新生成地图,生成很简单只要调用 DrawMap 函数就可以。

  1. <p>private void Update()</p><p>{</p><p>    if (Input.GetKeyDown(KeyCode.R))</p><p>    {</p><p>        DrawMap();</p><p>    }</p><p>}</p>
复制代码

在 DrawMap 中 会调用 DrawRoad 和 DrawFloor 俩个函数来分别画出道路和地板、墙壁。

部分函数解析:

  1. <p>//取一定范围内的一个奇数</p><p>private int GetOddNumber(int min, int max)</p><p>{</p><p>    while (true)</p><p>    {</p><p>        var temp = Random.Range(min, max);</p><p>        if ((temp & 1) != 1) continue;</p><p>        return temp;</p><p>    }</p><p>}</p>
复制代码

这个函数主要用来在房间生成的时候取一个随机的奇数出来,因为我们的房间连接需要一个中心点所以奇数更方便使用。

  1. <p>//生成一个房间,用二维 int 数组表示</p><p>private int[,] RandomRoom(int maxW, int maxH, int minW, int minH, out int width, out int height)</p><p>{</p><p>    width = GetOddNumber(minW, maxW);</p><p>    height = GetOddNumber(minH, maxH);   </p><p>    var room = new int[width, height];</p><p>    //方便以后扩展使用了二维数组,这里为了演示方便对房间内生成其他物体</p><p>    for (var i = 0; i < width; i++)</p><p>    {</p><p>        for (var j = 0; j < height; j++)</p><p>        {</p><p>            room[i, j] = 1;</p><p>        }</p><p>    }</p><p>    return room;</p><p>}</p>
复制代码

这个函数主要用来生成一个房间,选用二维int数组主要是为了扩展、存储时方便。本案例中 普通地板为 1,墙壁为 0。同时为了后续步骤计算方便这里把生成的房间的长宽也返回了。

  1. <p>private int[,] GetRoomMap(int mapW, int mapH, int roomCount)</p><p>{</p><p>    //第一个房间的坐标点</p><p>    var nowPoint = Vector2Int.zero;</p><p>    //当前生成的房间数</p><p>    var mCount = 1;</p><p>    //当前地图</p><p>    var map = new int[mapW, mapH];</p><p>    //第一个格子总有房间,作为出生房间</p><p>    map[nowPoint.x, nowPoint.y] = 1;</p><p>
  2. </p><p>    while (mCount < roomCount)</p><p>    {</p><p>        nowPoint = GetNextPoint(nowPoint, mapW, mapH);</p><p>        if (map[nowPoint.x, nowPoint.y] == 1) continue;</p><p>        map[nowPoint.x, nowPoint.y] = 1;</p><p>        mCount ++;</p><p>    }</p><p>    return map;</p><p>}</p>
复制代码

这个函数用来生成总体的地图,也就是用来查看哪里需要生成房间。需要生成房间的地方为 1 ,空白的地方为 0。与GetNextPoint 连用获取下一个房间的坐标点。因为房间总是相连所以不存在间隔。

  1. <p>private Vector2Int GetNextPoint(Vector2Int nowPoint, int maxW, int maxH)</p><p>{</p><p>    while (true)</p><p>    {</p><p>        var mNowPoint = nowPoint;</p><p>
  2. </p><p>        switch (Random.Range(0, 4))</p><p>        {</p><p>            case 0:</p><p>                mNowPoint.x += 1;</p><p>                break;</p><p>            case 1:</p><p>                mNowPoint.y += 1;</p><p>                break;</p><p>            case 2:</p><p>                mNowPoint.x -= 1;</p><p>                break;</p><p>            default:</p><p>                mNowPoint.y -= 1;</p><p>                break;</p><p>        }</p><p>
  3. </p><p>        if (mNowPoint.x >= 0 && mNowPoint.y >= 0 && mNowPoint.x < maxW && mNowPoint.y < maxH)</p><p>        {</p><p>            return mNowPoint;</p><p>        }</p><p>    }</p><p>}</p>
复制代码

这个函数大体的思想还是参照我前俩篇文章中讲到的随机游走法的思路。

最终效果

1、生成房间

12.png

2 、添加横向过道

13.png

3 、添加纵向过道

14.png

4 、添加墙壁

15.png

作者:超级汽水  
来源:Unity官方平台
原地址:https://mp.weixin.qq.com/s/OZggLLodTF9cownbt2FzGg


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2024-4-20 10:54

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表