塞尔达旷野之息中,最让我印象深刻的一个系统,就是它的化学反应系统(至少官方是这么称呼的),也就是其游戏当中的,元素与万物交互反应的系统。像是火可以点燃木箭,木箭又可以点燃地上的木头,木头可以将草点燃,被点燃的草又会烧到角色和敌人。
这样一套系统不仅仅是为玩家带来了更真实的体验,它实际上是旷野之息这款游戏的根基,他为游戏的谜题设计,关卡设计带来了极大的设计空间和玩法深度。因此我当时就非常想尝试复刻这样的一套系统。最近我终于有机会如愿以偿,进行了这套系统的复刻。
这是一款面向中轻度玩家的,以解谜为主 战斗为辅,重点考验玩家的思维&理解,而尽量不考验玩家的战斗操作和反应能力的游戏。游戏选用3D轴侧视角,一方面是致敬老塞尔达和Tunic,另一方面也是基于我们的休闲解密定位,减轻玩家的操作负担(不用操作视角),并且希望在玩家遇到谜题时,可以有更加可控的画面信息呈现。同时也照顾游戏的美学表现和方便关卡引导,让中轻度玩家可以畅快游玩。
其实要实现塞尔达式的化学引擎,要做到的最关键的一步就是将各个游戏中的物品之间交互的“中间件”抽象出来。我们可以称呼这种中间件为元素。接着我们在针对每一种元素,定义具有统一接口的规范化的生产者和接收者。
在Unity中,生产者(Property)和接收者(Sensor)分别对应着逻辑统一固定的两个脚本。并且由接收者脚本直接管理状态变更,其他脚本通过监听接收者的状态变更(委托调用)的形式,来对每个物体的具体逻辑进行实现。
假设我们主角的主角有一个喷火技能,还有一只羊和一个篝火:
主角的喷火技能因为是攻击判定,我们可以为攻击判定携带上温度信息,比如击中敌人就给其加100度,然后羊达到100度就进入燃烧状态,燃烧状态的羊激活自身上的温度生产者,寻找其他的温度接收者引燃对方:
然后,羊和篝火各自分别实现对于燃烧这一状态的表现就可以了:
将一个机制抽象为一个元素
围绕这个元素开发对应的生产者和接收者
利用代码/蓝图,串联组件,监听状态,实现各自的效果
如此一来,当年的游戏当中具有十几二十个可以着火的东西时,你突发奇想说,我想让路边的小草也可以被火点燃,你只需要为这个小草添加上与温度相关的两个组件,实现一下燃烧和被烧光的效果,你就会发现它已经自动兼容了游戏里之前所有能着火的东西。他们无一例外都可以对小草产生效果。你就会意识到,这就是化学系统的真正威力了。
其实我上面所说的这样的概念应该叫做“组件化”,将每一个功能拆分为一个单独的组件。每个组件的逻辑实现非常的简单,代码非常的健壮。而配置一个敌人或NPC,只需要配置上对应的组件,然后补齐组件之间连携的代码或者蓝图,就可以实现一个敌人的逻辑了。
当同一帧内存在大量需要更新的Property或者Sensor时肯定会卡。可以不要让Property和Sensor本身调用Update,而是在其启用时将自己注册到一个统一的Manager。Manager中设置每帧更新上限。
比方说针对前面讲到的温度传导系统,TempProperty可以只在其温度不为常温的时候才对附近Sensor展开检查。TempSensor也可以只在自己的累积温度不为0(常温)的时候才要求TempMgr更新自己。这样场景中没有发生任何温度变化的时候,即便有几万个Sensor或Property在场景中也不会触发任何的更新需求。
因为前面举例也说到了温度,这里我们就用温度举例来讨论。
在塞尔达中,温度被分为了五个档位,并且为了提升沉浸感和动态世界交互感,它的温度是一个明显的连续变化数值。但塞尔达这样设计是因为他地图上有两个利用温度进行“软锁”的区域,希望玩家拥有对应的对抗温度的“钥匙”才能解锁,并且塞尔达利用UI显示的表现了“一般热”和“超级热”的区别,否则很难向玩家传递这种温度的变化。
系统的设计需要考虑其的设计目的,尽可能的做减法,同时要考虑复杂系统对于玩家的可感知度。特别是这样的一套元素系统,它能给玩家带来乐趣的核心体验是来源于让玩家有一个琢磨这套规则的学习过程,并且在彻底理解规则后利用规则打破游戏,达成解密或意料之外的行为时的“我好聪明”的体验。因此“元素”系统更加需要充分表达各个物体所处的状态,并且确保这种状态是容易被理解的。
经过了简化的当前温度只被我划分了简单的:冷/常温/热,三个档位。看似简单,但其实也是从复杂的档位做减法而得来的。具体如何表现可感知型等,可以看上面羊被点燃的动图,它利用自身的Fresnel颜色变化来表现当前的温度状况。这也是我们游戏提升沉浸感的一系列设计中的一环,也就是尽量的避免UI,用直观化的3D场景展现状态和信息。这是我们这个慢节奏的轴侧视角游戏的优势。
而对于可被利用性,其实就是创造涌现性,其根本是在于单一元素与其他元素之间的“化学反应”。
仔细想来,塞尔达并没有真正意义上的元素反应。它只有很基础的元素间的物理影响,比如冰冻的敌人摩擦力低,可以被风吹走。以及火焰可以沿着草地蔓延等。我们期望设计元素间的反应,是希望利用这个反应达成以下目的:
核心目的:增加游戏元素系统的深度,为解密服务为主,为战斗服务次之。
它具有一定的平衡性,单一元素反应的结果尽量即可以成为解密的软锁/硬锁,也可以被玩家所用获得优势。
它足够的直观清晰。因为我们游戏的一大体验乐趣是玩家通过自己的研究和琢磨发现了一些道具的特殊用法。因此我们游戏中不能弹出详细的文字教学告知玩家一些道具的用法,这样剥夺了玩家通过研究掌握乐趣的方式。但同时我们游戏也依然需要有及其充分的关卡引导教学,通过关卡设计的方式来让玩家理解机制的运作。而足够直观清晰是这一切成立的前提(不然关卡真的不知道怎么做教学了)
现在我们来引入一个新的元素,一个塞尔达中没有的元素,比如游戏中常见的毒元素。我们来思考毒与火一起反应会发生什么呢?因为我是神界玩家,我最直觉的认知是毒+火会爆炸。OK,那这个就设计完毕了... 是不是有点过于简单了。
但这里其实可以与其他几个我们组内想到的方案进行对比,然后我们可以在这个过程中总结出设计原则。我们的脑暴如下:
首先,元素反应作为这个系统的涌现式的根基,我们希望的是一个元素反应可以最大化的影响玩家的决策。同时这个元素反应需要兼容到我们游戏的角色3C和关卡构成上。
然后,我们来看这个设计,这个方案限制了毒的初始表现形式必须是毒液。一种理解方式是:通过火来蒸发毒液,创造出毒雾。毒液与毒雾的最大区别可能是Y轴上的影响范围的区别,但角色本身是站在地上的,毒液与毒雾对于玩家自身3C而言没有区别,由于我们是一款轴侧视角的游戏,可以预见的大部分敌人也都是地上的,毒液与毒雾的表现形式并没有带来本质的变化。也许可能的作用是部分怪被设置在高处需要用毒雾去击杀,或者是有些在高处的机关需要用毒雾激活。但实际上这些应用方式基本都还轮不到火来出场,如果把毒的表现形式一开始就设计成毒雾,利用雾气向下走的一些物理规律,或者单独可以喷出毒雾的机关就足以实现了。没有必要浪费一个化学反应位置。
另一种理解方式是清除毒雾/毒液,这个说实话比毒液蒸发升华要好一点。因为它对于玩家和敌人具有决策层面上的影响,也可以设计需要清除毒通过的区域等。不过这种影响比较单一,容易变成火是解毒的Key这样一种单一思维,无论是对于玩家还是谜题设计者而言。
这个设计是极其的容易踩坑,对于CRPG、动作RPG或者战棋类游戏而言,这个设计算得上是一个不错的设计:
单纯的复合元素:首先,对于我们游戏而言,复合元素如果没有新机制,它就不能叫元素反应。一个同时具有毒的特性+火的特性的元素能干什么?能造成更大的伤害吗?当然我们可以设计这种东西,比如我们游戏中现在实装有一个技能,它是创造一个龙卷风,其本身具有风的元素效果,同时可以被火点燃,变为火龙卷。我们单独的将这一类机制归纳为元素染色机制,他是某些个技能所独有的效果。但是他不是元素反应,因为我们需要利用其创造解密,创造限制条件和优势条件。
新机制的复合元素:我们可以脑暴一下这个特殊火焰可以带来的新机制,解密角度而言,比如它能烧开特殊的障碍物,点燃特殊的机关,比火有更高的温度,或者是冷火。似乎很难摆脱专门为他设计的特殊机关,更高的温度也难以直观表达,冷火不如我直接设计冰元素出来。再加上我们游戏并非回合制,TTK也不长,这个元素在战斗中除非数值强的爆炸,或者针对怪物做机制表现,不然玩家很难感知其威力的差异。并且其需要两种元素复合才能诞生出来效果,还需要考虑玩家的投入产出比,如果你想让这个机制在游戏中发挥作用,就需要让他强到玩家愿意攒两个甚至多个技能才创造出两种元素并让他们复合。如果其中一种元素,比如说毒,脱离开玩家技能变为场地效果的话。那创造特殊火焰就变成了一种 特殊机关用特殊解法的,钥匙对应门的关系,不符合我们元素反应系统的设计初衷(拓展深度)。
表现直观:它的表现是巨大,清晰,单次的,易于展现
平衡设计:爆炸本身带火属性,可以点燃附近的其他毒,可以快速的清除毒雾,同时针对附近的怪也有爆炸伤害,这是优势。但同时爆炸也会炸到离得近的玩家,因此攻击距离近的火焰技能虽然可以快速清毒,但却会伤及玩家自身,玩家需要寻找远程火技能或者其他方式来产生火,这是限制。又同时因为爆炸会引爆其他毒形成了传导功能,可以用于设计谜题,延申玩家的火技能的触达范围,这是优势。同时因为连环爆炸,可能有一些玩家需要想办法获得东西在毒当中,玩家得用别的方式清毒或者是阻断毒雾的连接,否则东西就会被毒的连环爆炸炸碎,这是限制。
元素兼容:假设我们抽象一个叫做冲击力的概念出来的话,爆炸无疑是冲击力很强的行为。因此我们可以在游戏底层设计一套冲击力机制,所有的攻击判定都有对应的冲击力等级,这样就等于创造了一项隐形的元素。这样我们可以设计需要爆炸才能炸开的石头墙,但它不会爆炸成为让简单的钥匙对应门的关系,而依然符合元素系统的设计初衷,因为只要是达到了类似爆炸等级的冲击力攻击,都可以炸碎这一堵墙,比如Boss的攻击,比如后期才会提供给玩家的更强的技能,比如场地机关或道具,比如将来的其他元素反应结果。这样我们就用一套新的元素系统避免了爆炸的单一性,实现了涌现性。
如果遇到了钥匙对应门的简单对应关系,尝试设计更底层的系统兜住所有的机制,打通它们。让玩家可以理解其中的规则,并利用系统。
最好找到优秀的平衡点。像是上面的毒火爆炸其实并不是一开始我们就意识到如何去设计它的平衡性的,都是在考虑其各种表现的结果后统一实现的。比如我还设计了一种专门的敌人,它身上会带有毒气,玩家难以近战应对它。前期玩家需要利用风元素短暂吹散它身上的毒气,然后利用其他手段战斗,中期获取近战火技能后虽然可以一放技能就炸对方一大罐血,但同时也会炸到自身。此时理解机制的玩家,可以驱散敌人毒雾后点燃敌人,然后远离,敌人重新覆盖毒雾后会直接触发火焰爆炸把自己炸死。这就是中期对于乐于思考的玩家的奖励。而后期拥有了远程火技能,这个小怪用毒创造的限制就完全变为了玩家的优势,直接无脑秒杀,利用机制来让玩家感受到自身成长的强大。
元素反应的前提是每个物体需要清晰的记录自己处于什么“状态”,我们可以用Buff的方式去理解这些状态。
然后我们可以建立其一个元素反应关系表,表格的基本逻辑是,当有一个新的元素要被加入的时候,它有几种元素反应公式(配方)?每一种配方按照优先级顺序排序,配方包含产出什么Buff,需要角色同时具有哪些其他Buff才能产出?
比如上图,燃烧状态首先检查角色自身是否潮湿,湿了就进入蒸汽状态;否则检查角色是否被冰冻,是就进入潮湿状态;否则检查角色是中毒,是就进入爆炸状态。这样玩家也就会发现,角色潮湿或者冻结的时候中毒,被点火不会爆炸欸。这算是比较符合直觉的。
而对于Buff管理,因为我们游戏中不仅仅是玩家或敌人会有Buff,所有能参与到元素反应系统的物体都会有Buff。因此我们可以建立统一逻辑的BuffRuntimeEntity,挂载到所有需要参与元素反应的物体上,同时我们也可以建立通用的组件化代码,将温度传感器 毒雾传感器 受击Box等这种检测器直接与BuffRuntimeEntity建立连接关系,避免重复编写代码。
像这样,编写一个专门联通BuffRuntimeEntity(Buff Runner)与毒雾传感器的代码模块。这样如果我们需要让毒雾传感器直接向Buff Runner报告状态,就只需要加上它就可以了。而对于部分需要对中毒逻辑单独处理的物体,就可以不添加这个连接模块,编写新的代码即可。这样传感器与Buff管理器之间也解耦了。
在Buff Runner中我们编写各个Buff添加入角色/物品的Buff池的添加逻辑,处理好元素反应即可。接着再用委托的方式,解耦的调用各个Buff被添加到不同物体上的效果。
同时,我们利用建立一个Buff Manager来统一管理所有的Buff Runner,不然每个Buff都要分别在每一帧检测自己有没有需要更新的Buff。可以当一个Buff Runner身上有Buff时才向Buff Manager进行注册,由Buff Manager进行统一调度管理。
另外因为Buff具有大量的计时需求,最好的做法应当是每一个Buff记录自己的过期时间,每帧更新时只需要比对游戏时间与过期时间就好了,不需要每帧单独的更新一个Buff的Timer。
评论区
共 1 条评论热门最新