谈谈MC战斗(三)
上次我谈到衡量击退的价值,所以我做了个打点计时器。改进之后,计时器还会记录目标与我的高度差。
结论:
击退的XZ速度,是击退力度与原本XZ速度一半做叠加。因此,原本静止的物体受到0.4力度的击飞后,会在XZ方向以0.4的速度飞出。
玩家近战攻击生物时,击退的方向是两者中点的连线。
生存模式下,近战攻击距离为3格,修改REACH_DIST属性并不增长攻击距离(但理论上可以缩短);创造模式下影响,但可以忽略。
生物落地一瞬间会出现速度异常,加速后退。原因不明。
对于僵尸这种移速属性为0.23,实际移速为0.115的生物来说,一次0.4的击退,会让他最多倒退1.25格左右,大约花24刻它才能走回被击退的位置。
有一些事情必须先想清楚,那就是,MC的物理系统与现实有一定差别,这差别来自多方面:
MC的时间并非连续的,而是按照刻(tick)来的。比如某个物体第一刻在(0,0,1)的位置,下一刻在(0,0,2)的位置,那么我们并不能说物体“在1.5刻时处于两者之间”,因为根本就不存在1.5刻。这与现实世界有1.5s、1.05s有很大不同。在进入量子力学的尺度之前,现实世界的物理是连续且可分的。MC在前端会做一个现实上的插值,但在实际计算碰撞的时候,很容易发生穿透——你玩游戏的时候之所以不会有明显的感受,都是因为后台做了弥补处理。
MC具有阻力系统,所以并不是机械能守恒的。不过这个倒也很好说,看一眼它的阻力机制就知道了。不同的实体会有不同,是否处于水中也会对阻力有影响。
机械能守恒等在连续世界里,靠积分分散成立的东西,在MC里只能用来做近似估计。比如,你想通过起跳速度
来计算跳跃高度的话,那么你计算出来的只是近似高度,结果必须用excel等时机推演一下。比如,如果重力加速度是10,你以10的速度起跳,那么在连续世界里,
,但实际上,你会往上移动10格。因为,你的速度在这一个tick内都不会变化,所以直接上飞10格后停在空中,然后又10速下落了。
在这样一个不连续的世界,我们要如何定义“速度”?那必然是”下一刻和这一刻之间的位移”。为什么不是这一刻与上一刻的位移?因为在代码里有motion等变量,它们会指导这个物体下一刻移动到哪里,这样与编程里的概念对应较好。至于编程里为什么那么定义,我觉得但凡自己写过游戏里的运动系统的人都会知道,就不赘述了。
在现实世界里,速度的量纲和位移并不一致。另外,MC里的速度其实也有两种,一种是“格每秒”,一种是“格每刻”。本文讨论的基本都是“格每刻”。并且,假定MC里的方块边长为1米,这样说起来省事。

那个很尖锐的灰色线绘制在右边的副坐标轴上,蓝色和黄色的圆滑线绘制在左边的主坐标轴上。
黄色的线是僵尸和玩家的水平距离,蓝色线是僵尸与玩家的垂直距离,灰色线是僵尸相对玩家的水平速度。
上次就很奇怪为什么玩家的速度会有一次抖动,这次结合了垂直距离可知,僵尸在接触地面之前,往后倒退的速度会略微变快。

是的,还没接触地面,提前两帧左右,倒退的速度却自行变快。这怎么也讲不通。倒退的速度增加,本来只有被击退的第一帧而已。
我们都知道,在现实世界里,人类没法直接二段跳,忽略空气阻力的话,在空中怎么摆动身体都没法扭转重心运动的路线。但是,在MC世界里,你在空中按方向键的话,是可以控制水平速度的。
所以是僵尸自己控制了速度导致吗?那也说不通。如果是僵尸开始努力的话,不应该往后倒退得越来越猛,毕竟僵尸是朝着玩家扑过去的,而不是被吓得使劲往回跑。而且,这个事情必现,不是因为随机因素导致。在这里记录一下,留给以后看代码分析了。
看到这,我需要研究一个问题,那就是,我对僵尸的击退,会不会打歪。
如果你看过代码的话,你会知道,多数普通的attackEntityFrom都会造成力度0.4的击退。那么,玩家攻击的时候,击退的方向是玩家与怪物的连线,还是玩家的视野方向呢?

d1是两者的x距离,d0初始是两者的z距离。
这个怪异的循环是在干吗?他是在施加随机扰动,如果两者在xz平面的距离小于0.01,他就会把d0和d1随机设置一波,一直随机到大于0.01。这代码写的有点奇怪,for括号里的第三部分完全可以提取到循环体内,第一段完全可以提到循环外面,整体写成while循环才是正常人的写法。
然后,我们按照xz距离把参数传进去,进行击退。也就是说,如果两者距离大于等于0.01,那么击退就是以中心连线算的,并非按照玩家视野朝向。所以,我们忽略了两者连线的切向速度,这个研究方法并没有问题。(补充:击退附魔则是以玩家视野方向算,与此不一致,代码分析略)

他被打之后,水平速度只有0.34,算上垂直速度也只有0.36。
0.4力度的击退为什么击飞后,目标的速度不是0.4呢?
我们看看击退的代码。
物体的原本x、z速度折半,然后把strength按照xz参数作为方向参量正交分解为速度。
也就是说,如果物体原本在x、z方向不运动的话,那么你对他进行0.4力度的击退,它的XZ初速度必为0.4。
僵尸原本在XZ平面以0.115的速度向玩家靠近,受到了一个0.4力度的反向击退,得到的新速度是0.4-0.115/2,约等于0.34,理论计算与实际吻合。Y方向独享一份,就不管他了。
好了,这一部分理清楚了,那么,到底一次击退有多大的价值呢?

平均几次的实验结果来看,我们可以认为,一次击退大约能击退1.25米,生物想要恢复原本的移动速度大约需要20刻,但想走回被击飞前的位置,大约需要24刻。也就是说,如果你近战,稳定地用剑1秒(20刻)攻击一次僵尸的话,僵尸身后只要有个1.5m的空间往后飞,那么你可以稳定地确保僵尸离你越来越远,直到你打不中。
查看代码可以得知,玩家默认的REACH_DISTANCE为5.0,也就是说只要不超过代码里的额外限制的话,玩家可以攻击5米内的敌人……吗?
上一段NetHandlerPlayServer的代码。
如果你能看得见目标的话,那么你可以够到6m(36的平方根)的怪物;如果你看不见的话,只能打到3m的怪物(9的平方根)。即使你的REACH_DISTANCE再大也不能超过这个限制。
至于“能看见”的话,查看EntityLivingBase::canEntityBeSeen的代码:
这就知道了,只取决于视线上有没有实体方块挡住,有就会变严格。有人说了,那我视线被挡住,隔着墙不是根本打不到吗?实际上,因为延迟和动画插值等缘故,前端和后端可能有偏差。这里只是各自算各自的,前端判定了之后,后端再次判定罢了。
也就是说,普通情况下在开阔地带,玩家有5米的攻击距离,但是有了栅栏等在中间阻碍的话,3米的距离才比较稳妥。果真如此吗?实际上,我把自己用命令方块精确传送了一下,但是只能打掉三格以外的盔甲架。REACH_DISTANCE的含义还有待商榷?代码里,这玩意看起来确实是用来处理方块触及的。


严格来说,他确实先用触及距离做了判定,但是之后如果目标超过3格,目标实体就会被清掉了,设为MISS。
可我明明记得修改触及距离对攻击范围有效啊?所以我打开游戏,挂上我的阵地做成附魔。实测发现,在创造模式我能打五个,手变长后我可以多打一个盔甲架,但是生存模式,仍然只能打3格的盔甲架。好吧,那就是没有效果了。真遗憾,谨记3这个前端参数把。不过,前后端的不一致是不是给挂端留下了空间?
至于倒退速度的异常成因,以及对其他生物的分析,就留待下一篇记录了。到时候打断点看看代码。

