本文作者
目录:
1.知名游戏中的大型场景生成
2.迷宫数据生成算法
- 2.1迷宫算法的基本参数
- 2.2递归分割算法
- 2.3深度优先算法
- 2.4 Prim算法特点
3.项目中的迷宫场景部件制作
4.迷宫物理碰撞数据生成
5.迷宫场景资源管理和显示优化
6.总结和展望
1.知名游戏中的大型场景生成
场景程序化生成技术是一个广泛应用在游戏开发中的技术,较早的使用这类技术有名游戏《暗黑破坏神》系列,无论是1代、2代、3代,都使用了这个技术,如下图1-1:每次进入野外场景的时候能看到随机的场景(随机地形、随机怪物、随机藏宝点)。
另外一个知名游戏是手游《列王的纷争(COK)》,在这个游戏里的世界地图,也是程序化生成的,如下图1-2中所示,手机窗口中只能看到这个庞大地图中的一小点,地图上面会有程序随机生成的山脉、湖泊等景观,加上资源点、怪物等等。
还有一个可能是用程序化生成场景技术的游戏中技术点最多的一个游戏,它的名字叫《我的世界》,如图1-3,这个游戏世界是由海量的方块组成的,初步看起来逻辑比较简单,实际上,为了能生成拟真度很高的场景以及能让游戏流畅的跑在各个手机上,需要不少功夫。
我们正在制作一个FPS游戏项目,其中也用到了程序化生成场景的技术,如图1-4,本文的主要内容就是给大家介绍一下这个FPS游戏项目中我们具体是怎么用程序生成迷宫场景以及如何对迷宫场景的显示进行优化。
2.迷宫数据生成算法
首先,项目的策划目标提出了明确的需求,要生成一个比较复杂的迷宫,纵横通道和房间交错的地下场景,每次玩家进入到场景中进行互动的时候,场景都要有不同,但我们能控制场景的规模大小及其他一些自定义的参数。我们实验了几种常用的迷宫生成算法,分别是如下几种:
- Recursive division (递归分割算法)
- Recursive backtracker ( 递归回溯,也是深度优先算法)
- Randomized Prim's algorithm(随机Prim算法)
这几种算法实验的过程比较简单,分别用算法生成随机迷宫,然后在Unity用简单的方式根据数据绘制出整个迷宫,最后让策划去比较选择哪种算法下的迷宫是项目最想要的,本文将逐一介绍一下。
2.1迷宫算法的基本参数
下面的图2-1,展示的是一个规模为30*20的迷宫初始化后的视图,其中白色的格子代表房间,黑色的网格线代表墙,因为还未进行任何后续迷宫算法处理,所以每个房间都是用墙隔开的,都无法通行,本文提到的三种算法,都是在这样一个初始状态下,通过不同的方式把房间打通得到最终的迷宫。
2.2递归分割算法
递归分割算法的效果如图2-2所示:
递归分割算法跟其他两个算法一样,首先是继承了公共父类MazeGenerator,它定义了基本的迷宫数据结构,迷宫规模,还有根据迷宫数据绘制场景的成员函数GenMazeScene其代码如下,其中int [,,]mMazeData这个三维数组是要生成的迷宫数据,前两个维度下标分别表示行和列,第三个维度
然后是递归分割算法的代码,它的主要代码是RecursiveDiv,这是一个递归函数,可以这样理解:把整个迷宫作为一个大房间,每次递归时把当前的房间递归分成4个小房间,并随机打通其中的3个房间,直到这个待分割的房间为一个基本房间单元为止。代码如下
2.3深度优先算法
深度优先算法得到的迷宫场景有一条比较明显的主路,岔路较少且浅,如下图2-3所示:
相对应的核心代码如下,与递归分割算法不同的是,增加了房间栈用以记录访问过的房间(访问先后的路径)。
2.4 Prim算法特点
Prim算法的效果如下图2-4所示,相对前面两个算法来说,岔路较多,最难走。
除了矩形迷宫,还可以用Prim算法实现六边形迷宫,算法思路和四边形迷宫基本一致,下面是六边形迷宫的效果图:
Prim算法的具体实现代码如下,主要思路也是类似于深度优先算法,但不是找一个未访问的房间打通,而是找已访问的房间打通,如果找到死路,不是回到上一个路径点,而是随机在周围待处理房间里随机。
3.项目中的迷宫场景部件制作
FPS项目中的迷宫是在上面数据生成的基础上,由程序组合各种不同迷宫场景部件来完成的,我们先规划出若干的迷宫部件,例如描述4个通路的迷宫部件如图3-1所示:
美术制作完这个部件后,我们把它导入Unity,然后为每个部件创建一个场景,把部件导入到场景中进行灯光设置,然后静态烘焙,计算生成物理碰撞数据,最后按照子场景部件填写场景部件配置表,在程序代码中加载并拼接显示出来。
4.迷宫物理碰撞数据生成
每个迷宫部件的碰撞信息我们是用BVH树来保存的,每个迷宫部件单独生成自己的BVH树,其目的是为了让碰撞检测更高效,下面介绍一下BVH树的算法流程:
- 给场景中需要添加到BVH的物体添加标签
- 遍历这些物体
1把每个物体的三角形获取出来放入集合中
2根据集合构建一个BVH根节点
3开始BVH切分
4Shake(摇树:把未包含三角形的叶节点删除)
BVH树的切分过程如下图4-1所示,从一个根节点开始把场景等分成八个部分,然后把遍历根节点包含的三角形列表,把三角形根据自己的位置存放到各个子节点中,然后递归下去,直到达到最大深度或者没有三角形可以分配为止。
5.迷宫场景资源管理和显示优化
因为迷宫场景是一个大型场景,那么需要考虑流畅性运行的前提下,尽可能的减少内存占用和CPU、GPU占用。
首先场景的静态碰撞数据,因为是帧同步的网络游戏,所以需要全部加载到内存中,便于保证流畅度,其中加载的过程要经历先加载原始的碰撞信息,然后旋转、平移到对应的迷宫场景处的这个过程。
然后是子场景处理,为了保证运行流畅性,还因为每个子场景的数据量并不大,先做一个预加载,避免需要显示子场景的时候产生加载延迟
对于子场景的显示优化,主要思想是尽可能不显示看不见的房间,所以有了以下算法:
- 获得玩家当前所在房间作为当前房间
- 从当前房间出发,向着4个方向遍历,把遍历到的房间加入显示列表
1如果某个方向上遇到墙,则停止遍历
2如果某个方向上遍历层数达到设定最大层,也停止遍历
算法的效果图如5-1所示,只显示出了玩家周围的可见房间以减少显卡负担。
最后是碰撞检测的优化,因为我们针对每个子场景建立了BVH树,那么进行碰撞检测的时候,就能节省比较多的CPU负荷,下面以射线碰撞检测为例展示算法流程
RayCast递归检测
- 从根节点开始先检测射线是否和AABB相交,不相交就返回
- 如果和AABB相交,则查看是否有子节点,没有子节点(叶节点)就检测射线是否和三角形相交
- 如果有子节点,继续递归调用检测函数
6.总结和展望
大型场景生成技术是一个比较宽泛的技术,很多知名游戏都在使用,因为各个类型的游戏特色不同,所采用的具体实现方式和优化手段也有不同,本文介绍了一个FPS迷宫吃鸡游戏项目的实际制作方法和优化手段,包含了迷宫数据的生成,迷宫部件的制作,及迷宫显示的优化。
后续的将会推出其他几种典型游戏的场景生成算法和优化手段相关文章,敬请期待!
查看图片,开启U3D学习之旅
现面向所有技术发烧友,征集优秀原创技术文章,后厂反应堆将为你提供展示平台。
期待你的分享哦!(可添加下图微信投稿和留言)
https://u.wechat.com/MIlAkfuVzj6_bK9Wjsu-ut0 (二维码自动识别)