【unity】关于一个简单跑酷地图自动生成的思路与实现

前言

        最近在开发一款跑酷类小游戏,考虑到地编很麻烦,决定写一段随机生成地图的程序。由于项目时间紧张,本文内容比较简单,不适用于复杂的跑酷地图生成,如有错误,请斧正。

地图设计

        一、设计地图样式

        选取了类似地铁跑酷的地图(当然比它要简单得多),分为三条跑道,玩家通过切换跑道、跳跃、滑铲等方式避开障碍物。可将跑道看作一个3行n列的网格,每个障碍物占一格空间。

        二、障碍物类型

        1、柱型障碍物。即玩家不可跳跃或滑铲躲避的障碍物,只能通过切换跑到躲开。

        2、低矮型障碍物。即玩家可以跳过或切换跑道躲避的障碍物。

        3、悬挂型障碍物。即玩家可以滑铲或切换跑道躲避的障碍物。

        4、可消除型障碍物。即玩家可通过某个操作使其消除的障碍物,同时也可以切换跑道躲避。

        三、添加生成限制

        为了简化地图生成方法,并保证不出现死路,为障碍物的生成添加两个限制:

        1、每种类型的障碍物在每一列至多生成一个。

        2、在每一行(即每条跑道)中每个障碍物的前后两格内不能生成障碍物。

实现过程

        一、总体思路

        首先将地图3行n列的网格储存为一个字符类型的二维数组,用不同符号表示不同的障碍物类型以及通路。

        定义四个障碍物类实现同一个接口,并在类中提供生成本类障碍物的方法。

        再定义一个地图生成类,然后依次调用每类障碍物中的方法,生成每种障碍物,例如先生成地图上所有的柱型障碍物,再生成所有的低矮型障碍物,以此类推生成四种类型的障碍物。

        最后遍历该数组,在场景中绘制出完整的地图。

        二、程序设计

        UML类图如下:

        首先定义一个障碍物接口,包含四个方法:创建障碍物(createBarrier)、间隔数增加(intervalIncrease)、重置间隔数(intervalReset)、是否可以放置障碍物(isCanBeBarrier)。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public interface Barrier
{
    public char[,] createBarrier(char[,] map, int mapLength);

    public int intervalIncrease(int interval);

    public void intervalReset();

    public bool isCanBeBarrier();
}

       

        其次分别定义四种障碍物类实现该接口。此处以柱型障碍物为例。使用三个参数控制随机生成,分别为:最小生成间隔(minInterval)、最大生成间隔(maxInterval)、生成概率(rate)。并定义两个属性:当前间隔数(currInterval)、代表字符(barrierChar)。

        重写接口中的方法:

        先判断是否满足三个参数所控制的条件,当当前间隔数大于最小生成间隔且随机数落在概率区间时,表示可以生成障碍物,当当前间隔数大于最大间隔数时不论概率,都可以生成障碍物。代码如下:

    public bool isCanBeBarrier()
    {
        System.Random random = new System.Random();
        //当该位置与上一放置位置的间隔大于等于最小间隔时,按照概率生成障碍物
        if (random.Next(1, 101) <= rate && currInterval >= minInterval || currInterval >= maxInterval)
            return true;
        else
            return false;
    }

        基于上面的函数返回值,再加上上文提到的两个限制条件,即可得到障碍物生成函数,代码如下:

public char[,] createBarrier(char[,] map, int mapLength)
    {
        //每行生成一个该类型的障碍
        for (int i = 0; i < mapLength; i++)
        {
            System.Random random = new System.Random();
            if (isCanBeBarrier())
            {
                int idx = random.Next(0, 3);
                //当该位置是通路且前面两格内没有障碍物时则可放置障碍物
                if (map[idx, i] == 'o' && map[idx, i - 1] == 'o' && map[idx, i - 2] == 'o' && map[idx, i + 1] == 'o' && map[idx, i + 2] == 'o')
                {
                    //在数组中生成一个障碍物
                    map[idx, i] = barrierChar;

                    //重置间隔
                    intervalReset();
                }
            }
            else
            {
                currInterval = intervalReduce(currInterval);
            }
        }
        //返回生成后的地图
        return map;

    }

       剩余两个方法的重写较为简单,这里就不多做赘述。

        然后定义生成地图类,该类中定义了一个静态变量map,是一个储存地图的二位数组。定义一个Barrier类型长度为四的数组barriers,分别用四种障碍物的构造函数实例化,这样做的好处是方便对其统一操作。

        生成地图时只需要遍历barriers数组,分别调用其中的createBarrier(map, mapLength)方法,代码如下:

    public void createMap(int mapLength)
    {
        for(int i = 0; i < 4; i++)
        {
            map = barriers[i].createBarrier(map, mapLength);
        }
    }

        当然别忘了将地图全部初始化为通路,可将数组全部元素初始化成“o”。

        最后定义地图绘制类绘制地图。

        获取每种障碍物的预制体,遍历map数组并将预制体实例化。代码如下:

public void drawMap()
    {
        //实例化地图
        for(int i = 0; i < mapLength; i++)
        {
            for(int j = 0; j < 3; j++)
            {
                Vector3 position = new Vector3(j - 1, 0, i);
                switch (MapCreater.map[j, i])
                {
                    case 'o':
                        //通路
                        break;
                    case '#':
                        //柱形障碍
                        Instantiate(barrier_high, position, barrier_high.transform.rotation);
                        break;
                    case '_':
                        //低矮型障碍
                        Instantiate(barrier_down, position, barrier_down.transform.rotation);
                        break;
                    case '^':
                        //悬挂型障碍
                        Instantiate(barrier_up, position, barrier_up.transform.rotation);
                        break;
                    case '!':
                        //活动型障碍
                        Instantiate(barrier_active, position, barrier_active.transform.rotation);
                        break;
                    default:
                        Debug.LogError("地图错误!!!");
                        break;
                }
            }
        }
    }

        该方法只负责生成障碍物,别忘了生成地面哦。

        上述内容只介绍了每个类的主要方法,还有例如初始化等简单的方法没有过多赘述,诸位可自行探索。

最终效果

        仅作为演示用,所以没有添加美术素材,有点抽象请见谅。

        障碍物基本均匀分布,密度和频率可由最小间隔数、最大间隔数、概率三个参数决定,实例中每个障碍物的参数均相同,可改变参数使其不同来增加随机性。

小结

        本文中的每种障碍物的生成方法完全相同,为增加其多样性,可为每种障碍物“量身定制”这些方法。同时本文的障碍物均占地一格,如果想生成占一个跑道的多格或者多个跑道的障碍物,可在对应障碍物类中createBarrier(char[,] map, int mapLength)方法的基础上修改,希望本文可以给你一些灵感。

おすすめ

転載: blog.csdn.net/m0_67636792/article/details/134614936