搜索 Unity

以系统造就生态:游戏涌现性设计

2021年12月7日 类别 技术 | 10 分 阅读
Hero
Hero
涵盖的主题
分享

只需几个简单的脚本,游戏设计师就能制作出相互作用的游戏系统,产生有趣和意想不到的游戏效果,以一种有组织的混乱让玩家感到开心和着迷。本次,我们邀请到了游戏设计师Christo Nobbs向我们解释怎样使用几种Unity功能来设计涌现式游戏玩法。 

根据我们多年来与个人创作者和游戏工作室的访谈经验,游戏设计师在Unity中涉及游戏玩法和机制时,他们一般能向团队其他成员清楚、详细地展示自己的构思。

我们编写了一本新电子书:《The Unity game designer playbook》,供希望用Unity设计、制作和测试游戏的设计师们参考。游戏设计新手与老手都可用该指南为自己点亮Unity新技能。

本书由资深技术游戏设计师Christo Nobbs参与编写,Christo擅长系统游戏的设计和Unity(C#)。本文属于游戏设计系列博文首篇,其中Christo将展开讨论书中部分系统设计的技巧和实例。

Test project
上图中的测试项目被用于测试火焰蔓延系统及其可能产生的混乱。

在游戏设计中,实用性强的系统应该具备明确的功能、可存储数据,并带有模块化特征。这样一来,系统不仅能与其他系统互动,还能借助其他游戏对象改变自己的状态。在构思一个游戏系统时,我们不一定需要设想一个完整的架构,而可以把它看作一个个不停影响游戏的功能。

通过结合这些系统,我们可以创造出受玩家行为作用的独特系统生态,再借助“设计控制杆”(而不是那种藏在脚本里的硬编码数值)进行平衡。设计师将使用这些控制杆来调整参数,产生系统性的改变。  

大多数游戏都会有至少一个系统,它可以是单一带有可调参数的游戏机制或由多个系统交织成网。在类似《帝国时代》等战略游戏中 ,玩家必须学会通过控制资源来制约并击败对手,这是复杂系统生态影响游戏进程的一种例子。

但说到底,游戏的推进方式要由设计者自己来决定。我们可以将游戏设定成一个受控的线性叙事,或让一个或多个系统有机结合来产生涌现式的结果。

模块化系统催生出的有趣玩法

在设计一个小系统时,我们应优先考虑系统的模块性及与其他系统的衔接能力,由独立又相互作用的系统来驱动有趣的游戏逻辑。我们可以设想某个系统在另一个物体的作用下与自己相撞会产生怎样的连环反应,或者用两个相互对抗的简单系统来创造一个游戏环境。这里,每个系统的功能都必须能被玩家清晰理解,让玩家有机会了解并利用这些系统互动。

Oxygen Not Included
Klei的《缺氧》

缺氧(Oxygen Not Included)》是连锁反应的一个好例子,游戏的主目标是平衡各个系统、营造一个互相共存的乌托邦来不断推进游戏。另一个例子是拉瑞安工作室的《神界:原罪2(Divinity Original Sin 2)》, 游戏中某些小怪在受到攻击后会产生爆炸,如果玩家通过水桶、祈雨等手段淋湿它们,就可以避免爆炸、让战斗变得更轻松。或者玩家也可以用火焰法术点燃地面,并阻止怪物靠近。 

模块化系统的小部分功能甚至可用来打造另一个系统,为游戏带来更多的游玩价值。Christo到哪都会用的一个Unity功能是Animation Curves(动画曲线)。他说:“动画曲线的数据处理有着统一的方法,并且相比于参数它有着更精细的控制。我们即可以减缓某种机制的运行,也能让系统‘超频’,借此反复调试某些细节。”

模块化的设计方法还能让我们更准确地向程序员介绍系统,让后者能更高效地编辑和调试,或调整系统配置再重复使用。 

设计控制杆

用设计控制杆来调整游戏难度等参数有助于提高游戏运行结果的乐趣、调试出想要的游戏复杂度。我们可以反复测试和调整,直到取得理想的结果。

设计控制杆的使用可以贯彻整个游戏开发,从游戏概念设计和原型阶段,一直到后期精细打磨。Christo就表示自己在用流程图描绘想法时经常加入设计控制杆。我们需要尽量在项目初期就考虑好要加哪些开关和控制杆,方便后期的游戏调试、探索游戏的各种可能性,而不必大量地重写代码。

在下方的流程图中,敌人在Start处会生成在出生点,玩家在进入流程时则装载好了武器。本来玩家必须杀死敌人,但我们可以利用控制杆来改变游戏玩法、转移游戏焦点。在一种配置下,游戏玩法可变为赚取高分、在限定时间内快速杀死敌人,而在另一种配置中,游戏玩法可转变为在限定时间内杀死一定数量的敌人,再留下一点时间捡取战利品并撤离。

Gameplay design
简单的玩法设计

这种系统也可用于制作带有强力掉落物和多种子弹类型的“子弹地狱”类游戏。通过加入诸如限时重生等更多的设计控制杆,我们就能进一步扩展该系统。

《逃离塔科夫》中的武器制作系统

Battlestate Games的《逃离塔科夫(Escape from Tarkov)》是另一个在Unity里系统性设计游戏的例子。游戏有一个制作系统,可用于制作带有独特数据的武器,如下图。这套数据将决定枪的特性和玩法,与之相似的还有玩家的健康状况。如果玩家惨遭断肢或染上疾病,角色会变得难以使用枪械,而健康的玩家则能更好地控制武器。

Weapon from Escape from Tarkov
《逃离塔科夫》中的武器制作系统

武器系统与生命系统间的关系鼓励玩家不仅要珍惜自己的生命,还要珍惜性能更好的武器。没人想用一支没有防尘罩或枪托、准心跳来跳去的AK步枪,这种配置的武器很明显是一个糟糕的选择。 

游戏中还有大量带有不同特性的弹药种类及护甲。玩家必须判断对手带的是什么弹药和护甲,找出针对性的攻击策略来最大化伤害,这就形成了一种相生相克的游戏规则。每个系统都有自己的视觉元素,游戏的设计师们通过平衡这些系统来创造出自己眼中的游戏“精髓”。

Escape from Tarkov
《逃离塔科夫》设计的系统就是为了还原真实感。

将可编程对象用作“控制杆”

在Unity中加入设计控制杆的另一种方法是善用ScriptableObject。ScriptableObject可用作数据的容器,并作为单独的资源在脚本中引用,避免让脚本依赖其它场景对象。  

我们可以创建多个ScriptableObject资源、保存多个参数集,通过共享和交换数值来改变整块游戏体验,其作用类似于预设。运行模式下所做的修改将被保存到ScriptableObject,所以无须在退出后根据笔记再次修改。 

比如在设计某角色的原型时,我们可以将ScriptableObject替换成另一个带有不同数值的对象来改变角色的感觉。这是一种制作BUFF/DEBUFF原型或将特定角色保存至某个存档的一种可能的方法。

假设正在制作一款射击游戏,并且已经做出了一个枪支系统,其中的后坐力、射速、精确度、射击模式、音频设置、VFX设置等强制行为具备一定的随机性。我们可以先新建任意数量的新枪支配置,在运行模式下调整其设置,然后一次性保存所有的改动。这些预设好的ScriptableObject可在团队成员之间传递,并搜集反馈来找出对的游戏感觉。 

设计控制杆可作为公开属性来控制脚本的某个变量,变量的浮点或整数范围可用Unity的RangeAttribute(范围特性)限制,然后作为滑动条显示在Inspector窗口中。这样做的目的是在项目打开期间调整变量,让编辑模式下的修改或工具测试也能调整变量,而非仅仅是在运行模式下。

Panels
1)范围、使用提示和标题三种特性。Inspector窗口可以根据设计需要进行自定义。2) 用Odin创建的自定义Inspector窗口,该工具可在资源商店购买。3) 保存于ScriptableObject上的数据可用于创建不同的数据集或配置文件,这里是被用于创建特殊攻击。

相比于其他游戏类型,生存游戏的玩家期望有大量的行动选择及合乎逻辑的后果,并在此之间找出每个挑战的解决办法。那怎样才能设计出这种兼具挑战和对策的涌现式系统呢? 

让我们看看一种玩家需要待在特定区域才能存活的例子,比如Klei工作室的《饥荒》。玩家造出的篝火可以组织附近敌人进攻,也可以用来烹饪食物和保暖。但玩家、食物或易燃物在离火焰过近时也会被点燃。 

那什么样的系统才能产生上述的连锁反应呢?可以用一个体积(Volume)来逐渐加热附近的玩家,或者编写一个连续性的火焰蔓延系统。但何不以一种以系统为中心的角度来实现这种效果呢?我们可以让可燃物相互之间或与其它系统碰撞产生连锁反应,迫使玩家应对这种复杂的情形。

Capsule Colliders
在应用了火焰蔓延系统时,胶囊碰撞体(Capsule Collider)可用于表示热量从一个对象到另一个对象的传递并产生连锁反应。当该对象着火时,其周围的区域会变成红色,表示热量正在散发。周围受热量影响的游戏对象会随温度上升逐渐变成黄色,在最后点燃时变成红色。

我们可以建立一个池塘生态系统,在其周围的固定区域内种下树木。树木在发芽生长后会逐渐占据整个空间。这些可燃的树木完全长成时可砍倒作为木材。  

玩家能用木材建造花哨的木椅等物品,或用来在池塘边生起一团小篝火,温暖、烘干游泳后的玩家。但如果玩家有了点燃篝火的能力,会发生什么?

具备简单燃烧系统的木头在燃烧时会向半径范围内持续散发热量,当热量超过了附近木制物品或树木的燃点,就会着火。因此,玩家在点燃篝火时也就等于点燃了自己的木椅。要想挽救椅子,玩家必须把它扔到水里去,但这时火又把附近的树点着了,火势越来越大最终形成了森林大火。 

即使是这种迷你、有限的系统也能为玩家提供有趣和“无脚本”体验。另外,必须把重点放在玩家所期待的结果,而不是一味地还原真实,不然游戏将缺少意外诡谪的乐趣。 

在本例中,可以把这个“系统开关”存为一个ScriptableObject ,添加到任何可燃物上,为突发事件创造可能。我们首先来看看这么一个世界:水边有一棵枯树已经在燃烧,树向着水面倾斜,而玩家需要取暖。

本例中的树木就带有一个储存多种燃烧相关参数的ScriptableObject。

Panel
Code

具体来看这些参数。

  • Default Temprature(默认温度):不受全局状态影响时的占位值。假定所有树木都带有燃烧系统,则整个生态环境的感觉会随着全局气温的高低而改变,高温下树林将极易产生火灾。
  • Current Temperature(当前温度):物品加热或冷却时的温度,它决定了对象是否在燃烧(当前温度是否超过燃点)。
  • Combustion Temperature(燃烧温度):对象燃烧必须达到的温度
  • Heatup Rate(发热率):对象在某热源影响半径内的升温速度。
  • Cooldown Rate(冷却率):对象恢复到“冷却”温度的速度,可以称为恢复率(retention)或热量(thermal quality),这个名称只要表意即可。
  • Burn Rate(燃烧率):对象在时间段内的燃烧速度
  • Fuel(燃烧质量):对象带有多少可燃烧的质量
  • Heat Strength(加热强度):燃烧半径内的发热程度
  • Heat Radius(散热半径):热量扩散的范围

外加上一些配套的游戏逻辑,就可以让燃烧物旁的物体着火了。我们可以先存储原型的配置,然后不停试验,直到找到突破点,然后定下这些属性值。

Prototyped with assets from the Asset Store
用资源商店的资源制作的原型

这里的水并没有阻挡火焰的机制,但火焰在没有其他可燃物的情况下也不能蔓延到另一边,除非再加入一个新系统。 

本例中的火焰蔓延系统所带有的燃烧质量、燃烧速度和加热强度等多种参数能以多种方式产生同样的结果。我们还可以用新的参数来产生新结果,比如用“血量”替换“燃烧质量”,让树在经受一定的伤害后倒下,同时保留控制杆的功能。当一棵树的血量变低,其倒下的概率也将大幅增加。当周围布满了可燃物时,燃烧中的低血量树木在倒下后会将四周化作一片火海,除非玩家做点什么。 

在环境中加入更多类似的系统,就能得到一个可以多系统相互作用的生态。假设玩家用采集的木材建造和制作了物品,这些物品继承了燃烧系统搞不好就会成为定时炸弹! 

Panel

我们也能降低燃点阈值、提高加热速率来创造一个极不稳定的可燃环境,使对象更容易着火。增加散热半径来让火焰扩撒地更快、更不可控。将Heatup Rate限制在0到50之间可让火焰逐渐地扩散;这个值可以乘上Heat Strength,但必须保持在一个合理范围内。如果Strength为4,则Heatup Rate将被放大成0到200,如此高的热量会在几秒钟内引发一场森林大火。玩家将无法及时地灭火,游戏的体验也会变差。

Panel

如若将燃点阈值提高到300,这个系统便会更加平衡。玩家在火灾发生前会有足够的时间完成其他任务,在火苗大范围扩散的初期及时地控制住火势。尤其是当游戏具有砍树、建造障碍物等功能,或有同样简单的水系统时。

我们还可以进一步扩展火焰蔓延系统,为散热半径内的对象加上一个抗燃烧值。这样几片起火的区域可以有不同温度,游戏中也可以加入防火涂层等类似的升级。这可能过于复杂了,但通过这样系统地构思游戏,处在冰冻森林中的玩家将有机会利用燃烧系统提高存活的可能性。

A very memorable moment Don’t Starve from Klei, when a forest fire breaks out
Klei的《饥荒》里最让人难忘的一刻就是玩家不小心点着了整片森林的时候

火焰蔓延系统的例子表明单一的线性机制也能转化为有趣的游戏性,让玩家在学习和理解游戏系统的过程中探索出问题的解决方案。并且如果我们不强调系统的写实感,就不必再去思索额外的复杂度和抽象概念了。 

本文借助这个简单的例子探讨了如何用模块化、交互式的简单小系统来形成一个更大的游戏世界生态,用控制杆来平衡游戏,搜集他人意见来改进和迭代。这些元素能为玩家带来意想不到的乐趣、喜悦和悬念,有助于让游戏成为真正独特的体验。 

可在此处免费下载Unity的新电子书,详细了解在Unity中设计和提高游戏性的技巧、说明并汲取灵感。  

2021年12月7日 类别 技术 | 10 分 阅读
涵盖的主题