引言

本人作為一個游戲愛好者,入坑unity引擎半年。作為一個小白,在假期的時候嘗試寫一下 rouguelike游戲,但在一開始的圖算法的時候就遇到了瓶頸,花了很長的時間去摸索,因為國內(nèi)對于地圖算法程序的博客比較難找,翻閱很多國外網(wǎng)站(用翻譯軟件),自己寫了一個小算法。希望能幫到一些熱愛roguelike游戲的新手開發(fā)者。

(在最近的時間本人玩了很多的Roguelike游戲:以撒的結(jié)合,地牢之魂等等,我們就以它們?yōu)閰⒖迹?/p>

?

在寫算法的時候我們要想到如何使它快速的應(yīng)用于游戲引擎,所以這里我把地圖的每種元素以坐標(數(shù)組來表示坐標)對應(yīng)起來。在Unity中吧相應(yīng)的GameObject對應(yīng)地圖元素的坐標,就可以直接生成一個roguelike3d的地牢場景。

在這些地牢游戲中,他們每一層都有一個角色最初所在房間(初始房間)所以這里我們先定個小小目標創(chuàng)建初始房間。我們以一個簡單的例子,以撒的結(jié)合偽隨機地圖來說。(原作肯定不是我這種創(chuàng)建方法,但主要是一個地牢創(chuàng)建的思想)這里算法我用C#來給大家舉例。

?

方法步驟

第一步,初始化地圖:第一步主要是創(chuàng)建一個一定空間大小的房間。在程序中我們通過創(chuàng)建二維數(shù)組來定義這個房間。同時我們需要創(chuàng)建一個枚舉來表示房間中的不同元素,例如#表示地面,由于#是一個符號類型,所以我們將數(shù)組用char 來創(chuàng)建:char[,] map;,此時就可以在對應(yīng)坐標存放相應(yīng)的元素了。

第二步,創(chuàng)建第一個房間,加入房間類組的List。(list用于存放已經(jīng)生成的房間,用于在選擇一個房間這一步進行隨機選擇)

第三步,從list中選擇一個房間(準備以該房間為標準,選擇它四個方向其一,沿著此方向創(chuàng)建新的元素)

第四步,選擇一個方向,遍歷方向?qū)?yīng)的空間是否足夠放下一個房間;如果能繼續(xù),如果不能返回第三步。

第五步,創(chuàng)建新的房間。

第六步,選擇創(chuàng)建房間的個數(shù)循環(huán)創(chuàng)建房間直到地牢完成

大家根據(jù)自身需要來添加自己需要的步驟和需要創(chuàng)建的元素這里放我完成的一個簡單的偽隨機地牢圖以及代碼例如

選擇特定房間加入元素npc等。。

這里放一張運行結(jié)果圖

下面是代碼

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";
        }
    }
    //可以自己創(chuàng)建需要的房間類如:商店 boss房等,這里我只用到普通房間為例子




    class Dungeon
    {
        //存儲房間的數(shù)組
        List roomList = new List();
        int roomListLength = 0;

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

        //定義生成地圖種子
        public int seed;

        //創(chuàng)建地圖元素的枚舉這里只用到了地板和門:#和D根據(jù)需要來創(chuàng)建這些枚舉
        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];
            
        }
        
        //創(chuàng)建房間的方法,下面通過CreateFirstRoom和CreateNextRoom將它調(diào)用
        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++;
        }

        //創(chuàng)建第一個房間
        void CreateFirstRoom()
        {
            NormalRoom firstR = new NormalRoom();
            //在中心位置創(chuàng)建出初始房間
            firstR.x_Point = sizex / 2;
            firstR.y_Point = sizey / 2;
            GenerateRoom(firstR);
        }

        //上文提到的CreateNextRoom方法:用于在第一個房間創(chuàng)建完成后繼續(xù)創(chuàng)建房間。
        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] != '#')
                    {
                        //下方的步驟是為了標記所選方向準備創(chuàng)建的房間中心以及連接兩房間門的標記
                        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為生成地圖種子的值可以根據(jù)你的需求來定它的大小
            level.seed = 8;
            //初始化迷宮
            level.InitializeDungeon(100, 100);
            //創(chuàng)建第一個房間
            level.CreateFirstRoom();
            //p為房間數(shù)目,循環(huán)創(chuàng)建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();
        }
    }
}

實現(xiàn)之后可以得到每個坐標以及對應(yīng)的元素,在unity中生成對應(yīng)坐標的預(yù)設(shè)物體就就做好了一個Roguelike

程序的實現(xiàn)很簡單,由于本人只是新手所以寫的不是很好,請大家見諒!