【代码整理】MW马里奥动作参数与原理
【前言】1.发这个贴的动机是,昨天zqh问我MW的跳跃机制是怎样的,我就去查了一下相关的代码,发现了一些有趣的东西,所以打算把MW的马里奥动作代码进行解读,翻译成人话,跟大家分享分享。
2.具备高一程度的物理知识可能更有助于你理解本帖的内容。
3.波兰大叔的代码实在写得不堪入目,得带个波兰语翻译器去理解变量名和注释,另外还混杂了das后来打的补丁,所以我的解读可能并不完全准确。
零、基本知识
这一节主要讲解游戏运行的一些基本机制,以及位移、速度、加速度这些物理概念是怎样体现在游戏当中的。有过MMF或类似的游戏编程经验的朋友应当对这一节的内容较为熟悉。
(1)帧与代码
MW中的基本时间单位是帧。每一帧代表一幅静止的画面,帧的快速切换就构成了动画。而游戏过程中运行的代码,其实就是每帧执行一次的命令,这个命令告诉游戏程序,下一帧该做些什么,该呈现出什么样的画面。所以,只要读懂了代码,我们就能大概推知游戏的运行逻辑了。
(2)物理建模
游戏中的许多内容,比如我们即将要讨论的,马里奥的移动和跳跃,是以现实中的物理法则为原型的(尽管它并不完全拟真),这使得我们可以用一些物理学概念来讨论游戏中的机制和现象,或者反过来说,用游戏中的机制去模拟物理法则。这就是物理建模。就本帖而言,无非涉及到三个概念:位移、速度、加速度。下面就来简单介绍这三个概念是怎样在游戏中被模拟的。
1)位移:游戏中的每一个对象都有其对应的坐标,分为x(横向)坐标和y(纵向)坐标,刻画了对象的所在位置。通过代码可以实时改变坐标,这就形成了物品的位移。距离单位为像素。
2)速度:速度是指单位时间内的位移,翻译到游戏中,就是每帧的坐标变化。一般来说,速度也是按x方向和y方向分开讨论的,且分正负,我们约定,正的x速度代表向右的速度,负的x速度代表向左;正的y速度代表向下,负的代表向上。(注意:速度也是可以每帧改变的,它不一定保持不变。)
例如,马里奥的x速度是3,则代表马里奥每帧向右移动3像素。如果这个速度一直保持不变,那么,64帧之后,马里奥就向右移动了192像素,也就是6格。
再比如,马里奥的y速度是-3,则代表马里奥每帧向上移动3像素。但如果这个速度在下一帧就变成了-2,那么两帧之内马里奥的总位移是向上5像素。
3)加速度:加速度指单位时间内的速度变化,也就是每帧的速度变化。在现实中,牛顿第二定律告诉我们,加速度源于外界对物体施加的力。我们并不能直接改变一个物体的速度,而是通过改变对物体的力,来间接改变其速度的。在游戏中,也会有类似的现象:我们按左右方向键时,本质上改变的是马里奥的水平加速度,从而间接改变马里奥的速度和位移。
举个具体的算例:
假设马里奥第0帧的x速度为-1,加速度始终是+0.5,那么之后4帧,马里奥的速度分别为:-0.5、0.0、+0.5、+1.0,第1~4帧的总位移是-0.5+0+0.5+1=1像素。
这相当于一个“转身动作”,马里奥一开始向左,然后我们按右键使马里奥产生了向右的加速度,马里奥的方向从左逐渐扭转为右(注意,并不是立即扭转!而是在加速度的作用下,2帧后扭转为右。我们并不能直接让马里奥转头。),最终,第1~4帧内的总位移结果是,向右移动了1像素。
一、马里奥的横向移动
1.玩家施加的加速度
控制马里奥横向移动的无非两个键,左方向键和右方向键。当然,可能还要考虑加速键,那就三个吧。
正如上一节所说,我们的按键,决定的是马里奥的加速度。系统会响应玩家的操作,把马里奥的加速度立即调整到对应的值,然后驱使马里奥的速度发生改变,最后就决定了马里奥的位置变化。来看例子:
非游泳状态,松开加速键、按住左键时,马里奥的加速度将被设置到-0.1。于是,从马里奥起跑开始算,10帧之内,马里奥的速度分别是:
-0.1、-0.2、-0.3、-0.4、...、-1.0
马里奥总共向左跑了5.5像素。
作为对比,如果一直按住加速键,这个加速度将被设置成-0.3。相应的10帧之内速度:
-0.3、-0.6、-0.9、-1.2、...、-3.0
马里奥总共向左跑了16.5像素。这比不按加速键的情形快多了。
左键和右键导致的加速度绝对值是一致的,无非是方向的差异,下面就不做刻意区分了。不同情况下,按住左/右方向键的的加速度大小如下:
松开加速键,非游泳:0.1
按住加速键,非游泳:0.3
游泳状态(无论是否按加速):0.05
从操作手感来说,加速度越大,意味着改变速度的能力越强,会显得惯性越小(所谓惯性就是原有速度难以被改变)。从此来看,游泳状态和非加速状态下的惯性很大,而加速状态下的惯性是适中的。但为什么还是会觉得MW脚滑呢?请往下看。
2.最大速度
细心的朋友会发现一个问题,如果我们一直按死加速键和右键,马里奥的加速度一直是0.3,那么他的速度应当会不断膨胀,超出接受范围。但事实上并非如此,就像跑步,一开始起跑时速度不断增加,过一会儿就到了极限速度,不能再增加了。在游戏中,设定这样的极限速度也是必要的,因此,游戏在每一帧中会检查马里奥的速度,一旦到达某个峰值,就不让加速度影响速度了(虽然加速度仍然是0.3,但速度不受它影响,从而不变)。各种情况下马里奥的最大速度如下:
松开加速键,非游泳:3
按住加速键,非游泳:8
松开加速键,游泳:1
按住加速键:游泳:3
3.自然刹车
我们知道,刹车有两种手段:
一是按反向方向键。根据上面的说明,我们知道,这时候给马里奥施加了一个反向的加速度,使得他的速度逐渐减少。
二是松开左右键(或者两键都按,后面不提这种古怪情况了。)。这时,上面的解释就显得不够了:不按任何键,我们没有给马里奥施加任何加速度,他的速度应该保持不变才对啊?这显然违反常识。在现实中,摩擦等因素导致的阻力,会让一个已经被推出去的球慢慢停下来;游戏中也是如此,所以要提供一个伪造的“摩擦力”。实现起来也很简单,那就是,当系统检测到玩家松开了左右键的时候,自动给马里奥降速,直到马里奥完全静止。特别的地方在于,这个自然刹车过程并不像前面那样,模拟现实物理中的加速度对速度的影响,而是采取了一个等比递降模型:
下一帧速度 =当前速度 / 刹车系数
这个自然刹车系数,非游泳状态为1.05,游泳状态为1.03。所以,同样的速度,在游泳时松开按键,会比在陆地上花更久的时间让马里奥停下来。(然而水中的速度本来就很小,所以这个差异不是很明显)
4.小结
上面三点概括了马里奥水平移动的大致机制。其中涉及到的参数,可以总结如下:
游泳状态 加速状态加速度 最大速度自然刹车系数
否 否 0.1 3 1.05
否 是 0.3 8 1.05
是 否 0.05 1 1.03
是 是 0.05 3 1.03
根据这个表,我们就可以解释一些现象了。
1. MW不按加速键几乎什么也做不了。这是因为,不按加速键时,无论是加速度还是最大速度都太小了,限制了马里奥的横向移动能力。另外,在下一节中,我们还会说明,马里奥的横向速度还会影响其跳跃高度。1' .不按加速键,其实也并非啥也做不了。惯性大不一定是坏事,比如微操的时候,惯性大就体现为,马里奥可以进行很细微的移动(因为速度提不起来,只是很慢地挪动),这时松开加速键就很有用了。这就是为什么MW能够进行大量微操的原因。
2. MW的手感“很滑”。这是因为按住加速键后,加速度适中,最大速度很大。前者导致,玩家会明显感受到“加速”这个过程,而不是马上提到某个速度,会有滑滑的感觉,但事实上仍然可控;后者导致,当马里奥处于满速时,这个加速度不足以让他马上停下来,这是让人觉得惯性很大的根源。 这是一把双刃剑:对于不熟悉游戏手感的玩家,他会经常失控;但熟悉了机制之后,适中的加速度赋予了玩家对马里奥较强的控制力,只要操作足够精准即可,而更高的最大速度则让马里奥跑得快,跳得远。
二、马里奥的纵向移动
跳跃是马里奥游戏的灵魂。跳跃”手感“的微妙差别,都会影响关卡设计与玩家体验。
1.速度公式
我们先给出马里奥纵向速度的速度公式。注意,这里的纵向速度,指的是马里奥起跳后、落地前的纵向速度,正值表示向下,负值表示向上,以后不再赘述。
纵向速度 = 动态速度 + 0.2 * 关卡引力值
1)动态速度是一个实时变化的量,它的具体机制我们稍后再说。动态速度仅在它为正值时存在大小限制,具体而言,马里奥在空中时,最大动态速度为13,而在水中时则是6。这就保证了实际的下落速度不会太大。(但是,上升速度并无如此限制!)
2)关卡引力值就是我们在制作MW关卡时所设定的那个”引力值“,它相当于是始终加在动态速度后面的一个常数值。(这就和现实物理法则不太一样了,所以这个“引力”可以打上双引号)
仅从这条公式出发,我们已经可以推断出好几个有趣的事实了:
1.当引力值为5,马里奥的空中最大下落速度是14;而引力值为-20时,最大下落速度则是9。这就说明了为何在低引力关卡中下落时会有“轻飘飘”的感觉。
2.当引力值为-40,马里奥的空中最大下落速度是5,但水中纵向速度始终是负值,不超过-2,这就导致马里奥在空中向下落,但在水中向上浮,最终被夹在水面上,形成“死海漂流”现象。计算可知,只要引力位于-30和-65之间,都会引发死海现象。
3.当引力值低于-65,马里奥的空中纵向速度也始终为负,马里奥被强迫飞升,飞行关卡就诞生了。
2.起跳瞬间
前面说过,左右方向键直接改变马里奥的横向加速度,从而间接改变马里奥的速度和位移。而起跳的瞬间,则可能是唯一一次直接改变马里奥速度的情况。
具体来说,当马里奥站在地面(或者游在水中),按跳跃键的一瞬间,其动态速度会被立即按如下公式进行设置,从而使马里奥跳起来:
起跳瞬间动态速度 = -(基础值+陆上绿果增益+横向速度增益系数*瞬间横向速度)
注释:
基础值:陆上时为8,水上漂时为6,游泳时为4
绿果增益:陆上绿果状态时为1,其余情况为0
横向速度增益系数:陆上或水上漂时为0.2,游泳时为0.1
从这个公式,我们同样可以解读出一些有趣的信息。
1)绿果的优势在于比普通状态的跳跃初速度要大。结合第1节的公式,可以发现,起跳瞬间的实际效果,引力5关卡的绿果状态=引力0关卡的普通状态
2)水上漂或者游泳时绿果没用
3)我经常吐槽引力5的水关太难受,原因便在于水下基础值太小,导致马里奥不好游起来,可以通过调整引力值解决,我个人推荐3
4)助跑跳是有道理的。跑得越快,跳起来时纵向初速度越大,也就会导致跳得更高。陆上横向满速是8,代到这个公式里面,满速助跑跳和原地跳相比,初速度大了1.6,这甚至比绿果增益更明显。
3. “真引力”与滞留加速度
当然,上一节的动态速度仅是起跳一瞬间的事情。跳起来之后,速度将会如何改变呢?
众所周知,如果你只是在起跳瞬间摸了一下跳跃键,然后马上松开,那么马里奥几乎跳不起来,马上又落回地面了;而如果按死跳跃键,马里奥则倾向于滞留在空中,跳跃高度相当可观。可见,“跳起后按住跳跃键”这个行为。对马里奥的运动产生了显著影响,这就是所谓的滞留加速度。在讨论这个概念之前,我们需要先谈谈“真引力”的问题。什么是真引力?就是马里奥在空中时,为了模拟重力而提供的一个向下的加速度。它是马里奥跳起后会下落的根本原因。这个名字是为了和“关卡引力值”这一“假引力”作区分。
这个加速度,水上为1,水下为0.2。水上真引力非常大。
试看一例:
引力为5的关卡,绿果状态,满速起跳,实际初速度为-(8+1+8*0.2)+1=-9.6
如果马里奥在空中只受真引力影响,那么接下来逐帧速度分别为:
-8.6、-7.6、...、-0.6、0.4、...
不到10帧,实际纵向速度降为0,这意味着马里奥已经到达了跳跃最高点,开始下落了;再过不到10帧,就落地了。这是很短的时间。
这个基本上就是跳起后不按跳跃键的效果。
这时,起跳之后跳跃键的功能就呼之欲出了:按住跳键时,只要马里奥还在上升,就会提供一个向上的加速度,用来抵消一部分真引力。(当然,水下并没有这个功能;这仅仅针对水上情形。)我称之为滞留加速度。
这个加速度的大小是0.75,一抵消,就可以知道,按住跳键上升时,马里奥受到的向下加速度仅为0.25,是原来的四分之一。
换句话说,按住跳键和不按跳键相比,速度衰减效率是原来的四分之一,马里奥的跳跃上升时间是原来的4倍,于是就跳得高多了。更进一步,通过精确控制按住跳键的时间,我们就可以让马里奥的跳跃高度处于这两极之间,也就实现了丰富的跳跃操作。
这是MW马里奥动作的又一特征:真引力很大,而滞留加速度也比较大,从而使马里奥的跳跃高度范围很广。结合前一部分“横向加速度适中,横向极限速度很大”的特点,就可以知道为什么MW是一个难于上手、但上手之后跳跃性能相当强的游戏了。
(注:滞留加速度仅在陆上跳跃上升期按住跳键时存在。下落时按不按跳跃键都没区别,向下加速度都是1。)
4.小结
和上一部分一样,我们来总结一下上面所涉及的参数,比较难以列表,就直接逐个说明了:
基础值:陆上时为8,水上漂时为6,游泳时为4
绿果增益:陆上绿果状态时为1,其余情况为0
横向速度增益系数:陆上或水上漂时为0.2,游泳时为0.1
真引力:非游泳状态为1,游泳状态为0.2
滞留加速度:仅在陆上跳跃上升期按住跳键时存在,为0.75
结合上一部分的表格,就构成了MW的所有基本动作参数。
通过修改这些参数的大小,就能创造出各种不同的手感了。当然,SMWP目前不会考虑这个,手感的更换会对关卡设计造成毁灭性打击。但作为平台跳跃游戏手感的一次探索,这是值得私下试一试的。
三、总结与补充
上面这些基本就是我想和大家分享的内容了。有一些没提到的内容,下面稍作补充。
1)碰撞判定与穿墙处理
上面的整套物理建模其实并不是平台跳跃游戏代码中最蛋疼的地方。从技术上说,最麻烦的地方其实在于碰撞判定与穿墙处理——如何判定马里奥撞墙、落地、顶头、顶出隐藏砖等等情况?马里奥要是不小心卡进墙里,游戏要如何响应?波兰大叔在这方面的代码相对简陋,于是,一大堆穿墙bug就诞生了。后来添加的防穿墙模式,是das采取了较为现代的碰撞判定方法所得的成果。这套方法的基本思想是“先探路后执行”,每帧计算出马里奥的速度之后,并不会立即按照该速度对马里奥进行移动(因为有可能直接移动到墙里),而是先让程序检测该移动是否会导致卡墙,如果会,则进入一段修正程序,把修正之后的计算结果提交给主程序,这时再让马里奥真正执行移动,也就不会卡进墙里了。(当然,这个描述还是过于概括了。真正实现起来依然有难度。)
2)关于顶头失速和撞墙失速
玩过MF的都知道MF的手感有点“硬”,与MW形成鲜明对比。除了它的动作参数和MW有所不同之外,还有两个细节值得注意,那就是MF与MW关于顶头和撞墙的处理有所不同。在MF中,顶头不但会把纵向速度置0,还会把横向速度置0;而MW仅让纵向速度置0(否则就往上穿了),但未处理横向速度,所以马里奥仍然会按照原有的横向速度前进。另一方面,MF中马里奥撞墙时,把横向速度置0,因此马里奥必须重新助跑;但MW中并未把横向加速度置0,只是通过碰撞判定,不允许马里奥往墙里挤(体现为马里奥贴着墙还是在“原地跑”),只要障碍消除,马里奥就能按照原来的速度马上往前冲。
最后总结简单说两句,这是第一次在论坛里发比较长的帖子,也比较用心地用加粗和颜色标出重点,说实话花了好几个小时来码字,中间还丢失过一次草稿,导致又把很多内容重新码了一遍。在编排和用词上,尽可能让大多数的人都能看懂,如果还是有不解的地方,欢迎提问。
相当厉害 这得要有对游戏多深的理解才能写出这样的帖子...
大力支持!! 本帖最后由 克洛伊Prime 于 2020-2-21 12:32 编辑
讲得太好啦!!!
另外在我看来MF(RE)的顶头失速是横向碰撞检测的bug(。
马里奥在顶头的时候,横向跳跃彩条碰到了天花板,于是系统认为马里奥碰到了横向的墙,于是速度就没啦。
最近经常看这篇帖子,但是我发现一个问题。
根据你所说,如果引力为5时,绿果状态满速起跳并一直按住加速键,那么初速度是-9.6,加速度是0.25
那么速度将维持39帧(包括第一帧),这样算出来总的上升高度是9.6×39-0.125×39×38=189.15
但这明显和实际不符,因为马里奥应该至少到达224像素的高度才对。
我找了一些情况试了一下,如果初速度没错,加速度应该要略小于0.1才能符合已知的一些极限高度。
所以是不是这个帖子的参数标的有点问题? zqh——123 发表于 2021-3-16 01:13
最近经常看这篇帖子,但是我发现一个问题。
根据你所说,如果引力为5时,绿果状态满速起跳并一直按住加速键 ...
初速度应该是-10.6吧 本帖最后由 zqh——123 于 2021-4-4 20:52 编辑
马里奥奥里马 发表于 2021-4-4 17:03
初速度应该是-10.6吧
但是根据纳秘的例子,他说的是-9.6,我觉得可能想表达的是真引力在第一帧也有效话说-10.6算出来结果正确嘛
zqh——123 发表于 2021-4-4 20:51
但是根据纳秘的例子,他说的是-9.6,我觉得可能想表达的是真引力在第一帧也有效话说-10.6算出来结果正确嘛 ...
-10.6算出来差不多。
页:
[1]