- 0
- 0
- 0
分享
- 如何让玩家爱不释手,在游戏设计中加点儿“意外”
-
原创 2022-06-20
游戏需要制定明确的机制,只有这样才能让玩家知道该如何一步步升级;同时,游戏也需要“意外”,如果玩家在游戏里的行为结果太过有迹可循,很容易让玩家失去兴趣。
在本文中,资深技术游戏设计师 Christo Nobbs 将以例子深入说明怎样给游戏添加随机元素,从而让玩家保持兴趣和期待。
为玩家留下充足的视觉线索
用户可以借助显眼的图像元素来吸引玩家进一步探索游戏系统,催生独特的游戏体验。上一篇文章中(点击 回看),着重介绍了一个由大量树木组成的沙盒世界,这些木头在特定条件下可能会着火并让火势蔓延。游戏设计师在此基础上再给玩家一把斧子,让玩家可以砍树。
假设玩家所在之处完全平整,那他们将难以预测树倒下的方向。如果有些树是那种已经“死了”但还矗立着的枯树呢?这些枯树说不定什么时候就会倒下。这种不确定元素可以为游戏体验带来刺激和紧张感。
你可以用画面来告诉玩家这些倒下的树十分危险,让他们保持警觉。玩家则能通过图像细节来分辨危险的枯木和健康的普通树木,然后自行权衡利弊。没准儿玩家会因此思考:如果直接在已经倒地的树上收集柴火,从而避免被危险的枯木伤到,这样会不会更明智点?
作为一名游戏设计师,我们一定要罗列出这些可能的连锁反应,再围绕着它设计,保证它能顺利发生,比如树木向随机的方向倒下、甚至偶尔着火,这些元素都可以引发混乱但有趣的状况。
善用Unity.Engine.Random来产生惊喜
Unity 的 Random 脚本类是一种生成随机数据的静态类。它与 .NET 框架下的 System.Random 有着同样的名称与相似的功能,但两者之间有着几种关键的不同——比如前者要比后者快 20%-40%。
Random 类所包含的静态属性和方法如下:
value:返回一个(包括)0.0到1.0之间的随机浮点数(只读)
我们在上一篇博文里探讨了“操纵杆”(调试键)的作用,以及如何使用 ScriptableObjects 来储存调试数值。你可以用 Unity 的 Random.Range 设定一个数值范围,用随机生成的浮点数来取代固定的调试数值。此时,包括最大最小值在内的任意浮点数会在每 1 千万个随机样本中出现一次。
你可以使用这种方法从数值范围中抽取出一个用于游戏的值。只要多实验几次,你就能找到最适合游戏的范围,然后再围绕着这个范围进行设计。
随机化的森林冒险
随机元素能让游戏更吸引人。
打个比方,假设树有着 100 点的固定血量,而玩家每次挥砍斧子可造成 25 点伤害。那砍树这件事很快会变得可预测且无聊。即便树木的血量在 76 到 100 点间浮动,每棵树仍是砍四下就倒。不过若血量设在了 75 到 76 之间,则游戏玩法会非常不同,这时每棵树需要被砍三到四下才会倒。
另一种给砍树添加趣味的方法是隐藏血条,用视觉线索来暗示血量变化。这样一来,玩家将从游戏过程中逐渐学习到砍倒一棵树要多久。视觉线索可以增添有限的不确定性,这些不确定性可根据游戏玩法进行平衡和调整。而放弃固定数值、善用 Random 类可让原本单调的任务变成一种有趣的体验。
要想更进一步,你可以让每次挥砍减少 15 到 25 点的伤害。这样一来玩家将很难准确判断砍倒一棵树要多久。他们只得依靠视觉线索来揣测这棵树何时会被砍倒;这些线索可以是砍飞的碎块大小、树桩的切口大小、掉落的树枝多少、树干断裂的音效等等。
玩家不能准确预测树木倒下的时机,但砍得越多,玩家就能猜得越准,并最终提高自己的生存几率。
随机的目的是给玩家带来意料之外的挑战,促使他们权衡风险、争取最好的结果。
接下来我们再来看看几个使用 Random 类的例子。
卡牌游戏中的随机
假设某款卡牌游戏里的 AI 对手只会针对玩家的操作而出牌。如果没有随机性,对手每次出牌都会是固定的,因而也变得容易预测。即便变成了二选一,这样的随机还是太简单,玩家会很快总结出一套规律。
因此,我们可以试着在 AI 的每一步反击上增添一层随机性。如此一来,AI 不会仅仅只从卡池里选两张牌之一打出,或一直优先打出某张牌而放弃其他牌,让整个系统更加复杂、游戏更具动态。
你可以设定几种难度,让不同难度下的对手优先出特定几张牌;比方说,根据预设的值或攻击前的手牌来预测出需要出的牌。如果有一张王牌和其他强力卡牌搭配时可以造成数倍伤害,那对手就可以等到手牌中凑齐了这些卡牌后再打出,给玩家增加难度。你可以通过提高或减少某张王牌与其他强力卡牌一起打出的可能性,来给这种出牌组合设置“权重”。
柏林噪声(Perlin noise)
随机性能以不同的形式出现在游戏中。比如,柏林噪声(Perlin noise)就能根据一串种子值来生成自然的递进噪声,它可以在 Cinemachine中产生更加自然的第三人称跟随镜头。
要想尝试 Perlin noise,可以在资源商店上打开 Starter Assets - Third-person Character Controller 或 Gaia 资源包,或参考 M athf.PerlinNoise 文档。
攻击AI代理
在一次采访中,《光环2》的首席工程师 Chris Butcher 在讨论游戏的 AI 时表示:“我们的目标并不是创造出那种不可预测的东西。这里的人工智能必须有高度的连贯性,可以识别特定的玩家输入。让玩家在做出某些动作后可以预测到 AI 的反应。“
在这个前提下,我们该怎样建立 AI 代理同时保持游戏的不确定性和活力呢?
一种方法是用资源商店的 Starter Assets 和 AI 工具进行实验,比如能让 AI 代理向某个位置移动的 A* Pathfinding Project Pro。
当 AI 代理走向玩家时,玩家觉得自己马上会遭到攻击。但如果它走过来只是为了对话呢?如果我们想要加入更多四处走动、融入环境的 NPC,让周围的环境更有活力呢?这些 NPC 应该线性地收到可以前往的地点,或者根据一套规则并用 Random 类来选取符合逻辑的地点。
假设有一个 AI 代理能用一张较弱的弓向玩家射箭。不幸的是,代理必须站在一定的距离内才能射击,因为它的最大射程是十米。当 AI 站到了玩家面前 10 米处时,便会开火。这样的安排算不上理想,尤其是有第二个弓箭手也想在 NavMesh 上争夺同一个位置时。
要让这个情景更为有趣,我们可以划定一块区域让敌人来靠近玩家。我们能用 Random.insideUnitCircle 实现这点,将输出的 vector2 传入到 vector3 空间坐标的 X 和 Z 轴上,然后利用 RandomRange 在玩家周围划出一块最小与最大值之间的圆。
AI代理选取攻击位置的脚本
AI 代理应该在玩家附近选取一个位置再射击,而不是过于靠近玩家。为了用最少的代码带来更多的刺激,所有 AI 代理都能应用这个脚本,并从多个角度攻击玩家。AI 的行为在一定程度上是可以预判的,代理会在一个固定的距离展开攻击,但你并不能预测攻击的角度。
红点代表了代理将前往的位置
你也可以在此基础上为代理添加近战攻击的能力。用同样的方法在玩家的近身距离内随机取得地点,让代理能在适当的时机选择自己的攻击方式。
玩家知道自己有可能会遭受近战攻击,但从哪个方向来呢?会不会有举过头顶的高位攻击?或者从左到右的横扫攻击?玩家只能通过观察动画上的文字来确定即将来临的攻击。
这时如果有多个敌对 NPC 意图攻击玩家,则整个状况会立即变得复杂。不过在育碧的《Far Cry 2》里,玩家并不会同时遭受所有敌人的进攻。不同的敌人有着各自的攻击时机与方式。
如果你希望在游戏中真实地还原现实动作所产生的结果,你就必须应用复杂、多方面的等式或一个训练好的 ML 代理。最终,这种效果很可能仍然无法契合游戏,并且实行的耗时长、技术要求高,且如果架构得差,要理解、平衡或控制起来会非常困难。
不过在 Unity Random 类的帮助下,游戏设计师们也可以为场景创造逼真的效果、控制游戏的刺激程度,相比于其他方法,这样也能节省不少时间。当然,随机性不一定处处适用,确定性仍然十分重要。但只要在合适的地方、合适的时机善加利用,你就能给玩家带去非常独特的游戏体验,吸引玩家一次又一次地进入游戏。
长按关注
第一时间了解Unity引擎动向,学习最新开发技巧
-
阅读原文
* 文章为作者独立观点,不代表数艺网立场转载须知
- 本文内容由数艺网收录采集自微信公众号Unity官方平台 ,并经数艺网进行了排版优化。转载此文章请在文章开头和结尾标注“作者”、“来源:数艺网” 并附上本页链接: 如您不希望被数艺网所收录,感觉到侵犯到了您的权益,请及时告知数艺网,我们表示诚挚的歉意,并及时处理或删除。