redstone_machine...吧 关注:3,610贴子:60,846
  • 14回复贴,共1

EM的实验验证和源码解析

取消只看楼主收藏回复

运动计算和爆炸计算不统一 (Motion and Explosion are not uniform),简称EM,对于这个Bug在此贴中将提出一个有说服力的,有支撑的解释,并且从完全不同的方面给出几种有效的解决方案


IP属地:广东1楼2017-03-13 23:55回复
    运动计算和爆炸计算不统一 (Motion and Explosion are not uniform)
    简介:
    简称EM,属于爆炸和运动计算顺序的Bug
    PS:它真的坑了很多人
    定义:
    对于任意几个TNT实体,如果它们具有下面两个特点
    1. 可以因本身的爆炸效果而影响对方
    2. 在同一Gametick爆炸(在此爆炸的含义是变量fuse≤0)
    那么会使这几个TNT实体按照进入实体更新队列的顺序依次让其它TNT实体产生1 Gametick的偏移,再进行爆炸处理,直到在此Gametick没有fuse≤0的TNT实体
    下面将带你从实验计算和源码辨析两个方面来理解和解决EM


    IP属地:广东2楼2017-03-13 23:55
    回复
      2026-02-26 11:45:07
      广告
      不感兴趣
      开通SVIP免广告
      EM的实验验证:
      首先让我们看一个用来演示EM的结构

      按照常理两个TNT实体应该在水中同时爆炸,而不会产生方块破坏
      但是实际上两个TNT实体还是炸掉了这个结构

      这个现象的原因就是EM,这两个TNT集束堆可以因本身爆炸而影响对方,而且在同一Gametick爆炸,所以产生了EM,产生偏移(EM具体作用流程和产生原因随后会说)


      IP属地:广东3楼2017-03-13 23:57
      回复
        关于位移量的实验计算:
        如下图,我们有这个装置来测量这个偏移量,虽然会有一定误差,但是依然可以大致得到一些有用的数据


        就这样,测得三组的实验数据:
        10个推进TNT:7.5 m
        15个推进TNT:10.7 m
        20个推进TNT:14.9 m
        根据TNT实体的X轴和Z轴方向运动的关于时间n(单位Gametick)的运动函数和爆炸效果函数

        可以解得三组关于时间T的解

        这些解都十分接近1 Gametick 所以在误差允许的范围里,不妨就认为EM会产生1 Gametick的偏移量
        当然仅仅靠实验得出的结论是不可靠的,因此我们再看一下EM的源码解析


        IP属地:广东4楼2017-03-14 00:01
        回复
          EM的源码解析:
          首先对于一个实体来说,当它被创建,便会按创建顺序进入一个实体更新队列,每一Gametick里,基本都会按照队列顺序更新一次,所以对于实体来说它的更新是有顺序的,但是它们都是在同一Gametick中,基本无法显示出差异,所以我们均可以认为它们同时更新
          然而很不幸的是TNT实体就是特例之一,多个TNT实体的队列顺序恰巧会影响它们的最终处理结果,这也就是EM产生的原因
          那现在让我们看一下为何TNT实体的队列顺序会影响最终的结果
          下面是net.minecraft.entity.item.EntityTNTPrimed类里的onUpdate方法
          (覆盖了net.minecraft.entity.Entity中的onUpdate )
          用来更新TNT实体的位置和逻辑状态

          部分未标识在图中的变量等含义
          prevPos 是前驱位置
          motion 是运动量,类似于“速度”
          onGround 若所判断的实体在地面上则为true,反之则为false
          fuse TNT实体的爆炸引信时间,初始值为80
          isRemote 若属于客户端则为true,若属于服务器则为false
          可以看到TNT实体的运动计算优先于爆炸计算


          IP属地:广东5楼2017-03-14 00:03
          回复
            接下来再看看“Explode”的源码部分
            net.minecraft.entity.item.EntityTNTPrimed类里的explode方法

            net.minecraft.world.World类里的createExplosion和newExplosion方法

            于是经过了一系列的调用,终于可以看到真正的爆炸处理方法了XD
            net.minecraft.world.Explosion类的doExplosionA方法和doExplosionB方法
            doExplosionA:
            1. 经处理后,把将要破坏的的方块列入“破坏列表”,等待doExplosionB方法处理
            2. 把爆炸产生的实体运动处理完 (更新受爆炸影响实体的Motion值)


            doExplosionB:
            1. 处理并产生爆炸的粒子效果
            2. 将“破坏列表”的方块设置成air方块,并按照方块性质生成掉落物
            3. 根据参数判断是否应该产生fire

            可以看到在此依然是先进行运动设置,再进行爆炸处理,不过这两个方法是紧挨着循序执行,所以不是产生EM的原因


            IP属地:广东7楼2017-03-14 00:05
            回复
              但是如果将这几处源码结合在一起,就可以看出EM产生根本原因了
              1. 首先由于实体更新队列,实体大多都是顺序更新,这就导致了即使是同时放出的TNT实体也有先后的处理顺序
              2. 又因为TNT实体的运动计算要先于爆炸计算,虽然对于第一个处理的TNT实体,没有什么大的影响
              3. 但是当它本身的爆炸处理开始时,它的doExplosionA方法对旁边的TNT实体产生了Motion更新(Motion可以近似看作“速度”,然而其实Motion不是正经的速度XD),然后doExplosionB给予爆炸的破坏效果
              4. 结束并删除第一个TNT实体后,第二个TNT实体开始更新,但是因为第一个TNT实体的doExplosionA方法改变了它的Motion值,而且它的运动计算先于爆炸计算,所以他进行一段偏移再爆炸,而且刚好是1 Gametick的位移
              5. 就这样产生了EM的效果


              IP属地:广东8楼2017-03-14 00:06
              回复
                同样的,我们举个例子,还是这张图

                原因:在激活时因为红石线的激活微时序,导致两个TNT——A和B先后进入实体更新队列,当80 Gametick后,A_TNT先处理运动事件,但是由于它的Motion变量值为0,所以不移动,然后进行ATNT的爆炸事件,于是A_TNT的爆炸效果改变了B_TNT实体的Motion值,导致B_TNT先处理运动事件的时候,向相对的方向偏移,移出水后处理爆炸事件,破坏方块,而且只有一侧破坏


                IP属地:广东9楼2017-03-14 00:07
                回复
                  2026-02-26 11:39:07
                  广告
                  不感兴趣
                  开通SVIP免广告
                  对这种解释的实例验证:
                  上面提到了红石线的微延时,那么也就是意味着我可以控制TNT实体在同一Gametick 的爆炸顺序?
                  没错,的确可以,下面就是用来验证这种解释的实验


                  先激活Red处的红石线(注意命令方块的激活顺序),再激活blue处红石线
                  所以Red处的发射器发射的TNT实体先进入实体更新队列,产生EM后自然让blue处的TNT实体向左偏移,导致左侧破坏


                  此图反之,可以初步验证这个解释的正确性,也说明TNT实体爆炸的微控是可以实现的,虽然我认为爆炸微控没有什么太大的用处XD


                  IP属地:广东10楼2017-03-14 00:08
                  回复
                    关于以前一些EM的误解:
                    没错我打算发这个解释贴之前,我一直倡导的理论是集束堆同时偏移,貌似也误导了很多炮党XD,在此做出深深的道歉
                    下面我将会为以前集束堆同时偏移的例子进行解释:
                    1.在实际实践中,总会发现两个TNT集束堆并不是一个移动一个不动,而是两个集束堆都会向相对方向偏移,这不是跟之前说的矛盾了么?
                    不,并不是的,如果你仔细想一想,你会发现TNT集束堆不同于TNT实体的一点——数量
                    两个TNT集束堆比较多的TNT实体数,而往往两个集束堆的TNT实体进入实体更新队列的顺序更加多样,所以完全可以产生此次A集束堆的1个TNT实体处理完后,又开始处理B集束堆的1个TNT实体,这样作用到两个集束堆上就会互相偏移
                    2.当然想要真正解释清楚还是得拿以前我经常用的一个例子:

                    没错,你一定想说:“这个就是1个TNT实体跟1个TNT集束堆,它不符合你现在的解释”
                    可是如果你尝试着更改拉杆位置,就会发现因为微延时这个也会有炸膛的时候,并不是怎么样都不会炸XD(其实我也是刚刚发现这个,误导了你们真是抱歉了Orz)
                    这也告诉我们理论优先于实验,实验服务于理论


                    IP属地:广东11楼2017-03-14 00:10
                    回复
                      EM的解决方案:
                      1.结构方面:
                      竟然有这个Bug,那还是解决这种问题的,而EM产生影响最大的就是落弹模块
                      首先标准落弹的方法之所以会产生大量的误差,就是因为EM的影响,导致两个方向的推进TNT,虽然同时爆炸,但实际位置向相对方向大幅偏移,导致弹坑很不精准,对此的解决方案就是尽量减小两个方向推进TNT的爆炸接触(最好让他们完全不影响对方)
                      在此利用了我以前发现的一个黑科技——透门原理
                      指TNT实体可以通过打开的栅栏门,而爆炸冲击却无法通过
                      用透门原理隔开两个推进TNT的爆炸冲击,这样就不会产生位移,所以我们可以做出落弹部分,如下图所示

                      这种是平抛和落弹的结合,相对于普通平抛它是单炮口设计,缺点是依旧有死角(原点附近是死角),而且近距离点阵不准(摩擦作用),但是远距离借鉴了标准平抛的优点,异常的精准

                      这种完全悬空(推进TNT在下落中途爆炸,最底下不是封死的,有空隙,为了打击原点)优点是无死角,但是有一点不太精准(在误差允许的范围内,大概是0.5~2格)


                      IP属地:广东12楼2017-03-14 00:15
                      回复
                        2.源码方面:
                        其实做那么多结构来解决这个Bug实在是不值得,而且PE里面还没有EM,所以不妨让我们玩一些比较暴力的方法——改源码XD
                        更改前:

                        更改后:

                        区别就是把爆炸处理提前,并且将fuse的临界值从fuse≤0改为fuse<0
                        这样TNT实体即使顺序更新,但是每次更新都先处理爆炸计算(删除实体),这样就不会位移了
                        但是其实这样改不是很专业,不过的确是很有效的
                        PS:个人虽然很讨厌这个Bug,但是动不动就改源码也是不好的习惯,所以不到结构限制而万不得已,最好不要更改!


                        IP属地:广东13楼2017-03-14 00:23
                        收起回复
                          好了,到这里大概也就没有什么了,还有在此其实我要感谢一个人,那就是@让他该人士说
                          他曾经提出过这个解释的雏形,但是我当时没有怎么在意XD,不过真的给了我一些启发


                          IP属地:广东14楼2017-03-14 00:23
                          回复
                            然后@一波小伙伴


                            IP属地:广东15楼2017-03-14 00:24
                            收起回复
                              2026-02-26 11:33:07
                              广告
                              不感兴趣
                              开通SVIP免广告
                              迟来的题外话1:
                              大家认为现在矢量炮万事具备了么?
                              不,其实还差得很远,即使理论方面不断地完善和改进,目前依然是有盲区,导致了有些现象按照目前已有的理论依然无法解释,更无法解决,下面就为大家介绍两个新出现的反常现象
                              1.高度衰减:
                              最开始由@恩氟烷瑞 发现,在三维矢量炮的制作过程中,会发现y轴推进量不变,推进距离却随着xz轴TNT推进量的增大而减小,所以叫做高度衰减,也导致了目前的三维矢量炮项目无限期的搁置

                              2.爆炸接触率与AABB边框箱
                              相比看到了这个标题,大部分就明白了XD,没错目前还没有任何一个关于边框箱和爆炸接触率的理论
                              PS:边框箱可以近似为碰撞箱


                              IP属地:广东21楼2017-03-14 23:47
                              收起回复