Dungeon Architect插件介绍
Dungeon Architect是一个unity引擎上用于流水线产出场景关卡的插件。它让你可以通过设置参数来自动或手动的快速创建关卡。这个文档就是来介绍Dungeon Architect插件的各种特性。
概述
一个关卡的生成份为三个阶段
- 元件生成
- 布局生成
3,图形生成
元件生成
Marker元件作为根节点,下面附属各类图形化Visual元件。而这些Visual元件可以关联别的Marker元件,以及设置这些marker出现的几率。
通过图形化的编辑器界面生成的theme文件保存了这些编辑信息,实际上这个theme文件就是一个序列化文件,也可以被转换成别的文件格式。
布局生成
在这个阶段,仅仅只是将关卡的布局在内存中生成,并未创建任何模型或者对象。
接下来,根据布局中的叫做Marker的数据点生成关卡。一个marker数据点在3D空间中只有一个名字和一个Transform关系。
场景生成
这个阶段会生成实际对象,根据布局和元件,组合生成实际场景。
执行过程中,首先遍历布局中的每一个点,根据这个点,在theme文件中找到对应的marker,然后拿marker中附属的模型图形资源替换到这个点上来。
同一个布局,如果使用不同的theme文件,可以生成布局一样但是风格完全不同的场景。使用相同的theme文件,但是使用不同的布局,则可以生成风格一致但是布局不同的场景。
元件生成
Theme概述
Theme文件用来定义你的元件。其本质是一个序列化文件,在本插件中,保存为一个Asset文件。编辑界面如下:
创建theme文件
在Project窗口上的create的按键,依次选择,则创建出了一个默认theme文件。
编辑theme文件
入口
双击theme文件,或者选中一个theme文件后在unity菜单栏选择window的下拉框中选择Dungeon Architect。就可以打开theme文件的编辑界面。
默认的theme文件中,只有几个默认的Marker元件。
Theme元件概述
Theme元件分为三类
1, Marker Nodes
2, Visual Nodes
3, Marker Emitter Nodes
关系如下
w
Marker作为元件的单位,其下面有附属的Visual元件。而Visual元件又可以挂载Emitter元件。实际上Emitter元件是作为另外一个Marker元件的指针。
当一个Marker元件被实例化,那么其所附属的Visual元件也会被实例化。当这个Visual元件被实例化时,如果它下面挂载有Emitter元件,则根据Emitter上的权重比率来决定是否要在Visual下实例化一个Emitter指向的Marker元件。如此循环。
例如墙壁是一个Marker元件,其附属了墙和柱子两个Visual元件。而墙Visual元件又挂载了一个Emitter元件指向窗户Marker元件。并且概率为50%。那么在实例化这个墙壁Marker元件时,其墙面上有50%的几率出现一个窗户Marker元件。而这个窗户Marker元件下的Visual元件又可以指向窗帘,花盆等其他Marker元件。
Marker元件
在场景布局完成后,布局中的每个格子会去theme文件中找对应的Marker元件实例化到格子中(根据名字来匹配)。Marker元件作为一种根节点元件,其下面可以包括各种其他元件。
每个theme文件在创建会有若干种默认存在的Marker元件。你也可以自定义添加Marker元件,在theme编辑界面右键出现如下功能菜单,选择Add Marker Node即可。
Visual元件
Visual元件代表实际的美术资源,其中可以定义模型,贴图,图片等资源引用,以及这些资源的相对坐标,朝向,大小等Transform数据。它必须挂载在Marker元件之下,不能作为根节点元件存在。
Visual元件分为两种,一种是Game Object Node,一种是Sprite元件。使用哪种取决于你做的是3D游戏还是2D游戏。
直接在unity编辑器中拖拽Prefab到theme编辑界面即可创建Visual元件。除此之外,也可以在theme编辑界面中右键Marker元件来生成新的Visual元件。Visual元件中的美术资源可以为空,单纯用来作为Emitter元件的父节点存在。
创建好空的Visual元件后,可以重新定义里面是否包含以及包含什么美术资源
创建好Visual元件后,需要与Marker元件关联起来,否则毫无用处。直接从Marker元件拖拽可以拉出关联箭头,将两者关联即可。
Emitter元件
Emitter元件实际上只是一个指针,指向另外一个Marker元件。其意义就是某个Visual元件下需要根据一定规则出现其他的Marker元件。这类似于Game Object之间的层层嵌套。
如图,墙壁Marker元件定义了三种Visual墙壁。其中带窗的Visual墙,则指向了窗帘的Marker元件。
直接右键Visual元件可以打开创建Emitter元件的菜单。
这个菜单中会自动包含当前theme文件中所定义的所有marker元件类型。
注意:maker – visual – marker的引用中,禁止出现死循环。例如,墙壁 – 窗帘 – 墙壁 ,但是可以是墙壁 – 窗帘 – 花盆- 洒水壶。
Theme Node的参数配置
在theme的编辑界面中,选中元件,在Inspect窗口中有参数配置的界面。
Template:所引用的美术资源预制。
Is Static:实例化时,标记该Game Object资源的Static tag。这主要是影响到渲染批处理。如果是活动对象,就不要勾选。
Position:相对于Marker元件的坐标。
Rotation:相对于Marker元件的旋转。
Scale:相对于Marker元件的缩放。
Probability:这个Visual元件在Marker元件下出现的概率。1为100%。
Consume On Attach: 是否互斥Marker元件下的其他Visual元件。如果是,并且该Visual元件实例化出来了,那么其他的Visual原件就不会去实例化了。
Selection Rule:这里允许根据基类的接口编写一个逻辑判断是否要生成该Visual元件,这个逻辑会取代Probability中的概率属性。
你可以编写一个派生自AlternateSelectionRule.CS
类的脚本(在DungeonArchitect
命名空间之下),来定义产出规则。
范例1:
在墙壁下概率产出桌子。但是门口旁边的墙产出桌子的话会挡住行走路线。因此需要重新定义桌子的产出规则。
可以编写一个脚本,判断是否靠近门,如果是的就返回false不要生成桌子。
范例2:
在暗黑这类固定45度视角的游戏中,我们会希望阻挡摄像机视线的墙壁就不要刷出来了,改而刷一个不会阻挡视线的栏杆。那么可以编写一个脚本,根据元件的角度来判断墙壁和栏杆是否要生成。
首先在Wall Marker元件下分别生成墙和栏杆这两个互斥的Visual元件。
给其中的墙编写一个产出规则。
范例3:
这个例子中,塔楼的分布过于密集和单一。
希望达到的效果是分散而且不单一。
那么Marker元件的设计如下
塔楼的Visual元件是非互斥,但是指向的Marker塔楼下的资源是互斥的。再针对塔楼的Visual元件编写产出规则:
Grid Position为偶数才出现,这样就实现了间隔。
如果你需要更复杂的逻辑也可以实现。
Transform Rule: 顾名思义,针对Transform数据,你需要通过一定逻辑来决定。那么可以编写一个派生自TransformationRule.CS
的类(在DungeonArchitect
命名空间之下),来自定义空间规则。
范例1:
在这个例子中,石头的摆放太过单一,显得不自然。我们希望的效果是变得自然。
先制作一个随机出现各类石头的Marker元件。 然后制作一个随机石头Marker,其Visual指向石头Marker。 针对该Visual编写Transform规则。
范例2:
在上面这个例子中,虽然石头实现了不规则的自然分布,但是如果没有跟地形关联的话,会出现浮空的问题。
所以可以在这个脚本中加入对地形读取,以决定物件的摆放。
范例3:
在这个例子中,地板需要出现一种即将毁灭的感觉。所以针对地板元件的朝向进行随机。
范例代码
布局生成
自动布局模式
入口
下面这些预制组件是用来创建你的关卡的工具。拖拽预制到场景中,重置它的Transform。
这个预制组件可以根据你设置的参数自动化生成一个关卡布局。生成布局后,根据该布局和所引用的theme文件中的元件生成模型,灯光等。(theme文件需要预先自行编辑,然后手动拖拽到预制上进行引用关联)。在theme文件的编辑中,你可以自定义哪些美术元素需要附属到地板,墙壁这些marker元件上。
属性
选中预制可以在Inspector窗口中看到它的属性,设置这些属性将决定程序怎么样来生成你的关卡。
Build Dungeon按钮 点击它将会生成关卡,但是要求至少有一个theme文件被引用到。
Destroy Dungeon按钮 点击它会销毁当前已经生成的关卡,如果你仅仅只是修改了theme文件希望重新build,可以直接点build dungeon按钮,不需要点这个。
Themes引用列表 这个列表可以引用多个theme文件,theme文件里面定义了你的各种marker元素引用的是哪些美术资源。在build关卡前,你至少引用一个有效的theme文件。
Debug Draw 勾选以决定是否在Scene窗口绘制场景中的一些关键信息。
配置参数
这些配置参数决定了程序如何来生成布局,如果你什么都不改,那么生成的默认布局时插件作者写的布局。
参数解释如下:
Seed:修改这个参数将会完全修改关卡的布局,可以将其视作一个随机种子数。旁边的R按钮可以自动生成一个随机数来作为随机种子。
Num Cells:顾名思义,这个参数用来决定关卡的”格子”数量。数字越大,则场景越大,反之亦然。
Grid Cell Size:每个“格子”的尺寸。关卡的产出是基于这样一个表格系统,每个格子将有theme文件里面设计的元件填充。这是一个非常重要的参数,需要根据项目中的美术资源元件大小来决定这个参数的大小。
Min/Max Cell Size:这是每个“格子”允许的浮动大小范围。当一个“格子”转换成房间,走廊或者隐藏时,这个格子的宽高将在这个范围内随机选取。
Room Area Threshold:如果一个“格子”的大小超过了这个设置,这个“格子”将会升级成一个房间。升级成房间后,将会与其他房间或者走廊连接,不论是直接还是飞直接。参考后面会讲到的生成树结构。
Room Aspect Delta:房间的宽高比,当这个值越接近0时,创建出来的房间越接近正方形,越接近1时,则越狭长。
Corridor Padding:走廊一侧的额外宽度。
Corridor Padding Double Sided:这个额外宽度是否用于走廊两侧。
Height Variation Probability:通过调整这个值来修改你关卡中的高度变化。值越接近于0则场景越接近平地,越接近1则反之。一般来说,0.2至0.4是比较合适的。
Max Allowed Stair Height:最大的阶梯数。这个数代表着允许你的关卡拥有多少层阶梯,最高只能允许2层楼。根据你的阶梯的资源高度来决定这个值。在demo中,楼梯资源一个是200 units(一层楼阶梯),一个是400units(二层楼阶梯),因此这个值设置成2.
Spanning Tree Loop:决定你的关卡中有多少个回路。这个值越接近于0,则你的关卡越接近于单线,越接近1则回路越多。不建议有过多回路,0.2是一个不错的值。
Stair Connection Tolerance:生成关卡的过程中,会添加阶梯来衔接过渡不同区块。但是我们不希望阶梯太过密集,因此在添加衔接过渡阶梯之前,将会检查这个区块是否已经衔接过阶梯。如果是的,那将不再添加阶梯。这个值就是用来设置这个检查区域的范围。这个值过大过小都不合适,具体还是在测试中去把握。
Normal Mean/Std:随机种子生成器的参数。这个值越大,则随机种子越小,这个值越小,则随机种子越大。进一步影响到随机场景。
Initial Room Radius:内部使用,保持一个较小的值比如10-15。
手动布局模式
手动绘制布局
Dungeon Architect也允许用户进行手动绘制关卡布局。
在预制组件中选中PaintMode,在Scene窗口中会显示出关卡布局。
你可以执行的操作有:
鼠标左键单击:绘制格子
Shift+鼠标左键单击:删除刚才绘制的格子
鼠标滚轮:调整准备要绘制的格子的高度
选中Paint Mode,可以在Inspector界面中看到下列参数
当你希望整个关卡完全手动绘制,不希望自动布局有任何的介入,则可以在Dungeon Grid预制上的Num Cells参数中填入0。意味着自动布局分配的格子数量为0。
手动区域修改
入口
在Assets/DungeonArchtitect/Prefabs
路径下有若干种volume可供选择每种都有各自的特性。将其拖拽到Scene中即可以开始进行编辑。
Platform Volume
Platform Volume可以放置在场景的任何位置,插件会针对放置的Platform Volume
生成一个platform(房间或者走廊)在布局中。并且大小也会同步,但是朝向不能自定义。
在Inspector界面中,你可以自定义它是一个房间还是一个走廊。
选择走廊类型,放置在场景中时,它可以独立存在,也可以对已有的布局进行自动融合,或者阶梯衔接。
选择房间类型,则也会自动融合或衔接。只是衔接的方式跟走廊会有区别。(有墙,会针对衔接的走廊生成门等)
Theme Override Volume
顾名思义,这是一个覆盖体。它可以把场景中原有的美术资源替换成这个。
在其参数中,可以选择使用哪个一个Theme文件中的元件以及目标关卡。
Negation Volume
这个可以简单理解成一个橡皮擦。放置的区域将会移除原有的元件。
场景生成
地板生成
这里所说的地板只是渲染层面的场景底板。首先在unity的菜单中生成一个默认terrain。调整其位置,使我们的关卡在terrain中间。
在Assets\DungeonArchitect\Scripts\Builders\Grid\Landscape路径下,找到LandscapeTransformerGrid.cs脚本,挂载到Dungeon Grid预制上。
将之前生成的默认terrain引用到脚本中。然后可以设置各项参数。
参数说明:
Terrain:引用的地形。我们使用生成的默认地形即可。
Textures:可以设置terrain的各类纹理。不同类型的纹理映射的位置不一样。如下图示范。
Ground Level Height:设置terrain的初始默认高度。
Layout Level Offset:如果设置为0,则terrain的高度跟关卡的地面是一个高度。一般来说,建议低于关卡高度,只作为一个背景板存在。
如图中所示,如果为0,则地板的纹理穿透了元件的纹理。
Room Elevation Curve: 定义了房间周围的地板衔接的坡度。
Corridor Elevation Curve : 定义了走廊周围的地板衔接的坡度。
Smoothing Distance : 房间或者走廊的衔接坡的范围。
Room Blur Distance / Corridor Blur Distance : 当地板作为可行走区域渲染时候,路径和非路径的纹理侵入范围。
自定义元件实例化
在场景生成过程中,会根据布局和theme文件自动实例化元件。但是我们也可以通过编写脚本的方式,自定义额外的元件实例化逻辑。
首先,建立一个派生自DungeonMarkerEmitter.CS
的脚本。挂载到Dungeon Grid预制上,在里面可以自定义额外的元件生成逻辑。参考MarkerEmitterEmptySpace.cs和MarkerEmitterFindLowestPoint.cs两个例子。分别在空地和最低处生成指定名称的元件。
当然,前提是在theme文件中还是需要先定义好元件。
导航网格生成
插件支持运行时生成导航网格(不支持UNITY5),但是在生成时需移走活动对象,比如NPC。
如果要在运行时生成导航网格,需要将DungeonNavigation预制拖拽到场景中。
选中预制,可以在Inspector界面中看到下列参数和选项:
生成Nav mesh依靠的是Triangle Provider组件。这里有许多种Triangle Provider组件,可以自行选择,也可以另外自己编写:
Collision Triangle Provider:根据当前场景中的具备碰撞逻辑的模型来生成导航网格。
Layout Floor Triangle Provider: 根据当前场景布局的可行走区域来生成导航网格。需要引用Dungeon Grid预制作为目标。
Static Mesh Triangle Provider: 使用所有元件中的模型的定点和索引作为来源生成导航网格,这样会比较慢,但是是全地形的网格。
Terrain Triangle Provider: 使用terrain的顶点和索引来作为来源生成导航网格。
点击Build Nav Mesh即可生成导航网格。
参数解析:
Cell Size: 控制导航网格的精细程度,这个值会影响到生成速度。一般来说0.2到0.3是比较合适的区间。
Agent Height: 导航的最高高度。
Agent Radius: 导航的最大半径。
Agent Climb Agents:导航可以攀登的最大高度,高度低于该值的物体将不被视为障碍物。因为导航可以爬过它们。
Max Crowd Agents:同时可以支持的导航AI数量。
元件也可以影响导航网格的生成,如果你希望如此的话。在编辑元件的时候,给nodes设置Affects Navigation flag。然后在编辑界面就可以看到选项,不过与此同时也必须把这个元件标记为静态的,即把Is Static勾选。
最终生成
插件提供了默认的关卡生成器,但是你也可以提供自己的实现来替换它。实际上,我们应用到游戏客户端时,这一步几乎必定是需要自己来编写。默认的生成器只能用作参考。
创建自己的生成器脚本,需要派生自DungeonBuilder.CS
类。