爆款的诞生:《胡闹厨房2》的多人游戏模式解决方案

2019-05-13
5月11日,由Unity主办的Unite Shanghai 2019开发者大会上,Team17技术总监David Smethurst以“爆款的诞生:《胡闹厨房2》的多人游戏模式解决方案”为主题做分享了增加多人游戏模式在开发过程中遇到的问题和解决方案。以下是经GameRes游资网整理的演讲实录:


David Smethurst:我叫David,我是Team17的总监,我是负责编程内部的工作,与我们的合作伙伴合作。我的演讲是有关于《胡闹厨房2》的一些内容。

最早《胡闹厨房》是由Ghost Town Games开发的,这是一款多人合作烹饪的益智类游戏,我们大概可以让4位玩家同时在一个游戏中多个步骤准备烹饪。这个会比较有意思,它也获得了很多的奖项。游戏本身是非常成功的,但是我们的玩家对它有很多的期待,Ghost Town发了两个DLC,但是仍然觉得不够。所以接下来在续集中我们想要做什么呢?

这是续集中Team17所做的工作,我们对续集很感兴趣,我们希望和Ghost合作,希望提供一些功能集,让更多的人参与游戏。让玩家可以在世界各地通过网络连接来合作,但是他们仍然可以进行本地游戏。多人在线互动它是游戏核心的功能,当然,我们希望在关卡中设置更多丰富的内容,而且有一些人已经在不同的地区和分解的关卡中有不同的进度,但是我们还想要做得多一些,所以我们在关底设置了动态“英雄”的关卡。


气球的关卡是游戏设计的第一个原型关卡。在飞行的过程中,玩家的角色就是这些厨师,他们是放在篮子里,随着时间时间的推移,暴风雨进来,绳子被卡住,篮子落到地面,布局和需要制作的食谱也发生改变。这是《胡闹厨房》Team17所做的工作,我们加入更多的内容,也做了更多的设计,我们将玩家相互分开,你需要在这些环境之中来找到切分的可能。比如说我们引入阻隔和间隙,让我们在准备区和烹饪区分离之前进行食材的传递,这里面我们也是设置了时间的限制,包括一些新的厨具和一些可以投掷的道具,一些物品我们也进行了迭代。所以我们可以通过各种挑战的设置,来达成我们的目标。


我们还需要跨越不同的边界,运送食品到另外的一部分,或者是让玩家自己来计划他的一个方案。穿过两个部分之间的间隔,然后回到自己的区域,当然,我们还需要设置更多的食谱,所以我们加入了像寿司、汉堡、披萨、新的工具还有烤炉等,地图也有变化。为什么要这样做,或者是为什么这样做续集,因为游戏本身是受欢迎的,它很有可能是具有很好的前景,他已经建立好了自己的品牌,所以他会取得更大的成功,而不是因为之前的成功自满而断送了机会。

这些是Team17之前所做的工作,《Worms》我们开发了21个版本,我们可以看看Alien Breed,还有《胡闹厨房》的人员,我们完成了原版新功能的添加。而且我们将它做得非常成功,这当中,团队的整个成员都参与到了这当中。


我们实施的一部分,首先要解决风险最高的部分,就是联网。我们先是从原始代码库开始,希望能够保持游戏本身的机制和感觉,但是我们需要增加多人在线,而不会损坏这样的感觉,所以我们是非常需要去有网络方面插件的增强,我们可以使用Unity或者是完全重新编写,因为考虑维持到原版的机制,重新编写不太可行,所以我们来看看第三方插件,这里有几个选择:Photon、Ulink、Forge和Darkrift,这几个插件要考虑到成本,有些是云平台的付费,我们是多平台发布,要保证兼容,所以插件使用要考虑到重要的因素,就是你要对原有的代码进行兼容,如果是有过多的设置或者是让游戏更加复杂,那我们的开发时间受到更大的影响。另外是大量的开发人员在解决方案上是可以帮助我们将它设置到最好,另外Unity有一个高级的API,就是HLAPI,通过它进行网络连接同时也进行很多工作的同步,包括变量的协调。新的代码库也有大量的工作要做。接下来就是传输层,而且还要考虑到像平台的套接层,以及比较低层级平台上的表现。这是在Unity中可用的传输层之上的网络层,我们需要完全控制,并且决定每一个平台上的具体细节,而我们内部的程序员也在开发这样的项目,同时我们也会培训其他的成员来提供内部的支持,来共同达成我们的技术需求。

现在,我们来看看网络拓扑。因为这部分是在网络构建中非常重要,我们需要了解它决定了游戏何时是需要去进行通讯和协作的,还有就是我们的数据以及带宽方面的限制,我们采用的云托管服务器需要非常小的带宽,因为客户包来回传递这会导致成本非常高,每一次都会因为新加入的玩家来遇到更大的带宽要求。另外也要保证所有的游戏端都要可以充当服务端,每个端都有对象,在所有点都有所有权,然后在单点保证信息的传递,因为它是多向的,非常混乱。所以我们采用cs结构的网络拓扑的形式,然后对客户包进行调整,它是直接适用于游戏当中的物理对象,我们选择拓扑结构是因为他们适合我们的需求。这也决定了我们使用原始的游戏的代码,就可以保持原有的感觉,这对我们来说很有意义,可以进行不同的模拟。另外是代码在不同服务器上的相互移动,也可以更加高效。

最后是更高层级的网络上,通过Unity在传输层上传输自己的解决方案,我们有一种办法让原本的脚本在网络环境下工具,而不需要对这些脚本进行破坏性的修改。我们使用增加额外的组件的方式来进行扩展。这也意味着对每一个原始的组件我们做出三个版本,一个是服务端,第二个是客户端,第三个是原始功能。我们的服务器使用全部三个版本,其他的端是使用两个版本。当我们使用Unity时,我们为游戏当中的每一个对象添加网络组件,以便提供唯一的网络标识。我们还提供了客户端版本,如果将现有组件加入当中,这些游戏已经预先应用到了厨房设计的预制件,无论游戏对象如何使用游戏的脚本,我们都希望网络代码在所有的层级当中顺利移动,这也意味着说,我们会通过在加载关卡时对场景进行扫描的方式,来增加网络组件。我们迭代场景中的所有对象,对于每一个对象,我们都会添加server组件或是client组件。这就是无论我们如何捕获这些实例,无须检查每一个资源,也不用告诉设计师和美工,如何在我们的GameObject上做出修改才能联网。之后我们做《胡闹厨房》续集时,从第一天我们希望游戏保持这一状态。现在这一游戏仍然是可以在单机平台上运行,同时也可以支持多人在线游戏,同时我们的功能在开发时进行网络测试,在完整的烹饪管道中能够足够地去完成它的工作,而避免说在实际的平台上运行不了的情况。


这是我们新的网络层,现在通过扫描和设置场景中预先存在的对象添加多人支持,同时也需要在关卡中生成新的对象,因此创建新对象的级别中是可以生成一些预制的游戏对象,列表来完成目标,同时通过网络生成新对象引用列表。听起来很简单,但是需要确保所有的客户端,在所有的衍生对象上都能保持同样的ID,这样就意味着说我们的网络、数据的路线非常重要。当游戏运行时,接下来就要进行网络的验证,我们必须重新编写所有的代码,我们首先是要支持移动玩家的平台,同时我们还需要支持各种不同的物理对象。比如说像有些物品的掉落,比如说移动,还有玩家在厨房当中扔食材等。我们建立了一个系统,客户将他们的输入发送到服务器,并且进行整个模拟,向周围传输结果。这样的话,我们就有可能面对一个滞后的问题,在等待服务器物理结果传输回来,我们还允许他们在服务器上直接移动,以响应本地的输入。唯一的一个问题是,物体放在游戏环境中他们会移动或者是旋转,因为他们没办法平滑地在平台上相互移动。为了解决这一问题,我们在所有的物体上都是通过统一平台,来防止物体旋转滑过的时候发生误差,而且厨师也会在他们的平台上移动食材,如果不能让厨师和食材移动的时候,就会出现情况。我们可以检测厨始和空间的定位,将厨师角色和物体同时移动,在这当中进行了大量的迭代和测试,特别是低带宽情况下,游戏的玩家也没有发现误差,但是仍然出现一个问题,两个玩家相聚比较晚就会出现一些解决不了的延时的问题。


接下来看看网络的功能,性能我们希望保持稳定,尤其是帧率的稳定,玩家玩游戏希望流畅的体验,而网络的速度是比较稳定的。我们有很多带宽,但是我们要保证数据传输量不要太大。那我们原始的游戏是将各个预制件水平拼凑在一起,所有的站点都是单独的对象。那么在这里,我们需要大量的绘制调用,因此,运行的性能是比较差的,为了能够帮助美工建立了一个地板编辑器,就是说可以在场景的视图中编辑地板的位置,这样我们可以做计算,我们可以进行可视化的处理。那么我们也确实是传递给美术,我们必须在Maya中创建整个对象的FBX的文件,然后导入到Unity中。而不是在编辑器中编辑,并且将对象简单地组合在一起。

但是为了解决我们在绘制耗时,我们依然需要做效率上的提升。比如说我们的美工他们需要更多的纹理和共享材料,我们会由渲染器进行合批,另外为了在满足美术的要求的同时,达到能在最差的平台运行的标准,我们选择了配合ForwardRendering使用烘焙光照。为了辅助烘焙过程,我们会有专门的工具在专用的机器上运行。另外我们是有一个编辑器工具,他们是强制在预制的物体上应用正确的设置。比如说它强制设置MotionVectors选项为”Camera motion only”,以防止的TAA中的额外DrawCall开销。我们要求所有的美工关闭静态物体的Light probe功能以防止合批被破坏。我们还要求美术对所有不对场景产生明显画面影响的物体关闭阴影,这不会影响主要的操作区域的视觉质量。并且我们进行定期Profile分析,来确定瓶颈在什么地方,并且我们也要确定未来一个开发更好的改善机会是怎样的。

我们在使用Unity的Profiler的时候,也会去进行特定平台的Profiler的研究,在进行游戏的时候,我们还要去进行关卡和关卡性能的检查,那我们设计了简单的性能、跟踪的系统,这个系统在一个关卡中对FPS进行采样,关卡结束向玩家(这里指的是QA)汇报统计的数据,这是分不同的帧数范围来进行汇报,还有帧数波动等等。同时我们也会进行质量的检查,另外我们还会收集标准差,帮助我们评估帧速率的检查。有时候这些测试可以自动化地进行,而且可以非常快,容易规划,另外就是在夜间可以完成,这样的话,很容易去帮助我们提交数据。在这部分,目前一切进行很正常,但是实际上我们仍然遇到困难,比如说项目结束时发现特定关卡的CPU的性能出现在渲染问题上,对这些问题我们确认以后很明显是DrawCall瓶颈,部分物体还是基于Prefab拼接,而不是在Maya中作为一个整体制作。在Unity批处理材料时,没办法在低端机上绘制足够多的DrawCall。之后我们做了很多美工方面的工作,因为我们的内存资源是丰富的,我们选择将相同的材质的对象合批,这样我们就可以减少不需要资源的浪费。

在可能的情况下,我们还去进行了网格的调整,尽可能合并和减少资源的调用,在平台的低端平台上运行时依然还是会面对很大的挑战。因为我们内容的创作者它想要实现更多的东西、更多的内容和更多的光照效果和细节。所以我们必须在统一的渲染管线中实现操作。所以在这里很重要的一点就是渲染功能,因为我们低端控制没有大量的内存,帧速率降低,而且我们考虑效果的时候是需要压缩的,一开始进行的测试它是采用Forward渲染的,通知渲染了除太阳以外的所有灯光,最初在照明方面我们取得了很大的进步,包括深度缓冲区等。另外我们还必须在后期效果上进行验证,就是使用Depth+Normal,最初使用这些工具降低了成本,但是仍然是有效率和时间的问题。然后我们最后的结果就是包括大量的绑定阴影,虽然说屏幕空间的阴影对于我们的阴影效果非常有用,但是它是比较花时间的。尤其是在这一过程中,我们是进行了一些常规的阴影贴图。那么我们所选择的是在这个工具箱中的阴影,阴影的分辨率非常高,我们采样一次shadowmap,通过双线性插值时阴影变得柔和,来保证视觉效果相同。


最后一个问题是后处理,最初花费了10毫秒,降低Quality设置,在每一个像素上做更少的读入和其他的优化,会让整个的速度都会更好一点,但是很明显,我们需要一个新的解决方案。同时你可以看到SSAO是唯一的一个后处理管线当中需要法线的部分,所以如果我们可以去掉这个步骤,我们就可以简化pre-pass。我看评估了volumetricobscurance方案,一种极端简化的SSAO技术,是非常方便而且容易实现的SSAO技术,它由CryTek针对XBOX360和PS3开发,这样的一种方法解决了我们高样本的需求,同时使得定制的发生器的输出,只需要4个样本就可以获得非常好的结果。那TiltShift也是经常使用的,经常5毫秒以上,最低质量的设置中也是如此,也是要优化它,不然我们的美工也是无法控制和使用的。他们只需要在底部和顶部使用这一技术,在我们的摄像机下,也就是最近和最远的区域,我们采用最新颖的方法,因为我们选择不将它全部变成全屏后处理效果,相反地我们渲染了两个四边形,一个在底部一个在顶部,其效果的覆盖40%屏幕的区域。进一步的优化是,其实我们并没有实现一个正确的景深效果,我们研究了一些2000年初的技术,你只要在模糊纹理和清晰纹理之间进行双线性插值,就可以得到一个从模糊到清晰的景深过度。最后一个改进是把postprocessing stack中的FXAA的文件拿出来修改,强制它使用主机的code path。默认的实现是使用最高质量的实现。这个修改必须在源代码中进行,因为这些接口没有暴露出来,也不会受任何Quality设置的影响。

刚才讲到了美工的原则,就是减少网格的复杂度以及合并DrawCall。另外我们区分了渲染用的网格以及碰撞网格,主要是为了减少碰撞耗时。我们在网络层其中的增加的一部分关键就是更新管理,这个概念在开发后期添加到游戏中,当我们通过系统向游戏中添加大量的逻辑和修饰组件时,我们自然会增加场景当中实现了MonoBehaviour的Update方法的组件数量,这回导致引擎从C++跳到C#的回调增多,性能不是特别好。所以我们每一个网络组件实现了UpdateSynchroniser接口,而不是通过MonoBehaviour的Update方法。通过这样的修改,大大减少了组件所花费CPU的时间。我们还在加载界面增加了一个额外的步骤,这个步骤就是扫描场景当中某些关键组件当中的某些关键实例,并且保存它们的引用,这对玩家交互代码很重要,因为每一帧,我们都必须扫描所有的可以跟玩家交互的GameObject。对于游戏当中的对象,预先存储的引用数组大大减少了分配。还有一个非常重要的改进,就是把委托放在一个List中,而不是使用+=,-=的方式来进行注册,功能是相同的,你可以看到加等和减等是相当大的垃圾源,所以我们还是要考虑尽量避免他们的发生。


还有一点是我们有时候没怎么讲到的是,就是说一个用户界面的一些具体元素的性能,特别是玩家的HUD,因为玩家是用来理解游戏世界游戏中心的一些核心数值。最一开始的时候,屏幕顶部的一些小部件都是带有Animator组件的用户界面元素,这个问题是UI组件的移动,会导致我们的Canvas被标记为需要重新布局,重新布局,这对性能是有影响的,尤其是进行多处组件都在这样做的时候。我们怎么办?我们干脆移除了Animator,将UI元素实例化设置成静态的状态,并且使用着色器进行渲染,这样相当于我们得到了动画,但是我们的画板还是非常干净的。如果在画板上任何内容发生了变化,整个画板都被标记使用过的或者是非干净的,并重新计算所需要的内容。所以将所有的静态元素放到一个画布上是最好的方法,将动态元素放到单独的一个Canvas上也有用。对于单个元素的影响我们可以控制,我们重新组织了层级结构,因为大大提高了时间的效率。

另外一个就是性能优化就是一个定时器UI,一开始我们用一个整形计时,然后把整型转换成字符串,字符串的创建会造成垃圾的产生和废物的产生,我们通过预先分配的字符串来,避免垃圾的产生。

其他的内容或者是更加具体的是我们的输入是否有任何的延迟。那么你可以看到Switch的版本最终实现的目标是每秒30帧,PC、XBox的目标是60,我们与其他版本相比,Switch的版本即使是在稳定情况下也是响应很慢的。通过调查我们发现两个问题,游戏移动系统以和游戏相同的帧数进行,它是30帧或者是60帧,当玩家施加一个力,只是增加DeltaTime并不会实现同样的结果,因为加速度和DeltaTime成正比,而速度不是,就意味着有一个物体在30的速度下加速变慢,我们必须把角色的更新频率锁定,使其以60FPS的速度运行,哪怕是在较低的帧速率下也是如此。即使做了这样的调整,我们也是慢。我们使用高速的摄像机来录制200FPS的视频来进行测试。从我们输入到它出现到屏幕上的时间,我们是拍摄屏幕和控制器,并且检测到输入后出现大白框的显示,可以测量游戏的输入延时,那么这个测试我们做了5次,同时计算了平均值。我们发现在游戏中有一个与帧速率的延时,60PFS当中延迟的是一半,PS4是5帧,XboxOne是3帧,Switch是3帧,直接读取本机的输入,测试时间为PS4节省一些时间。最后我们将这些时间安排在一起,他们都来自于Unity,我们和Unity进行交谈,看看我们有没有方法来解决这样一些问题。所以当你有机会做出这样的变化时,尽早测试这个,特别是由于游戏当中所产生的。

还有一个Split—Pad,其中有一个控制器,其中有半个控制器控制单个厨师,再使用另外一半控制另外一个角色二个控制。如果想让两个用户交互,或者吵架的话,这种新的功能是对整个的环境是非常敏感的。你不能同时切菜和投掷,因为相同的按钮不能用于两个操作。

由于兼容性问题,我们无法在联机模式下使用Split-Pad,因为Split会导致创建一个新的Guest玩家,在联网环境下,这个新的Guest玩家无法通过认证。但是我们确保了在线环境下可以用半个手柄来操作厨师的能力,它有一定的支持能力,那么在往后看,并不是所有的这些主机都有相同的功能我必须要考虑到主机的一些差别。而且UI必须满足这些不同主机的差异性,比如说我们在疯狂厨师其他的特点,就是这种系统集成到主菜单当中,而且选择玩哪种模型之前,主人邀请朋友参与到他们的厨房活动中,这是非常好的系统,尤其是对于PS4和Xbox one和Steam来说,但是Switch上我们遇到了一个问题,因为这样的功能并不是说所有的平台都能支持的。我们需要在平台上保持相同的体检,所以就需要不同的用户界面,这样主机就可以进入在线的状态,他们的朋友通过自定义菜单来加入游戏,我们需要另外一个自定义的代码路径来支持浏览列表的概念,以支持附近的玩家加入进去。这样的类型我们在下载的时候,有的时候都是在争夺这一焦点,其中常见的例子是由于某些错误的状态,如控制器断开链接而出现的对话框,这些对话框的焦点,就是你要将它关掉焦点将丢失,所以我们要考虑到它的逻辑层。


由于我们是在主机上开发,我们还需要计划一些不定和DLC作为其中的部分,在开发任天堂当中有时候需要AssetBundles的支持。同时平台也要求你将Asset收集到Bundle当中,而不是将资源添加到场景当中。那么在游戏当中,我们创建了自动化的系统,将资源添加到一个固定大小的Bundles里面,我们尽量调大这个阈值,这里我们创建小的资源包,然后减少不定的范围,或者是限定补丁的大小。我们使用了Asset Bundles,当你从里面加载数据时,你的加载时间也会受到影响。

《胡闹厨房》是非常棒的游戏,也有很多人非常它,我们会不断地优化我们的管线,尤其是关于它的细节,其中的一个挑战就是我们希望能够有更多的设置。

早期版本中,它使用了大量的半透明面片,让美工可以控制,性能非常差。当游戏发布以后,我们发布了特别建模的叶子,这提高了性能,也是让游戏有了风格。这样一种单独建立的树上的叶子,也是比较好的改进,因为它更加适合我们的风格。

还有在最初的游戏版本当中,几乎每个游戏对象都有自己的素材,还有一个简单的着色器,完全满足了对象在游戏开发当中的需求,虽然这让这个游戏的开发变得更加简单,这让我们开始为每一个不同主题开始绘制它的纹理图,并且贴合实际情况的着色器,每一层仅对所有的艺术资源使用很小的这样一种素材,这只用了1/4,所以提高了整体的帧速度,这是我们发布以后最大的单性能提升,因此,我们的游戏基本上都是以60秒/帧来运营。


我们已经为游戏更新和发布它的内容包,每个扩展包都有一个小型的活动,自从游戏发布以来,我们还添加了练习和生存的游戏模式,玩家可以很放松地学习,并且知道每关怎样玩。通过精心设计的组件系统,运行时可以切换网络组件和本地组件,无需对场景进行更改即可切换本地模式和联机模式。这对补丁的大小来说也是很有帮助的,我们可以让补丁非常小。

我的演讲完了,谢谢!

最新评论
暂无评论
参与评论

商务合作 查看更多

编辑推荐 查看更多