DrakkenRidge: Building an open-world adventure for mobile VR
Simeon Acker and Cyril Guichard - Garage Collective
GUEST BLOG
Made with Unity, DrakkenRidge 是一款由 Garage Collective 开发的开放世界动作角色扮演游戏,专为移动 VR 设计和优化。在这篇访客博客中,Team 分享了他们如何利用各种设计和性能技术,以 Unity 的实体组件系统作为渲染的骨干。

介绍 DrakkenRidge VR
我们是 Garage Collective,DrakkenRidge 的幕后团队,这是一款为 VR 构建的开放世界动作角色扮演游戏,你可以在地平线上看到的所有点进行访问。扮演一位强大的法师猎人,探索六个制作精美的岛屿——每个岛屿都独一无二,拥有自己的角色、任务、地下城和秘密。
这段旅程将带您从温暖舒适的海滩到令人惊叹的雪峰;从郁郁葱葱的神秘森林到深埋地底的危险峡谷。打造新建物品,战斗数十种精心设计的生物和敌人,解锁新建的魔法流派,让每一次遭遇都成为独特的体验。
以这个范围构建一个游戏,对于一个两人团队来说,可能没有 实体组件系统 是不可能的。在这篇POST中,我们将介绍我们如何使用这些工具来make我们宏大的冒险游戏。
更平滑的游戏Development混合技术
使用ECS来构建游戏有几个巨大的优势,但作为一个小型独立工作室,在决定如何为移动VR设计和工程化DrakkenRidge时,我们也必须权衡其他因素。
使用 ECS When存在两个主要挑战:
- 陡峭的学习曲线:使用 ECS 需要完全从典型的面向对象逻辑中转变范式。
- 将实体系统与传统的 Game Object 系统分离可能会使它们之间的交互变得具有挑战性。
所以我们面临的问题是:
哪些对象和交互应该利用 ECS 的强大功能,哪些对象应该保持为传统的 游戏对象 和 MonoBehaviour?
DrakkenRidge 中的 Entities
DrakkenRidge 由巨大的开放环境组成,给定空间中有数万个物体——在任何给定时间可能有数千个对玩家可见。
将这些渲染为传统的游戏对象需要大量的内存开销,引擎需要评估整个Scene层级并为每个Object计算变换组件。W渲染n渲染,尤其是ON移动端设备上,GPU 会明显吃力。
那么如果我们把这些对象渲染成Entities 怎么样?

作为 Entities,它们避免了与 GameObjects 相关的许多渲染和内存开销,而是使用 Entities Graphicssystem 将它们批处理到内存块中。
此外,DrakkenRidge 艺术管道大量使用了纹理图集技术,使庞大的环境可以使用更少的 Materials(请参阅我们关于纹理图集的博客文章)。
Entities Graphics可以将All具有相同网格和材质的对象组和手柄,然后引擎可以创建紧凑的绘制Calls,一次渲染许多对象。
这可以节省数百个 Batches 和 SetPass 调用。

渲染数千个对象——仅使用八个 SetPass 调用
自定义定义实体系统
DrakkenRidge 利用 ECS 一次性渲染数千个对象。但即使解锁了巨大的性能提升,我们仍然可以ON移动端VR 平台上做更多事情来改善用户体验。
距离作者System
我们为 DrakkenRidge Entities 创建的第一个功能是距离编写系统。它会帐户各种变量来确定一个ObjectWhen应该对Player隐藏或可见。
因为Player的Vision在Open世界中延伸得非常远,成千上万的远处的物体可能同时进入View。该System决定了其中哪些应该保持突出显示,哪些应该淡化背景。环境效果,例如距离雾,用于覆盖过渡,使远处的物体淡出视野。

带有自定义义值的距离构建组件用于运行时时实体剔除
每个实体都会根据各种参数在 in-Editor 中生成唯一的剔除值。然后,该数据在实体创建时间时传递给系统,这些系统使用该预先计算的数据来做出关于何时渲染或剔除实体的运行时决策。
自定义实体组
使用 DrakkenRidge 距离作者系统,实体可以被分配到具有共享决策处理的组中,从而允许例如,根据各种游戏内因素快速渲染或剔除城市的一部分,而不是让城市中的每个实体单独决定。

具有共享Decision处理的群组Entities
实体 LOD
DrakkenRidge 距离作者系统 的第三个功能允许在飞行中将实体替换为渲染成本更低的另一个实体。这项技术最初是为解决更昂贵的着色器和 Materials 的问题而编写的。
尽管DrakkenRidge中的环境模型已经高度优化,但一些着色器会增加明显的开销。该 LOD (Level of Detail) 实体系统仅在必要时才允许渲染“具有更高成本着色器的实体版本”。
/// If using a simplified LOD object, see if we are in its range
if (inDistanceReturn.useLOD)
{
if (inDistanceReturn.isInDistanceLOD)
{
ecbParallel.SetEnabled(unfilteredChunkIndex, inDistanceReturn.lodEntity, true);
if (ChildLookup.TryGetBuffer(inDistanceReturn.lodEntity, out lodChildBufferData))
{
var newParent = inDistanceReturn.lodEntity;
SetEnableChildren(
entityCanBeReEnabledComponentLookup: entityCanBeReEnabledComponentLookup,
unfilteredChunkIndex: unfilteredChunkIndex,
parentEntity: newParent,
enabled: true,
childBufferData: lodChildBufferData,
ChildLookup: ChildLookup,
ecbParallel: ecbParallel);
}When满足条件时,对实体使用简化的 LOD。
CPU 优化
DrakkenRidge 的距离作者计算通过爆发编译的作业运行,以获得最大的 CPU 性能。
Unity Jobs 是可以作为异步任务在背景中运行的函数,而 Burst Compiler 通过将其翻译成高度优化的原生机器代码来加速 CPU 密集型任务。
[BurstCompile]
public partial struct ProcessBufferJob : IJobChunk
{
internal EntityCommandBuffer.ParallelWriter ecbParallel;
public EntityTypeHandle entityTypeHandle;
[ReadOnly] public BufferTypeHandle<Child> childTypeHandle;
[ReadOnly] public ComponentTypeHandle<DistanceCompData> distanceCompDataTypeHandle;
// A BufferLookup for random access to Child buffers.
[ReadOnly] public BufferLookup<Child> childLookup;
[ReadOnly] public ComponentLookup<Disabled> disabledComponentLookup;
public PlayerDesignatorCompData targetDesignator;
public HelperStructToUpdateEnabledEntities addToBufferStruct;
public Entity playerEnt;
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
{
var entities = chunk.GetNativeArray(entityTypeHandle);
var childAccessor = chunk.GetBufferAccessor(ref childTypeHandle);
var distanceCompData = chunk.GetNativeArray(ref distanceCompDataTypeHandle);
bool hasChildBuffer = chunk.Has(ref childTypeHandle);
DynamicBuffer<Child> lodChildBufferData;
for (int i = 0; i < chunk.Count; i++)
{
Entity currentEntity = entities[i];Tip:动态缓冲区的一个优化是将其转换为 NativeArray 以便迭代:“var lodChildNativeArray = lodChildBufferData.AsNativeArray();”
DrakkenRidge 中的游戏对象和 Physics
在DrakkenRidge中,90%的世界使用ECS进行渲染,它在背景中运行自己的任务,以保持可见的环境处于美观和优化的状态。
但对于我们只有一名程序员和一名艺术家的小型DevelopmentTeam来说,我们Decision使用传统的游戏对象和MonoBehavior来版本我们许多关闭距离的VR交互。这在一个快速测试和频繁设计周期的 Environment 中允许了更快的迭代时间。
Physics相互作用与战斗
DrakkenRidge 中的 VR 交互在很大程度上由物理模拟驱动。这包括从抓取和操作物体,到 Player 攀爬和跑酷,再到施法和近战战斗的一切。
玩家可以与基于 Physics 的攀爬挑战互动。
在Open世界中runningPhysics模拟
DrakkenRidge 中的战斗由一个系统驱动,该系统将独特的动画与每个关节的物理行为相结合。每个AI决定如何根据情况让他们的Death感觉具有反应性和回报性。
将每关节的物理效果与脚本动画相结合,以构建独特的反应
DrakkenRidge 中的许多物品和法术允许 Player 一次性对多个敌人造成伤害,通常会导致同时对许多角色骨架运行 Physics 模拟。
将爆炸Physics学应用于AI组
Physics优化
实时物理模拟可能成本很高,尤其是在移动平台上。为了减轻这个问题,世界中的每个PhysicsObject都会轨道Player的位置,以及Player是否处于可以与模拟交互的位置。
例如,Player 可以攀爬由受 Physics 驱动的链节构建的长链,这些链节会相互以及与周围的世界互动。这些物理碰撞体、关节和刚体是根据 Player 是否直接操作或在 View 中看到该链而动态启用/禁用。
General来Rule,我们渲染远处的物体,但只允许与附近的物体进行Physics交互。在一个VR游戏中,当Action发生在关闭距离时,这符合Player的预期。
进一步优化
When优优化移动端VR渲染,我们考虑了每个Environment的构建方式,并选择了最符合我们需求的解决方案。
除了ECS之外,我们还考虑了各种技术,包括LOD和替身。
游戏对象 LOD
渲染带有LOD的游戏对象包括为给定对象设置多个版本,以便在距离Player不同时渲染。例如,一个高度详细的房屋模型被替换为一个非常简化的低多边形版本,当玩家离物体较远时可以节省渲染时间。
在DrakkenRidge中,每个Object都已采用高度优化的低多边形复古艺术样式进行建模,并且世界的大部分是通过ECS处理的,因此传统的3D LOD不会提供任何实际优势。
然而,我们创建的距离作者System 是为了在必要时处理远距离 Entities 的 LOD 渲染而构建的。
冒名顶替者
一个冒名顶替系System 3D 游戏对象 转换为 2D 看板,WhenPlayer离它们距离时。这最适用于茂密、遥远的背景物体,例如森林。DrakkenRidge 在某些情况下会使用冒名顶替者。

正确实现的替身与它们的3D对应物无法区分。
在DrakkenRidge中,我们测试了各种优化技术,并仔细选择要{implement}的。Unity ECS 迄今为止提供了最大的性能提升,以远低于使用传统游戏对象管道的成本渲染了我们的整个世界。
结束语
对于DrakkenRidge,我们结合的设计和优化技术被证明是完美的配方,使我们能够构建一个独特的VR冒险。Open世界由 Unity 的 ECS 驱动,而我们的关闭距离交互则采用熟悉的 游戏对象 和Physics工作流程进行设计,使Team能够快速设计、测试和迭代。
移动VR的开放世界RPG不必遥不可及,我们期待看到更多开发者迎接这场冒险!

DrakkenRidge VR现已发布。在我们的 Steam Curator Page 上探索更多 Made with Unity 游戏,并查看 Unity 开发者在 Unity Blog 和 Resource Hub 上的更多故事。