关于Rougelike游戏地牢地图算法,在3d引擎中的应用实现

作者:Maousama
2017-03-25
58 78 4

引言

本人作为一个游戏爱好者,入坑unity引擎半年。作为一个小白,在假期的时候尝试写一下 rouguelike游戏,但在一开始的图算法的时候就遇到了瓶颈,花了很长的时间去摸索,因为国内对于地图算法程序的博客比较难找,翻阅很多国外网站(用翻译软件),自己写了一个小算法。希望能帮到一些热爱roguelike游戏的新手开发者。

(在最近的时间本人玩了很多的Roguelike游戏:以撒的结合,地牢之魂等等,我们就以它们为参考)

在写算法的时候我们要想到如何使它快速的应用于游戏引擎,所以这里我把地图的每种元素以坐标(数组来表示坐标)对应起来。在Unity中吧相应的GameObject对应地图元素的坐标,就可以直接生成一个roguelike3d的地牢场景。

在这些地牢游戏中,他们每一层都有一个角色最初所在房间(初始房间)所以这里我们先定个小小目标创建初始房间。我们以一个简单的例子,以撒的结合伪随机地图来说。(原作肯定不是我这种创建方法,但主要是一个地牢创建的思想)这里算法我用C#来给大家举例。

方法步骤

第一步,初始化地图:第一步主要是创建一个一定空间大小的房间。在程序中我们通过创建二维数组来定义这个房间。同时我们需要创建一个枚举来表示房间中的不同元素,例如#表示地面,由于#是一个符号类型,所以我们将数组用char 来创建:char[,] map;,此时就可以在对应坐标存放相应的元素了。

第二步,创建第一个房间,加入房间类组的List。(list用于存放已经生成的房间,用于在选择一个房间这一步进行随机选择)

第三步,从list中选择一个房间(准备以该房间为标准,选择它四个方向其一,沿着此方向创建新的元素)

第四步,选择一个方向,遍历方向对应的空间是否足够放下一个房间;如果能继续,如果不能返回第三步。

第五步,创建新的房间。

第六步,选择创建房间的个数循环创建房间直到地牢完成

大家根据自身需要来添加自己需要的步骤和需要创建的元素这里放我完成的一个简单的伪随机地牢图以及代码例如

选择特定房间加入元素npc等。。

这里放一张运行结果图

下面是代码

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication2
{
    public class Room
    {
        public int x_H_Length;
        public int y_H_Length;
        public int x_Point;
        public int y_Point;
        public string roomStyle;

    }

    public class NormalRoom : Room
    {
        public NormalRoom()
        {
            x_H_Length = y_H_Length = 4;
            roomStyle = "NormalRoom";
        }
    }
    //可以自己创建需要的房间类如:商店 boss房等,这里我只用到普通房间为例子




    class Dungeon
    {
        //存储房间的数组
        List roomList = new List();
        int roomListLength = 0;

        //定义地牢的整体大小
        int sizex, sizey;
        char[,] map;

        //定义生成地图种子
        public int seed;

        //创建地图元素的枚举这里只用到了地板和门:#和D根据需要来创建这些枚举
        int doorTempx, doorTempy, areaTempx, areaTempy;
        enum Mark
        {
            floor = '#',
            door = 'D',
            monster = 'm',
            chests = 'c'

        }

        //ran用来随机
        Random ran;


        void InitializeDungeon(int sizex,int sizey)
        {
            this.sizex = sizex;
            this.sizey = sizey;
            map = new char[sizex, sizey];
            
        }
        
        //创建房间的方法,下面通过CreateFirstRoom和CreateNextRoom将它调用
        void GenerateRoom(Room Rm)
        {
            for (int i =Rm.x_Point  - Rm.x_H_Length; i <= Rm.x_Point + Rm.x_H_Length; i++)
            {
                for (int j = Rm.y_Point - Rm.y_H_Length; j <= Rm.y_Point + Rm.y_H_Length; j++)
                {
                    map[i, j] = (char)Mark.floor;
                }
            }
            roomList.Add(Rm);
            roomListLength++;
        }

        //创建第一个房间
        void CreateFirstRoom()
        {
            NormalRoom firstR = new NormalRoom();
            //在中心位置创建出初始房间
            firstR.x_Point = sizex / 2;
            firstR.y_Point = sizey / 2;
            GenerateRoom(firstR);
        }

        //上文提到的CreateNextRoom方法:用于在第一个房间创建完成后继续创建房间。
        void CreateNextRoom()
        {
            int roomNum;
            int wallPos;
            bool CDtemp = true;
            while (CDtemp)
            {
                ran = new Random(seed);
                roomNum = ran.Next(roomListLength);
                wallPos = ran.Next(4);
                seed = ran.Next();
                Room m = roomList[roomNum];
                ChoiceNextArea(m, wallPos);
                if (ChoiceNextArea(m, wallPos)) { CDtemp = false; }
            }
            map[doorTempx, doorTempy] = (char)Mark.door;
            Room CNRRoom = new NormalRoom();
            CNRRoom.x_Point = areaTempx;
            CNRRoom.y_Point = areaTempy;
            int p = CNRRoom.x_Point;
            int n = CNRRoom.y_Point;
            //表示生成房间的每个房间的中心点,输出在控制台可查看
            Console.WriteLine(p + " " + n);
            GenerateRoom(CNRRoom);
        }



        //该方法用于选择所选定房间的方向
        private bool ChoiceNextArea(Room Room,int Pos)
        {
            bool CDtemp = false;
            switch (Pos)
            {
                case 0: if (Room.x_Point + 3*Room.x_H_Length + 2 < sizex && map[Room.x_Point + 2 * Room.x_H_Length + 2,Room.y_Point] != '#')
                    {
                        //下方的步骤是为了标记所选方向准备创建的房间中心以及连接两房间门的标记
                        doorTempx = Room.x_Point + Room.x_H_Length + 1;
                        doorTempy = Room.y_Point;
                        areaTempx = Room.x_Point + 2*Room.x_H_Length + 2;
                        areaTempy = Room.y_Point;
                        CDtemp = true;
                    };
                    break;
                    
                case 1: if (Room.y_Point + 3*Room.y_H_Length + 2  0 && map[Room.x_Point - 2 * Room.x_H_Length - 1, Room.y_Point] != '#')
                    {
                        doorTempx = Room.x_Point - Room.x_H_Length - 1;
                        doorTempy = Room.y_Point;
                        areaTempx = Room.x_Point - 2 * Room.x_H_Length - 2;
                        areaTempy = Room.y_Point;
                        CDtemp = true;
                    };
                    break;
                case 3: if (Room.y_Point - 3*Room.y_H_Length - 1 > 0 && map[Room.x_Point ,Room.y_Point - 2 * Room.y_H_Length - 1] != '#')
                    {
                        doorTempx = Room.x_Point;
                        doorTempy = Room.y_Point - Room.y_H_Length - 1;
                        areaTempx = Room.x_Point;
                        areaTempy = Room.y_Point - 2 * Room.y_H_Length - 2;
                        CDtemp = true;
                    };
                    break;
            }
            return CDtemp;
        }


       


       

        public static void FinishDungeon()
        {
            Dungeon level = new Dungeon();
            //seed为生成地图种子的值可以根据你的需求来定它的大小
            level.seed = 8;
            //初始化迷宫
            level.InitializeDungeon(100, 100);
            //创建第一个房间
            level.CreateFirstRoom();
            //p为房间数目,循环创建10个1房间
            for(int p = 0; p < 10; p++)
            {
                level.CreateNextRoom();
            }
           


            //输出整个地图到控制台,检查生成的地图
            for(int i = 0; i < level.sizex; i++)
            {
                for(int j = 0; j < level.sizey; j++)
                {
                    Console.Write(level.map[i, j]);
                }
                Console.WriteLine();
            }
            Console.WriteLine(level.roomList[0].ToString());
        }
    }


    
    
    class Program
    {
        static void Main(string[] args)
        {
            Dungeon.FinishDungeon();
        }
    }
}

实现之后可以得到每个坐标以及对应的元素,在unity中生成对应坐标的预设物体就就做好了一个Roguelike

程序的实现很简单,由于本人只是新手所以写的不是很好,请大家见谅!

近期点赞的会员

 分享这篇文章

您可能还会对这些文章感兴趣

参与此文章的讨论

  1. DEMON CHICKEN 2017-04-09 Steam 用户

    消灭14赞0回复

  2. Lianey 2017-05-06

    25赞1回复,真的很棒

  3. trace 2017-07-30

    房间是否可生成检测不是很好,如果创建的房间不是统一大小,你这个检测将会出很大的问题(已经尝试过了会有部分房间区域重复)建议使用房间边缘检测。和循环生成房间的While里面如果设置不合理很容易卡成死循环(总的地图过小,房间数量过多情况),建议使用循环次数上限。

    最近由 trace 修改于:2017-07-30 22:02:35
  4. Way 2018-07-25

    收藏,期待用得上的那天

您需要登录或者注册后才能发表评论

登录/注册