本文是克里斯托·诺布斯(Christo Nobbs)为游戏设计师撰写的系列博文第三篇。该系列将扩展《The Unity game designer playbook(Unity游戏设计师攻略)》的内容,这是一本100多页的深度设计指南,可指导游戏设计师在Unity中建立原型、制作和测试游戏性。克里斯托之前的博文链接可在文末找到。
Unity包含有几种“类型(type)”可用于保存数据,这些数据在平衡系统、游戏性、角色设置、载具概况等方面和设计控制杆一样好用。Animation Curve(动画曲线)就是这样一种组件,它能为游戏设计师和创作者提供更有趣的可能性,尤其是在原型设计上。你可以在项目中用它来控制粒子系统的动画变量,或控制音源组件的递减或其他属性。
一条曲线是一张线状图,它显示了某一输入(X轴)所产生的反应(Y轴)。Unity在多个地方都有使用曲线,尤其是在动画一块。Curve Editor(曲线编辑器)本身带有许多可以利用的选项和工具。
本文将重点介绍怎样用AnimationCurve API编辑动画曲线。修改后的曲线可用于捕捉和存储数据,辅助结果分析。曲线也可兼容ScriptableObjects,正如攻略书中所讨论的,它非常适合用来编辑游戏数据。
你可以在Inspector面板中公开或序列化变量,用动画曲线进行编辑。曲线可以在编辑模式或运行时保存、导出或加载。可编辑切线则允许你控制关键点之间的曲线形状。
关键点创建后,你可以控制点的切线和手柄,调整形状来做出最理想的效果。
像动画曲线这样的设计拉杆可让设计师无须写出复杂的数学公式或缓冲函数就能微调游戏性。
动画曲线的一种用法是在时间轴上为线性运动加入细微差异和细节。以角色关闭车门为例。动作起手,角色的手抓着门把关门时会出现晃动。门一开始关得很慢,然后随惯性速度加快,再突然停止;最后也许还有一次小反弹或上锁扣。所有这些动作都可以保存在一条曲线中,形成序列。
如例中所示,动画曲线适用于随时间推移改变物体属性,并创造出自然的物体运动。其他用法还包括控制物体的移动或旋转方式、角色在进入或结束冲刺时的加速或减速,以及引擎向汽车动力系统的传动。
当你在Inspector面板对新建动画曲线编辑时,你可以通过传入一个参数来求得曲线的值,这类参数在Unity中被称为time(时间值)。在文章后半段,我们将看看在不使用X轴表示时间时,坐标位置为何更显具体。
我们来看看怎样用动画曲线来控制汽车发动机的输出功率。你可以创建一个“伪”发动机扭矩曲线,根据引擎RPM(每分钟转数)返回施加在车辆上的力。如果玩家踩下油门,扭矩便会逐渐增加。
与其划出每个挡位或每种车辆的转速范围,你可以将转速“归一化”或将其设置在0和1之间,然后用“扭矩”曲线保存配置并重新用于其他车辆和挡位。
你可以通过将原始或当前转速除以最大转速来求得曲线的值,即:
normalisedCurrentRPM = currentRPM / maxRPM
为了使数值能在Curve Editor中便于编辑,让曲线可以重复使用,输出功率(Y轴)也将以0到1之间表示。这个值在应用到车辆的动力上时可使其刚体在Z轴上向前移动。
你可以通过在曲线的(0,0)到(1,1)数值范围内为其他车辆取值并产生不同的效果。
动画曲线可以抽象化表达复杂的系统,让你能可视化地控制系统。在发动机的例子中,你可以用上图曲线(代表“伪”变速箱)在加速时添加动力,营造出汽车换挡的感觉——在动力输出时抬高曲线,在挡位转速到顶、动力减弱时再次降低曲线。这对于设计车辆运动的原型是非常理想的,你可以用一条曲线绘制出所有挡位的汽车动力,从第一挡的起点(x0,y0)到第五挡的顶点(x1,y1)。
汽车加速时,发动机的转速通常会逐渐积累,所以本质上你是在制定一段时间内的数值变化,类似于让一个物体进行线性运动。请在游戏设计者攻略书的时间和动画部分了解更多相关内容。
我们再举一个例子:汽车在不平坦的地形上行驶时、在拐弯时、或在车轮上传动时的悬空和运动。你可以用动画曲线来做出逼真的转向、侧向力控制或轮胎打滑,并可视化地微调。
如果你需要为悬浮或轮式载具建立基于射线的机制,在发动机中产生“悬停”效果的常见选择是在每条射线上施加一个向上的力,并使用PID(比例-积分-微积分,proportional–integral–derivative)控制器算法来控制反弹,或者用胡克定律求得阻力。你可以在Unity的“Hover Racer Live 7/21 Cycle 4.2”中看到一个例子,后文还会有一个基于此建立的PID例子。
PID算法是一种控制回路反馈机制,也是一种控制器,它广泛应用于各行各业需要响应性矫正的地方,包括游戏开发。PID控制器会计算出一个误差值,用于表示实际测量的过程变量与期望目标间的差异,同时考虑到弹性方面(或我们给出的例子),它可用于替代胡克定律。游戏中的PID可以用于:
PID可以用在游戏开发中的任何地方,特别是在需要“准确和优化的自动控制”的沙盒和模拟。Squad的《坎巴拉太空计划(Kerbal Space Program)》就使用了PID来让航天器向一个方向推进。
据这项研究所说,在游戏开发以外,“PID调节是最成熟和应用最广泛的连续系统技术”。你可以在这个Control System讲座:PID控制简介中了解更多。
PID算法的编写可能不需要太多时间,但它们需要一定的时间调试才能平衡;这个耗时还会根据车辆的增多而倍增。但在制作原型时,你可以先用动画曲线代替之,从而省下不少时间,免于编写并调试多个PID控制器(曲线最终还是要用PID来取代才能实现最佳的控制效果)。
曲线非常适用于建立原型,它可用于与真实世界的参考实例相比对。在涉及空间计算时,曲线在游戏引擎中呈现出“数学上的完美”,除非额外添加或启用默认重力,世界中不会存在对立的运动力。因此,你可以很轻松地用一条简单的曲线来控制力的变化并产生强大的效果。
在创建车辆悬空时,你可以使用曲线来根据弹簧的压缩程度施加相应反作用力(归一化为0到1之间的数值),不必使用PID来产生阻尼。再结合刚体和少量的阻力,车辆的弹跳摆动会被抑制,悬空也会随负载的变多或变少而变化。
要确定弹簧的压缩程度,你可以用1.0f减去弹簧射线的碰撞距离。不管长度,若弹簧被压缩了25%,则压缩程度将是0.25。你可以将压缩度设为曲线的X值,将其乘以所需的弹簧力(把归一化的值还原成原数值),然后应用到AddForceAtPosition,根据悬空点的数量,在整个循环中施加向上的力。这时,除了Unity默认的-9.81f重力外,你不需要添加额外的向下力。
公式是这样的:
upwardsForce = forceMultiplier * forceCurve.Evaluate (springCompressionNormalized);
rigidBody.AddForceAtPosition(hitNormal * upwardsForce, point.transform.position);
将质量:力的比设为13:110,并应用下方曲线。
载具静止在了压缩度约50%的弹簧上,有恰当的质量值、向上的力和少量的线性和角度阻力来抑制摆动。车会出现反弹和静止,但不会触底,除非从极高的高度掉落或因玩家增加的质量而超载。
为了找到合适的数值,你需要先从y = b^x曲线(看起来类似四分之一的圆)找起。先设置较低的阻力,并将车辆的质量设定为现实中的质量。然后调整向上的力,直到车辆静止在约50%的弹簧压缩量。你可以反复使车辆掉落,检测底盘是否会触地,并观察其在反弹后的静止位置。如果车辆会在不平坦的地形上行驶,你可以用此方法求得悬空系统的弹力,并设置每个牵引点可启用或禁用,快速做出一个可控的悬空系统。
在悬空模型上使用动画曲线可以保证不同的车辆(汽车、货车或悬空较差的卡车)能做出不同的运动,比如那些一直触底的载具可以像街机游戏里那样弹跳,或在拐弯处滚动,在加速和刹车时晃动。如果你还未使用过Unity的刚体系统或自己的悬空方法,可以先将曲线与现有系统结合使用。你可以用曲线进行转向,或增强发动机的动力、悬空、阻力、轮胎抓地力、制动力等。动画曲线是Unity一个非常方便和泛用的工具,可用于在车辆设计过程中添加控制杆,放到Inspector面板中直观地控制各项特征。
上图的一排球体是一串摆放好的刚体,其X和Z Freeze Position属性被设为受限。球体会接受一个基于弹簧压缩量计算的向上力,但每个球体的曲线各不同,我们将其并排放置以更好地进行视觉对比。你可以用该方法为物体找到理想的弹力水平,或者通过调整来平衡自带的反弹性。作为一名设计师,你可以通过操控向上的力来创建抽象化的复杂功能。
曲线作为一种强大的XY轴图表数据,虽然在技术上并不完美,但它可以帮助你快速建立阻尼解决方案的原型,并在Inspector面板中可视化地进行编辑,或在运行时保存为预设。在这篇关于阻尼艺术的博客里,Alexis Bacot列举出了所有“依赖于良好阻尼的东西。像是摄像机、动画、运动、颜色渐变、UI转变,等等等等......它的用处可多哩!阻尼是作品润色的关键。光是它本身就足以影响一款体验是好是坏。”
在文中,他用Unity的SmoothDamp演示了如何制作一种美观的淡入和淡出效果,怎样对目标的变化做出准确的反应。不过,曲线不会像“高级的弹簧减震器那样反弹,这对汽车悬空或假球物理学来说反而很好”——这是动画曲线所带来的一种优势。
当然,曲线除了能作为一种XY数据控制游戏,还有更多的用途。你也可以将其用作一种评估工具,通过调用AddKey Unity API来直观地绘制数据。如果要评估一个随时间变化的坐标值,如车辆悬空时的阻尼或掉落的球体,你可以在方法中加入AddKey(elapsedTime, currentSpringCompression),然后调用该方法并通过InvokeRepeating传入captureResolution作为记录频率。若记录分辨率被设为0.1f,则系统每隔0.1s就会在曲线上添加一个关键点。你可以在检查器中查看结果的缩略图,或打开图表查看完整数据。
我们最后再看一眼掉落的货车。动画曲线决定了多少弹簧压缩度会产生多少力,并生成一个足够贴近的结果,这在第三次反弹时将产生更多的摇晃。你可以比较用动画曲线创建的悬空与PID控制器的悬空效果(详见Unity “Hover Racer Live 7/21 Cycle 4.2”中用到的PID)。两者间唯一的区别在于PID的结果是乘以悬停力而非动画曲线的Y值得到的。
在应用PID并做出大量的平衡性微调后,车辆的悬空感觉上会更加贴近现实参照,且尾部晃动更少,对应的抑制力也更小。不幸的是,一旦车的质量有了变化,PID就需要重新平衡,这一过程会耗费大量的时间。为了设计原型,我们可以借助动画曲线快速、直观地做出效果,并用曲线图分析运动。要想评估应用的PID,你同样可以将运行结果用曲线绘制出来。结果要好得多,尽管第二次反弹略显夸张,但已经实现了轻型大货车的动作和外观。
总结下来,我们在创建车辆运动时,可使用动画曲线来:
除了车辆物理学之外,动画曲线还可以作为设计控制杆,控制原型中的玩家运动、连续攻击伤害等等。作为一个强大的原型设计工具,动画曲线使游戏设计师能够测试各种力,并找出恰到好处的机械“感觉”,还免去编写复杂算法或物理运算的必要。
若你想了解更多的指南和灵感,可在这里免费下载并查看《Game Designer Playbook(游戏设计师攻略书)》。