原文链接:GDC Vault - Procedurally Crafting Manhattan for ‘Marvel’s Spider-Man’
目录
简介
如何实现程序化系统?如何控制程序化系统的影响范围使得其结果可以从手动调整中受益?这些已经有很多讨论了。这篇演讲描述了《漫威蜘蛛侠》的开放世界生产管线,其中每个程序化系统最初是如何被设计用来迭代的,以及他们之间的依赖关系是怎样的。另外,游戏的场景是写实风格的,这为程序化系统带来了更多的任务。程序化系统所依赖的元素和游戏的区域也处于不同的完成阶段,使得开发必须并行。Insomniac 的程序化系统被用来创作、修改、和观察游戏内容,而这些内容远超出了预期。这备受期待和无法预料的挑战最终铸造了一个成功的故事,给未来的项目铺垫了一个路线图。
0. 开场
我的名字叫 David Santiago。
我是Insomniac Games的首席技术美术(Principal Technical Artist),致力于开发管线中的程序化生成系统。
Insomniac 今年在GDC上有非常多的演讲。如果你错过了之前的几场,我相信他们有录像。而我也会提到一些即将到来的演讲。
在我开始之前,我想感谢所有为这次演讲提供帮助的人。
以及 Insomniac 。我想感谢它们提供这样一个美妙的环境来工作。
而且,Insomniac 正在招聘。那就顺便来谈谈空缺的事吧。以下是一些目前空缺的职位:
我可以稍后展示这张幻灯片。
而且,我相信,我们还在两个地方招实习绑定师。
下面,剧透警告!
下面我想谈一谈游戏。
我觉得你们中有些人可能还没拿白金奖杯,但是我所透露的内容都能在网上找到。
所以,下面举手表决一下。谁还没看过蜘蛛侠的开放世界内容?
哦。。好的
那么有多少人没有玩过这个游戏?
好吧
我需要的信息差不多就这些了。所以如果所有没举手的人都玩过这个游戏。那我们就从计划创造这款游戏开始了。
立项
回到几年前,Bryan在一次会议中,宣布了Insomniac将为PS4制作的下一款游戏。
其实我没参加那个会议,但我听说大家都吓坏了。
我记得有人告诉我要做《蜘蛛侠》的时候我就说——
蜘蛛侠?你开玩笑吧?太难以置信了!
但我很快就被说服了,这就是我们要做的!
而我的任务就是:用我之前在《Sunset Overdrive》中所用的技术来构建一个城市,创建建筑和标记,但这次是为曼哈顿。这次将会包含更多写实风格的住宅区,不过我心里也有计划怎么做。
我回顾了Bryan的演讲,他为每个部门设定了游戏的支柱和目标。在如此短的时间内创造出如此多的内容,真是令人惊叹。
然后在2016年,公众开始传言“哦!蜘蛛侠游戏要来了!”,粉丝们也开始在网上分享他们对此的期待。
同样在2016年,我们放出了城市的景色
还有这条有交通、行人、热狗车的街道。
然后我们切换到这个页面,人们兴奋起来了!
对了,这里提醒了我,我要感谢一下 Marvel’s Games 和 Sony Interactive Entertainment 的支持。
但最重要的是,我要感谢我的家人,我的同事,还有我同事的家人。因为有他们的支持和热情,我们才能全身心投入到这款游戏中。
回到2016年,我们切了几个蜘蛛侠的镜头,蜘蛛侠在城市中飞来飞去。大家看到这些镜头后真的兴奋到了极点!
但对于Insomniac来说,这些CG是真的,是质量标杆,而且是最低的质量标杆。
从一开始,我们努力确保我们所有的演示都能在PS硬件上实时运行。我们还有一个在2016年E3上展出的计划。不过整体而言,我们的计划不足以应对未来两年的挑战。
说到这个,我们的创意总监Bryan,大约在11点半之后在南厅有一个演讲,你可能想会听一听他的想法。
但我今天的演讲是关于程序化系统,以及我们如何将它们整合到流程中的,这是我们面临的挑战的一部分。
演讲内容总览
好,下面让我概述下今天的演讲:
我将讨论开发世界的内容是什么,特别是程序化生成的内容
- 我将谈谈我们面临的挑战是什么?
- 我将概括我们用在蜘蛛侠上的程序化系统
- 我将描述我们最初的管线
- 以及项目在生产过程中如何发展,以及我们如何更新管线。
- 我也会提一下完成游戏时的额外挑战
- 最重要的是,我们学到了什么
1. 挑战
这是我们漫威里的曼哈顿岛
但是,蜘蛛侠是什么?游戏里有什么?
首先,这是一个开放世界的游戏:
- 设计的可玩空间大约是6千米乘3千米
- 有九个地区,从 Harlem 区的上东区到上西区
- 544个街道,1202条小巷。大部分都是虚构的小巷,实际上在曼哈顿很少有这样的小巷
- 超过8300种不同的建筑
- 3250大厦预制体。包含一个区域内连续的建筑或结构。
- 超过350种不同类型的店面,他们是独特且可定制的。
- 超过3000个犯罪
- 超过3000个小插曲任务
- 故事
- 任务
- 主角
- 反派
那么我们如何才能做出如此大型的游戏呢?
我们知道,对于这样的大型游戏,自动化或者说程序化系统将会被大规模使用,但 Insomniac Games 并不是一家大公司。
我意思是,在制作蜘蛛侠的时候,我们整个公司只有大约250人,一直在忙于开发多款游戏。
我们知道,使用自动化和程序化系统是关键,那么什么是程序化系统呢?
程序化系统通过算法来制作内容,而不是手工。数据通过一系列操作来完成,而操作将基于参数、属性、或是其他输入。
(注意,这不是实时的程序化系统,我知道有些程序化系统是实时的。而我今天所说的这些程序化系统是为了创作内容的)
设计师和美术师在整个游戏过程中尽可能地去创造游戏体验,而程序化系统则能够帮助他们快速创造出许多内容,并让美术师和设计师拥有一个良好的起点。所以《蜘蛛侠》的目标是在早期创造一个开放的世界,让设计师和美术师有剩余的制作时间来润色游戏。
我们的目标是80%程序化内容是可用的,稍后我会详细介绍。
所以我们从《Sunset Overdrive》的基础系统和工具开始。
但我们需要为蜘蛛侠来加强基础工具和系统。
在《Sunset Overdrive》中,我们在地形上建造道路,还会放置一些小物件和标记。我们还有一个系统来划分游戏的各个区域。但是《Sunset Overdrive》中的建筑非常独特,因此用手工制作的效率更高。
2. 程序化系统
我们对自己的程序化系统的主要部分有了一个计划。
在这个演讲中,我将以朝东为向上的方向,因为这样的长宽比更合适,希望你们能习惯。
在前期计划中,程序化系统应包括:
- 地面系统,如地形、街道、人行道、一些装饰和集成的标记
- 建筑以及其标记
- 行人与载具的交通系统
- 生成大量物件的系统
地面系统
Define Roads And Alleys
地面系统的第一步是道路和小巷。
道路和小巷定义了一个城市。
其他的一切都将依赖于道路的位置、大小、以及其他配置。
注意:依赖性在本次演讲中是一个重要东西。
从编辑器的曲线与直线中,我们将生成:
- 道路
- 人行道
- gray spaces(译注:灰色空间?)
- 岛周围的海堤
Ground Modifiers
地面修改器(Ground Modifier) 将影响环境如何构建
所以黄色的部分是我们的地面修改器区域的内容。
这些是额外的曲线、多边形、体积。
他们将对特定的区域进行:切割、变形、修改或增加材质等。
还有一个类似的设计修改区,影响的一些设计内容,例如:
- 犯罪
- 交通
- 导航等等
Tiles
我们不会为了地面创建一个巨大的模型,而是为每个 streaming区域(streaming region)(译注:streaming加载的单位)生成模型。
所以说,我们的开放世界被划分为了正方形的Tile,边长为128米——应该说边长是大约这样的120米。
你可以看到有一些双线,这是我们系统中一个有趣的细节。它表明了对一个Tile的偏移进行调整的历史,调整的原因多种多样。我们实际上保留了他们的历史,这样我们可以进行验证以确保它在大致上适合其对应的streaming区域。实际上在最终的游戏中只有很少的区域进行了调整。他们大多都在海岸线上,只是为了减少Tile的数量让它变得更大一些。
你可以注意到 Rikers Island prison (里克斯岛监狱) 没有Tile。因为那里没有程序化的内容,所以程序化系统不包含那里。
Tile ID
每个流区域都有一个TileID。
这是一个映射位置,从A01
到X48
。这样便可以有层次地将开放世界的所有区域都包含。
你可以注意到右上角的X48
,那个孤立的Tile是我们在开发整个游戏过程中的测试区域。
Gound
下面是我们程序化生成的地面
每个Tile都有大约16
个模型,这对于物理或是其他系统来说都是一个可管理的数目。
这幅图里实际上是debug版本,所以有些区域是不完整的。你可以看到顶部里克斯岛监狱的一些碎片,它们后来是手工制作的。
还要注意,中央公园的地面也是手工制作的,因为要适合其本身的特色。这些手动的工作都被经过程序化的处理,最终整合到游戏中。
Ground Details
地面系统的最后一部分,类似于 物件系统(prop system) 的开端。
在这里,我们添加了地面的细节、车道线、人行道、基于区域的材质等等。
我们做了一些街道物件比如井盖,和人行道道具比如路灯,还有一些东西类似垃圾桶、树等等。这是为了设计师可以测试。所以当他们在城市中飞来飞去的时候就可以看到——哦!别撞到树上了。
建筑系统
现在来到建筑系统.
建筑是从一些基础几何体构建而来,比如 立方体、圆柱体、多边形体积。它们定义了建筑的轮廓。
随后,这些简单的几何体被程序化系统处理,它会从 建筑资源包(building kits) 中实例出一些资源贴到几何体的表面上:
至于屋顶,模型可以被重新调整大小和重复平铺,以适合屋顶的大小。如果程序化系统不能处理,则会自动创建一个自定义的模型。屋顶的物件也是此时被添加到大楼上,例如水塔。
此外,程序化系统还会添加一些用来悬吊的提示标记,和多种用来横越的 窗台路径(ledge path) 等等。
我们发现,当我们修改街道时,我们很可能也会需要改变建筑。
下面是两场相关的演讲:
因此,我们的建筑工具最初是采用 仅重新计算标记(recalculate markup only) 模式设计的。所以,如果手动修改了建筑物,或从头开始建造了完整的建筑物,我们的系统可以自动读取该建筑物的预制件,生成标记,并且该标记始终通过以下方式进行优化: 连接所有线段以最小化曲线的数量,最小化标记的数量。
我想这就是关于建筑系统的内容了。
交通系统
现在来到交通系统。
从路上的行人,到交通工具,都是程序化生成的。
行人系统
就行人交通系统而言,我们要做的第一件事是在人行道上放置静态的 行人体积(pedestrian volume),这些Volume只是在人行道上填充了一定密度的行人,然后他们便会站在那里开始移动。但在之后他们可以被整合到行人系统中以真实地行走。
然后,我们创建一个路径网络,该路径网络会从一个点到另一个点,例如从人行道上的一点到另一个点,例如跨过道路的两个点。
然后,我们要整合其他资源中的路径,比如公交站和地铁站。因为他们有一些固定的路径,比如上下楼的路径、公交站和地铁站周围的路径、公交候车亭周围的路径等等。这样就有了一些内建的曲线,而它们将会被整合到行人系统中。
最后,我们整合所有手动放置的路径,例如大部分是是穿过公园和广场的路径。
所有的这些都被合在了一起,用来生成行人交通节点供实际游戏系统所使用。
这包含每个连接的正确 交通阶段(traffic phases) :
当绿灯亮的时候,他们会穿过马路;当红灯亮的时候,你可以希望他们停下来;当黄灯亮的时候,他们可能会加速穿过马路,也可能停下来。
我们会显示调试用的颜色,这样我们就能直观地看到它是否正常工作。
每个行人在到达一个节点的时候,他们可以决定是穿过马路,还是转头继续走人行道。
交通工具
现在来谈谈交通工具
它和行人系统非常相似。
但首先,停车的车道和行驶的车道必须根据道路的大小从一个车道模板中选择。如果一条路没有设置模板,则它会随机。
然后,行人系统的十字路口变成了交通工具系统的停止线。
就像我们在行人系统中使用volume来填充行人一样,我们也用volume来在停车线上放置停车。唯一的区别是,我们要创建多个volume,用来回避公交站、消防栓、小巷、以及其他你不能停车的区域。
我们也创建了一个有向网络,连接了一个交点到另一个交点。在每一个交点,给了每个汽车一个合法的机会可以转弯。
我们还整合了小巷,作为有限制的路径。你可以看到上图中远处的红色路线,那就是可以行使的小巷。他们被整合进交通系统中。我将随后谈一谈他们有限制的用途。
接下来,我们将创建交通节点,以及节点之间的连接。
基于交通的路道标记,将会应用于随后的物件系统中。
这里你可以看到很多选项,左边的那辆车,可以右转或者向前穿过街道。但是右面那辆车则没有选择,它只能穿过十字路口。
下面是一个更复杂的情况,同时有行人的路径和车辆的路径:
首先看车道,这是一个多车道的街道:
可以看到很多选择,所以人们能做什么就变得有点复杂了。
看中下方的区域,所有都是绿灯。所以它们可以右转弯到单行道上,而其他的可以直行。相反方向,从左到右的车可以左转进入单行道。
这是这片区域的行人路径:
你可以看到广场上手动放置的路径。画出这些路径是必须的,因为这样才能和程序化的路径相交,从而整合到系统中。
装饰物系统
装饰物系统主要包括 物件、贴花、和一些预制体等,一会儿做解释。
我们程序化地优化了一些物件的摆放。例如:
- 街道上的 井盖、打滑痕迹、焦油沥青
- 街边的 路灯、交通灯、邮箱、树
- 等等
还有,系统中的 行人预制件(pedestrian prefabs),例如:
- 公交站
- 地铁入口
- 热狗车
- 出租车
它们可以影响人的行为。例如,人们可以在热狗车中等待,然后沿人行道行走。如果遇到了犯罪,则会进入“逃离”模式。
我们也会整合一切手工放置的预制件,它们将会被整合到系统中。
我们也做了一些程序化的照明,这个我就不多讲了,因为今天有一个专门针对它的演讲:
在我们最初的设计中,我们的工具已经具备了在街道上摆放垃圾和碎片物件的能力,其复杂性达到了需求。
我想这里我可能需要问一下,有多少人玩了第三幕。如果你玩过,就会发现到处都是各种东西。
这里的挑战是:看起来到处都是垃圾,但是不会阻挡任何车辆、行人、犯罪、小插曲任务等等。这就有些棘手了。
注意图中蓝色的区域,他们是程序化生成的水坑,位置是随机生成的。当环境中的湿度达到一定的阙值时,他们就会产生水坑,因此他们会在下雨天被激活,它还会溅起涟漪。
以上就是所有用来放置物件的工具。
Impostor自动生成系统
(译注:Impostor技术:一项优化技术,在远处用一个面片来代替实际几何体,面片的贴图不仅包含颜色信息,还可以包含法线信息等)
Impostor的生成,在项目的早期阶段就被决定作为程序化系统的一部分了。
下面是俯视角观察的城市的Impostor版本:
Impostor是城市中每个结构最低级的视觉表现。
我们根据建筑预制体的高分辨率模型生成简化版本,然后我们再根据简化版本投射到贴图上,以此制作图集。
we populate the impostor zones with the correct updated impostors, so that we can put them in the right kind of containers for optimizing memory and such.
(译注:我们以更新Impostor的方式来排列它们,这样他们就可以被放到正确的容器中,达到优化内存等等的目的。(大概是一种优化方式))
当地面结构、材质、光照等改变时,我们就程序化地重新生成Impostor。所以基本上,我们会一遍又一遍地生成Impostor。
这里再次强调,Impostor在整个城市中是始终存在的,每当你看到城市的一个景象时,他们几乎全是Impostor。
以高分辨率加载的区域(译注:应该指Tile)最多只有12个。
程序化系统的其他需求
好了,这就圆满完成了!?
那么我们可以开派对了!
等等,我在派对上没看到设计团队,发生了什么?
就在我们觉得有能力应对开放世界的所有挑战时,设计团队也表示他们需要程序化系统的关爱!
所以我们遇到了新的挑战。
游戏设计相关
第一组挑战是动态的遭遇,包括犯罪和小插曲任务。
我们在世界中依据不同的规则,程序化生成了29种不同的犯罪,和25种不同的小插曲任务
另一个来自游戏玩法的挑战是:一些任务会对主角的移动有要求。举例:
在一个任务中,罪犯会使用喷气背包。那么你就必须要找一个有64平方米屋顶的作战区域,然后离另外两个屋顶有不到40米的距离,并且原来的屋顶到那里有不到20米的高低差。而且每个屋顶都有个清晰的路线,这样我就可以在屋顶之间放置导航点,在屋顶和人行道之间放置导航点。
然后系统就会在全世界运行,根据游戏中当前的建筑寻找所有可能的位置,然后根据每个区域所需的密度或整洁程度,来放置犯罪。
音频
之前我提到了灯光,但是音效团队也需要程序化系统的帮助。
我们程序化的方式生成了河流的音效,城市中其他合适的地点也将分布音效。
关于这个话题,可以看我们另一场演讲:
3. 管线
现在回到我们的话题。
如果我继续讨论内容的创作,我们将面临更多的程序化内容、其之间的依赖、还有手工制作。
我们必须梳理一下,建一个管线。
考虑一下:
- 犯罪以及小插曲任务,将依赖于环境。
- 移动路和小巷,将改变他们定义的街区。
- 移动犯罪的影响取决于其复杂度,因为它会影响物件的摆放。
所有这些东西,非常多!
很多的系统!
很多的规则!
很多的依赖!
我们需要一个计划——
那就是创建一个管线,我们在其中可以按照正确的顺序完成内容,可以在其中迭代。
使用Houdini
因此,我们选择使用Houdini。
虽然,Houdini是我们用来开发和管理程序化系统的主要工具。但是,算法可以在任何软件包中实现。事实上,我们有大量程序化生成内容的代码,是和Houdini或其他DCC软件无关的。
这个,是我们在大约2016年实现的,包含程序化系统的开放世界管线。
it’s a very simplified diagram without every possible error or box because this just unreadable(不确定的翻译:它被过度简化了,所以几乎没有什么具体的可读意义)
它可以粗略地划分为三部分:
- 第一阶段是主要世界布局,设计师会在其中放置道路,粗略规划建筑,找停车的区域,任务区域等。
- 第二部分是大多数程序化内容创建的地方
- 第三部分是打磨阶段,程序化系统会协助完成游戏内容。
顺带一提:
- impostor的光照数据将会在每个阶段的迭代中重新计算
- 在第二阶段和第三阶段,音频数据和UI数据也会程序化地生成。
因此,游戏总是保持可游玩的状态。
阶段1 - Top of the Pipe
这是第一阶段:
Curves define streets and alleys
第一阶段的第一步是用设计师的曲线定义路。
实际上,城市的第一版是来自插画师绘制的曼哈顿地图,程序化系统可以参考此图像,在编辑器中创建曲线。程序化系统还将随机地在不同区域放置立方体盒子。它会读取设计师的颜色数据然后覆盖到不同区域。
当到达了某个状态后,我们就移动到编辑器中,随后一直在编辑器中工作。
设计师可以在编辑器中重新定义道路。他们可以在其中指定道路的方向,或者说多条路线的主要方向。他们可以指定道路宽度 和街道宽度。(道路宽度 指的是一边人行道的外侧到另一边人行道的外侧;街道宽度 指的是从一个曲线到另一个曲线)
他们还可以改变车道的配置,等等。。。
所以,最开始只有主要的道路被指定了。而程序化系统会指定剩余道路的大小、方向、布局等等。
举例,我们想要70%的道路是单向的且有一个停车道。然后系统就会穿过整个城市创建可行的交通道路,最后如你所愿,交替创建出单行道。
不过,随着时间推移,设计师可能会指定某些道路的大小等配置,以配合故事,或追逐战等等。
但我打赌,大部分街道应该还是程序决定的。
对于小巷。我们最开始小巷的数目很少。
设计师会想要打破一些区块的阻碍。因此在房子建好之前试图创建小巷是没有意义的。
Ground system makes island
然后,我们利用这些道路曲线创建地面。
方式就如同之前讨论的那样。
Primitive buildings modularized with instanced architecture
然后,设计师就会设置一些参数来定义建筑。这样,粗略的轮廓就被创建出来,然后程序化系统可以使用模块化的组件来装饰他们。
这样,设计师就能在空间中测试了。看看蜘蛛侠摇摆的路径是否符合期望,看看城市的轮廓与景观是否能将玩家引导向期望的路径,看看不同区域的密度与形状是否有期望的变化。
我们有一场GDC演讲就是关于如何决定上面这些事情的。
有一件事情要意识到的是:在项目的早期,蜘蛛侠的完整动作还没做好。因此这限制了我们对于一些布局的决定。所以,在做这个第一阶段的时候,我们显然需要有机会能返回到最初设定道路曲线的步骤中。
当独立的建筑被填满一个街区时,程序化系统将自动创建它们之间可编辑的小巷曲线。你可能会问:“为什么还需要创建可编辑的曲线?不能直接将建筑之间的空隙视为小巷吗?”
答案是不能,因为其中一些需要人工设计,目的是配合犯罪和追捕。
我们还会用这些曲线来改变地面、表面的物件、照明、音频需求等。
阶段2 - Heavy Lifting
所以,在第一阶段之后,我们应该已经完成了街道部分,还有粗糙的建筑。现在可以移动到下一阶段了。
第二阶段是我们程序化系统的大部分工作发挥作用的阶段。
这个示意图中的每个方块,都应该有一个来自之前的方块的箭头。因为任何基于依赖关系的变化,都需要上游重新提供数据。
记住,所有的这些都是尽可能地迭代的。所以,如果管线没有被破坏,我们将在此阶段基于所有的更改生成所有内容,以保持游戏更新和可玩性。
最后一件非常重要的事情是:
美术和设计师不应该插手任何程序化生成的内容,他们必须使用程序化系统中可控的参数和输入来影响效果。如果他们不这么做,就有丢失他们的工作(手工劳动)的危险。
这里是我称之为 Art Poly Line Pass 的地方:
美术师可以在这里用多个多边形体积替换设计区域中的几何体,以进一步定义建筑物的形状。美术师还可以指定他们要用于建筑物的资源集。美术师还可以修改或删除小巷曲线,表示他们不希望在那里有一条可驾驶的小巷。而之后我们会为他们重新计算路面。
这个图,这个“Phase 2”,实际上代表了两个几乎一样的阶段,我们称之为 模块化场景的两个阶段(two stages of environment modularization passes)
Now iterations of the environment arts first modularazition pass would use the structures with their final layouts and shapes.(不确定的翻译:模块化环境的第一次迭代将会使用最终的布局和形状)
但是建筑风格和物件,将可能在“Phase 2”的第二次迭代中发生变化,也可能在 Phase 3 中变化。
在第二阶段的最后:
我们可以理论上精确地放置行人、交通、小插曲事件、犯罪、物件。
精确性是我们程序化系统在设计上的要求。
我们的目标是80%内容使用程序化系统来创建,所以美术师和设计师只用做传统方式中20%的工作量。
所以,我们要做的就是根据规则和参数生成内容,但不是规范。因为手工的打磨将会做最终的调整。
阶段3 - Polish
而在这一点上,第三阶段会批准做这些事情:
除了美术师,每个团队都会在游戏中使用手工制作他们的部分。
而程序化系统,只是去更新装饰、Impostor、光照、音频、等等。
因此,应该有足够的制作时间来开发程序化系统,添加新的 悬吊标记点(traversal markup),或支持新的犯罪。
或者我们也可以开始制作优化工具来改进性能,或制作验证工具来帮助找BUG。
规划地区
所以,这条管线是按照一个特定的计划建立起来的。
这个计划是通过几个阶段来统一开发这座岛。
因此,我们需要锁定初步的地图布局和结构,并定义关键的区域,然后才能进行任何细节处理。
所以,我们将从全岛范围的系统开始。
再提一遍,我们将在地图布局中最后确定这些道路:
额,下面,是一个片段,展示了前期制作到后期成品的变化,不过好像不怎么有趣(笑)
所以我们有了生成的地面,而其中包含关键的空间和道路标志。
因此,我们将一次工作一个分区,添加细节,并迭代该分区内的依赖关系,以使其达到下一个完成级别。这在我们的管线设计中很有趣。
所以只要我们逐一处理岛上的每个地区,保持统一的打磨级别,我们就可以将每一个地区视为一个独特的岛屿,有其独特的属性,然后再为每个地区建立边界。我们还使用了一种特殊的方式来处理共享的Tile边缘。
举个例子,下面是一个地区,处于阶段2,第1步。
在这一点上,我们已经完成了阶段1,也就是说我们确定了路线图还有初步的建筑,他们定义了轮廓和可供穿越的空间。然后他们都被转换为了 结构化预制件的实例(prefabs of instance architecture)。
然后,现在来到阶段2的第一步,也就是 模块化美术资源步骤(Art Modularization Pass)。每个美术师被分配到一个区域中。他们在被分配的区域中工作,直到这个 地区(district) 的所有区域都完成。然后程序化系统就可以对这个地区运行,放置犯罪和小插曲任务。随后美术再移动到下一个地区继续工作。
邻居依赖问题
但是我们发现,当美术师在一个区域中工作时,他们需要调整道路和小巷以匹配这个地方的特点。然而,道路和小巷的调整将会影响其他多个地区的交通系统,这样我们就需要改变更多的道路了。
——这就导致了变化的多米诺效应。
而这将我们带回到流程图的第一步。
另外,由于处于不同的里程碑和不同的开发阶段。不仅不同的地区处于不同的状态,两个相邻的Tile也可能处于不同的状态。
比如其中一个还在初步阶段,但另一个已经在打磨阶段了。但为了放置我们的大部分内容,Tile必须完成环境的 Art Modularization Pass,这样才能获得一些批准(译注:应该指手动编辑的批准),但他们没有完成。
也就是说,这边的工作可能已经有很多了,但是那边的工作却还不够。这对我们的平稳开发是不利的。
程序化系统不是不能重新生成内容,只是说重新生成可能会导致手动工作的丢失。
比如,在一个区域调整小巷,仅仅是调整一个小巷,就会影响很多周围的Tile。比如:
- UV生成的连续性
- 物件的位置
- 小巷附近的犯罪
- 小巷附近的小插曲
这就产生了多米诺骨牌效应,远超出了我们的想象。
老实说,我原本设想他们只会发生那些游戏内容很少的地方,如边缘或者海岸线。
and I knew those who get neglected and fall behind.(译注:不知道是自嘲团队中有些人也忽视这个问题了,还是说有些游戏的做法是粗暴忽略掉这个邻居问题)
也就是说,在每一个Tile上,都有疯狂的依赖关系。
每个区域都至少依赖于其邻居。
因此,最好的做法是保持开发的并行。
4. 制作中发生的
在实际开发中有很多事情没有按计划走:
城市中总会有新的任务、demo、测试区域。
新的屋顶功能还需要更新额外的标记点。
我们还搬迁了几个与故事相关的地点
我个人有一点压力和沮丧,因为我们没有坚持我们所做的管线。
我们怎么办?
所以我们怎么办呢?
我们把几个头儿召集在一起,试图搞明白问题。这不是一天的事儿,而是发生在很多周,针对了很多主题和其对应的团队。我觉得很多人都经历了些痛苦。
我们必须要知道,我们美术的目标是什么,我们玩法的目标是什么。
我们的系统,比如地面系统等,需要达到一定的完成水平,这样我们就可以继续去完成那些依赖它的任务。
当在一个Tile上工作时,邻居或者说地区需要到达一定的阶段,可以添加犯罪和任务等。而不会影响任何下游的工序。
这样,我们就能有更多的信心地把工作移交给下一个程序化系统,更少的担心需要返回到之前的步骤。
然后,在某个点上,最终的 Impostor,光照,UI等就会被完成。
谈到我们的团队协作。我们环境美术的Leader将有一个讲座——学会增强美术师的力量。
新计划
所以,当知道了我们需要什么来完成工作时,我们为程序化系统制定了新的计划:
在可能的情况下,我们对依赖项进行了分支,这样每个Tile就可以尽可能独立地被编辑。
而每个操作都应该基于依赖性来进行模块化。例如:
- 交通灯:交通灯实际上只依赖于道路和人行道拐角,一旦我们知道了路的方向和拐角,我们就知道了该在哪里设置交通灯。因此在这一点上,我们有了它的 依赖集(dependency set)——这里是放交通灯以及任何与他有相同依赖的东西的模块。
- 邮箱:邮箱有些额外的依赖,他们需要避开小巷,公交站,人行横道之类的东西。因此,具有这些依赖关系的任何事物都应在对应的 道具放置模块(prop placement model) 中。
我们也需要一些更好的规则来捕捉极端情况。
总是有很多特殊的极端情况的,我们需要些规则来排除一些无效的内容。
对于内容的摆放位置,我们不需要100%的准确性,因为我们知道会有手动调整。
但是,我们需要确保程序化生成的内容100%都会在游戏中。
我们会放置内容,然后指出其变动范围,表明程序化系统对它最合适的摆放位置并没有信心。
放置160个犯罪然后只是让人们调整到正确的位置,这是相对容易的。
否则,只放120个,随后让美术师或者设计师去计算,然后记住他们还需要再放置40个犯罪,这是相对麻烦的。
这个能验证摆放位置可信度的系统,在之后演变为一个更大的验证系统,作用于更多的内容。它将会验证一个内容是否是程序化生成的。
建立秩序
建立秩序意味着:
让团队尽量在一个相同或连续的区域中工作,这样便可以更快到达新的完成级别,也就可以走向模块化的下一步骤。
So originally each district was a set of contiguous tiles. Well now it’s like I don’t care, just give me some contiguous tiles, so we cannot work through this game.
(不确定的翻译:最初我们只是在一个地区内的连续Tile上工作,但是现在,我们不在乎是否是一个地区了,就只是持续对连续相邻的Tile上工作)
而新的程序化步骤所保留的手动工作,将会与美术师和设计师的工作交织在一起。
说起来容易做起来难——但是我们做到了。
5. 最后的挑战
然后,更多的挑战来了。
攀爬问题
我们在一些新资源上有一些攀爬的问题,特别是一些新的店面。
我们需要添加一些额外的 窗台路径(ledge path)、标记、墙面碰撞(译注:wall call collision???)。他们是由程序化系统添加的,因为店面是对应于特定的 建筑套件(buildings kits)。
Act System
在项目的制作后期,我们实现了 Act System。
在一开始,我们不认为蜘蛛侠需要在一天中的不同情况有不同的行为。
但在项目的制作过程中,我们实现了可以控制世界状态的系统,它可以控制一天当中的时间,还有天气等等。而很多事物也将受其影响,比如商店可以在白天开门晚上关门。
在第三幕的时候,可能会有不同的人群在外面走动。我们可以在系统中将城市的东西改为第三幕,这样路面上就会有很多垃圾,还有不同的小插曲任务。
这个方案是在2017年10月提出的,我们在12月设计了它。当时我在度假。然后我们2018年3月的时候就完成了,就在2018年下半年发布之前。
我对新系统的处理很满意。
任务空间更新
在后期,还有一些任务空间的更新,需要一些资源的重新提供,包括美术的交通,和一些区域的开放世界玩法。
音频
哦对了,我提了音频了吗?
对于一个可见的纹理材质,我们需要程序化地生成一系列其匹配的碰撞音效。这对于中央公园来说意义重大。
6. 经验教训
我们都知道——严格的流水线是最棒的。
(当然,但我们也需要生产的灵活性)
我们希望尽快锁定程序化生成的元素及其依赖性,这样我们就可以继续工作。
我们还希望对程序生成的内容进行分层模块化,以减少依赖性。这就是我刚才说的红绿灯的案例,一旦我们知道了放置交通灯或其他任何具有相同依赖性的内容,我们就可以将其放到一个模块中。
你不要对程序化系统模块的目标进行过度定义。
你不需要创建出完美的规则,因为:
- 打造一个完美的系统需要花费更多的时间。理论上,你创建完美的系统所需的时间是无限。
- 如果你只依照最标准的规则,你不会找到那么多符合标准的地方。我的意思是,就像之前说的,很多东西不会被放在最合适的地方,最后还是需要手动做一些事情。
完美的规则,或者说过度定义的目标,会增加“约束”。
而“约束”就是“依赖性”,它是我们一直都想减少的东西。因为我们知道事情总会变化,如果依赖性有限,则变化也是有限的。
我们的程序化系统应保留手动工作。
一旦工作交由手动,我们就要改变模式,让程序化以合适的方式作用,例如:
- 让程序化维护手动工作
- 让程序化工作变为手动工作的一种增强
- 或者以其他的方式来处理。
另外,复合依赖(compound dependencies) 应该被划分为独立的步骤。
复合依赖是指:在单个资源中——
- 有明显不同的规则集合
- 或是规则有明显的分支
例如,我们有一个犯罪——
- 它可以被放在路上,比如十字路口等等
- 也可以放在屋顶上
我们不想把这个犯罪的规则都放在一个单一的模块中,我们应该把它分开。这样——
- 一旦我们犯罪地点是在路上,我们就单独使用那些只针对于路的规则
- 如果地点是在屋顶上,我们就单独使用那些只针对于屋顶的规则
这就是复合依赖。
下面继续讨论复合依赖。他其实限制了单一的资源,却对多个资源都增加了依赖。
所以,如果你在网络中用了复合的依赖,你将会给邻居带来实际上不需要的依赖。
我们标记了程序化生成的内容。
如果知道某些东西是程序性的,就意味着你可以自信地一次又一次地重新生成,而不会破坏任何手动工作。
相反,一个不正确的工作流会允许美术在基于程序化生成的东西上工作,而没有任何提示性的标记。例如:
- 一个美术看到了一个小贩推车,它想加强一些效果,于是他添加一些东西比如汽水罐和椒盐饼干。
- 但程序化系统不知道这些添加的小东西。
- 而程序化系统可能会在下次决定小贩推车位置的时候,会基于某种原因放在别处。
- 之后,你就会看到漂浮在空中的汽水罐和椒盐饼干。
当然,这个特定的例子没有发生,但是有一些类似的案例。
因此要考虑到:如果某些内容是程序化创造的,那就意味着它可能会从一些手动工作中受益。就像这样——哦,我看到了一个东西处于程序化生成的状态,我或许想要对它进行些定制化或者摆放到特定的位置。
我们想尽可能地使用程序化来验证内容。
我们想要找到那些错误放置的内容。
例如,刷出点是在一个没有内部的建筑里。
我们会自动删除和修改这些内容,前提是我们真的对结果有把握。所以:
- 如果那个刷出点真的不能用了,我们就删除它。
- 但如果它可以在建筑外用,则我们会基于系统中的规则和知识来努力调整它。
- 如果我们对程序化的调整没有信心,则我们就为美术师或者设计师指明问题,让他们去做决定。在很多例子中,我们就是让元素处于这样的状态下,他不会建造或生成什么东西,直到有人手动地修改他们。
在项目的后期,我们的程序化系统已经变得相当聪明,可以很合适地放置犯罪等。
程序化系统实际上做了很多工作,有时候我们不知道该把东西放哪的时候,就让程序化系统去协助做些决定。
总之,我们学到了很多东西,我们在很多地方上都能做得更好。
Elan做了一个事后分析:
但总体来说,我们还是对结果满意的。
回头看一看:最初的目标是完成80%可交付级别的程序化内容,美术师和设计师的工作只占传统工作的20%。
但实际上,我们已经超过了80%的目标。
不过,剩下的时间也用于额外的迭代和额外的手工制作内容和优化。
我们也多次讨论了依赖性,你知道的当你改变了地面,很多事情都会发生改变。
实际上,路的数据在一月底就被封锁了。
而程序化系统最后一次改变是在2018年6月,两个月后你就能在商店里买到游戏了。
谢谢大家,谢谢 Insomniac Games 的所有人能做出这样棒的游戏