前言
最近在开发一款跑酷类小游戏,考虑到地编很麻烦,决定写一段随机生成地图的程序。由于项目时间紧张,本文内容比较简单,不适用于复杂的跑酷地图生成,如有错误,请斧正。
地图设计
一、设计地图样式
选取了类似地铁跑酷的地图(当然比它要简单得多),分为三条跑道,玩家通过切换跑道、跳跃、滑铲等方式避开障碍物。可将跑道看作一个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)方法的基础上修改,希望本文可以给你一些灵感。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/136131.html