一、现象
最近在优化树场的爆炸室,使用的是纯牛奶型爆炸室BV1UK4y1v7pc,即斜角黑曜石爆炸室。

tnt在里面爆炸
特点是爆炸室外部的实体的爆炸接触率(伤害判定要素之一)为0(表现为尽管在tnt范围内但不会受到击退,且每次仅受到1点伤害),但能炸掉外部对角线上为主的方块(当然tnt对于方块的破坏能力还是受到了一定的削弱)。至于为什么会有这个效果和mojang神奇的tnt对实体和方块破坏的判定有关。
但是经过测试,事实却有微小的出入。
测试的结论是:不论tnt在这个爆炸室内的哪个位置(是否矫正),外部西北角的贴着角的部分实体仍然会受到大于1点的伤害。而其他三个角则不会。
这些实体包含:蠹虫、兔子、物品实体等等,均可以自行复现。让它们贴着最靠近tnt的角落即可。为了更直观的感受这个效果以及范围大小,我用命令方块召唤了若干物品以供实验。
图中标出了tnt的范围,其中右侧为西北角,左侧代表其余三个角,用颜色指代对于物品的影响:
受到大于5点的伤害、受到3点伤害(四舍五入)、受到2点伤害(四舍五入)、受到1点伤害(四舍五入)、受到伤害但不被击退、无影响

tnt爆炸前
tnt爆炸后,从物品被击退的情况可以直观看出爆炸接触率,我们发现西北角的物品确实没有那么安全,中间段无人幸存。此外爆炸上方的范围似乎比下方稍大。

tnt爆炸后
其实这个结果也算是意料之中的,因为tnt位于正中的话,tnt的爆炸中心则比位置稍高,经过测算这个值是0.061250001192093。目前的1.14+正式版本中tnt的爆心比中心高出的量都是这个。(约等于0.98/16)
那么我们就索性让爆炸中心位于正中,排除这部分影响,得到了如下的结果。

tnt爆炸前(爆心正中)

tnt爆炸后(爆心正中)
现在能够得到基本对称的图案了,上下的范围基本相同。也就是说这个偏移还是以X、Z的方向性为主。
再次总结一下的话:斜角黑曜石爆炸室对于特定小体积的实体无法做到完全保护,其中西南角的实体会被爆炸明显影响(最高8点伤害)。且这爆炸的微小矫正无关。
经过测试,creeper的爆炸也有类似的效果。(实验中没有creeper受到伤害)
那么,这个结论有什么用呢?答案是可以在安排此类爆炸室的时候,尽量将流量最小的角落安排为西北角,这样可以降低物品损失。
二、解释
根据fallen提供的反编译过的源码,我们得以完全地理解这种现象背后的原因。
先放一下源码,然后进行解释。
这段是tnt检测实体的爆炸接触率的源码

爆炸接触率检测
接下来用通俗易懂的语言来解释一下它的含义
对于一个实体和一个爆炸源,进行爆炸接触率检测是这样的:在实体内部取若干检测点,然后进行爆炸中心与检测点的连线,最后不被遮挡的线所占比率就是接触率。而取检测点是这样的,比如在先在X轴上取若干等距离间隔的点,然后取过这一点且垂直于X轴平面即为检测面。而XYZ三个轴对应的检测面的交点就是检测点。就好像是切豆腐,横着一刀,竖着一刀,还需要转90度再竖着一刀,那么中心三刀就产生了一个交点,这就是检测点。这些交点的特征是:X、Y、Z坐标分别于对应轴的检测点的X、Y、Z坐标相同。
而爆炸检测箱则是这些检测点连线形成的长方体的外表面,可以看作是在爆炸眼中的实体。
先取得实体的碰撞箱三维边长,假设分别为X、Y、Z。(所有实体的X、Y、Z轴的边长只有一个值,也就是说必然是长方体碰撞箱)
然后对XYZ分别定义一个常数,为ΩX=2X+1。 ΩY、ΩZ同理。
解释一下这个常数的含义,对于ΩX而言,X/ΩX就是X轴分段面(点)间的距离,而当我们把它向下取整得到[ΩX],得到的就是对应轴分段之间间隔的数量,也就是对应轴所取的检测面(点)的数量-1。
不过其中发生了向下取整这种事情,于是mojang:我来矫正一下,定义了变量ΔX与ΔZ,而这个ΔX=0.5*(ΩX的小数部分)*X÷ΩX。(y轴没有矫正)
这玩意是怎么来的呢?
来画一个图,只考虑X轴

图例
也就是说ΔX是分段后得到的空位的一半,我们将这个分段点集体向右边(正X)移动ΔX之后,分段点就是对称的啦,这样就不会出现一些奇奇怪怪的问题。
但是,上面所说的只是mojang所预想的,实际代码出现了纰漏。
mojang取得的ΔX少乘以了一个X,也就是说这个其实是个比例,而不是最终值。但是天才的mojang直接把这个错误的值加入,并且对所有点进行了整体移动,随后就发生了神奇的事情:实体的检测点可以位于实体的碰撞箱之外。
这就是导致西北角物品被炸死的根本原因,我们来把物品实体代入进行计算。
物品实体的X、Y、Z均为0.25,那么对应的ΩX、ΩY、ΩZ就是1.5
取整后为1,也就是[ΩX]=1,仅仅有一段间隔,间隔X/ΩX=1/6,每个轴两个检测面,一共8个交点(检测点)
而此时理论上的ΔX=0.5*(ΩX的小数部分)*X÷ΩX=1/24
也就是说本来只要向X轴正方向偏移1/24就能让检测箱中心正好位于物品X、Z轴的中心
但是mojang的错误导致我们少乘以了一个X,那么得到的错误结果就是1/6
由此,我们来看看实际的检测面在哪里,首先从物品碰撞箱X最小的地方开始,计算偏移量,即1/6,然后取了两个面,第一个面就是在1/6处,第二个面要加上间隔,也就是1/6+1/6=1/3。好的,我们的得到了X轴检测面的位置,也就是物品碰撞箱X最小的面向X正方向平移1/6以及1/3。但是有一个小细节,就是物品的X轴其实只有1/4长啊!这表明平移1/3之后,这个检测面上的任何点都在物品的碰撞箱之外。
这与测试的结果西北角出事恰好符合,物品在爆炸的西北角,所以物品的东南角贴着墙。然而由于检测点偏移,物品的检测点跑到了爆炸室的里面,一共八个检测点有两个是暴露的,接触率为1/4,自然就被tnt毫不留情地炸死了。
3、验证
我们还可以根据wiki给出的公式,由此算出贴墙的物品受到的最大伤害:
爆炸冲击力F=(1-0.884/8)*1/4=0.222
伤害=F*(1+F)*7*4+1=8.61
实际情况中物品收到的伤害大约为7、8,也基本符合。
此外,根据之前算出的偏移,我们知道检测点偏移1/3而物品总共就长1/4,因此超出了1/12,如果往回移动1/12,那么检测点就能不跑到爆炸室里面从而导致物品被炸死。经过实际测试,也是符合的,如果把物品往外移动0.084,则刚好能不被炸死。
4、结论
最后我们知道了这个神奇的事情的发生机理,也就是mojang代码的纰漏,同时对tnt对实体的检测有了更深的了解,知道了爆炸检测箱在X、Z轴的偏移。
此外,通过上面的代码我们还可以看出Y轴由于不加任何偏移,因此检测箱永远是贴着碰撞箱的底面的,也就是说任意实体的检测箱都是靠下的,这个也可使用命令方块进行验证。(实际情况中我们床炸可以用往面前放方块来大量减少伤害,也有检测箱偏下的功劳)
以下为验证情况,两个例子中,tnt的爆心分别位于黑曜石的上、下表面所在的平面,也就是说物品分别只会受到来自上半球、下半球的爆炸射线。红箭头表示TNT炸过来的方向。

上半部分露出来,没被炸死

下半部分露出来,露出长度相等,被炸死
由此,我们也验证了检测箱偏向Y负。
最后,根据已有的公式,还可以算出实体X、Z轴的长度对检测箱X、Z的偏移情况的影响。
以X轴为例,其实就是偏移量少乘以了一个X,如果完全不偏移那么就是检测箱贴着X轴最小的那一面:
当0<X≤0.5时,检测箱严重向X正偏移,存在检测点位于碰撞箱之外。这类实体在西北角都会被明显炸伤(如蠹虫、小鸡、正好是0.5的鹦鹉、幼年海龟、小史莱姆)
当0.5<X<1时,漏了X的影响表现为偏移量偏大,因此向X正偏移,不过不至于出现超出碰撞箱的情况。
当X小于1,当X越小,偏移越明显。
当X=1时,漏了X正好等于没有漏了X,所以不偏移。mojang修复bug会不会是把所有实体碰撞箱边长改为1呢(滑稽)
当X>1,漏了X的影响表现为偏移量偏小,因此向X负偏移,不过这个偏移量也不会导致检测点超出碰撞箱,毕竟起点就是碰撞箱。当X越大,偏移越明显。
最后,相信mojang能在1.18修复这个bug,还大家一个完美的纯牛奶爆炸室。