欢迎光临散文网 会员登陆 & 注册

【戴森球计划】游戏内传送带结构与刷新机制与环带爆带bug原理及游戏内外修复方法

2023-07-07 19:11 作者:莳槡_makuwa  | 我要投稿

首先,关于非环带的所有属性说明与机制的视频版教程在这:传送带刷新机制、优先级、以及两个重要问题

以下内容为包括环带结构在内的传送带主要结构与机制的图文版说明:

1、货物在传送带上的存储方式

传送带中的最小存储单元为buffer(byte型数据),每个货物占用10buffer的空间,在这10个buffer中,最上游的5个buffer表示物块的位置偏置,值从246~250;接着由2个buffer表示货物的id、1个buffer表示堆叠数、1格buffer表示喷涂点数的信息,这4个buffer的数值都不超过100;最后是一个值为255的顶部buffer。即对于一个这样的传送带上的货物:

一个货物占用传送带10个buffer

这10个buffer在游戏数据中存储的值为:

上图为氢在传送带上时,游戏内部表示这个氢的传送带上对应的十个buffer的数据

而当且仅当传送带上某些buffer为空时(并不在任何一个物品的10buffer范围内,如不连续的物品间的空隙等),其值为0。

2、传送带可推挤插入的机制

这一部分内容我在关于蓝带并带无法跑满的bug与解决方法中提到过,以下为文字版:

当并带或分拣器等发生在线路中部的可推挤插入发生时,首先,有个插入的目标buffer,即一节传送带的中心buffer:

一节传送带的中心buffer的id,即符号"@"后面的那个数字

可推挤并带插入时,首先判断从目标buffer之后的向下游5个buffer的区间内是否存在空buffer,即是否存在空隙。若不存在空隙则无法插入

此处模拟一个并带结构,左边的黄色区域是往右输入的一个并带的物块,右边是往上移动的传送带,表格中每一行代表一个buffer,左边的物块指向为插入的目标index的位置,绿色区域中至少有一个buffer为空隙时,插入才有可能发生

若这5个buffer中存在空隙,则会从空隙处开始向下游遍历到最多目标index+10的位置,找最下游的空隙,并从最下游的空隙开始向前遍历寻找足够的空隙来保证线路中有足够让物块插入并推挤的空间。遍历时,出于卡顿考虑,最多遍历2880个buffer,如果遍历2880buffer或者遍历到传送带的最上游(buffer[0]处)时仍未找到10个空buffer,则判定为没有足够的空间推挤插入,此次插入不会发生。反之如果在那之前找到了10个空buffer,则判定可以发生推挤插入,物块会被成功输出到这节传送带上,并且如果一开始没有10个连续的空buffer就会将上游货物向后推挤,以塞下这一个物块。

3、线路总容量、线路货物数

在线路数据中其它比较重要的信息为线路总容量和线路货物数,对于一条非环带的不闭合线路来说,线路总容量就是起在内存中的buffer数,而线路货物数则是此时线路上有多少个物块的统计数量。由第一个概念可以知道,因为1个物块就需要10buffer的存储空间,所以一条线路上能够存放的物块数量不能超过(线路总容量÷10)后向下取整的值。

此处一条线路的线路总容量为115,代表这条线路上最多放11个物块

然而,对于环带来说,其实际上的线路容量为面板显示的线路总容量+9,即如果上述这条线路的信息是一条环带的线路信息的话,那么我们实际上可以往线路中放下12个物块。但是如果 环带的物块数量≥(线路总容量÷10) 的话,环带就会发生爆带现象,此时环带上的物块将不再刷新,直到外部将物块取走让线路货物数重新小于线路总容量。

4、环带/非环带末端输出与货物流动逻辑

无论是环带还是普通传送带都有始末端,buffer[0]所在的位置为始端,buffer[实际线路容量-1]所在的位置为末端。进入线路末端货物输出的判定逻辑为末端buffer的内容是否为255,即此时是否有一个货物已经顶到线路底部无法再在线路内向前移动。仅在这种情况下会进行尝试输出的判定。此时非环带会进行上文说过的可推挤插入的判定,若成功推挤插入,则当货物数据放到目标节点对应位置后会把最末端的十个buffer清0,相当于完成了一次货物的转移。对于环带来说,其末端输出是输出到自己头节点的非推挤输出,他会直接判断自己的0~9buffer是否为空,若为空则直接转移货物。若末端输出失败,则从有空隙的地方开始向上游遍历货物让货物依据传送带等级和前方具有的空隙总数更新。

为了表述得更易懂,下面举一个例子表示当蓝色环带上某一货物接近末端而未抵到末端时的更新情况。


蓝带环线上物品货物刷新情况举例(图中横向每格为1buffer,蓝色代表货物所占buffer)

5、环带爆带bug发生的原理

当环带上有[线路真实容量/10]([x]表示不超过x的最大整数)个货物时,线路上剩余的空隙数量不超过10个buffer。即使这些空隙全部聚集在0~9buffer内,也无法保证0~9的十个buffer全为空,所以环带末端永远无法成功输出,这就导致了整条环带堵死。

于是,制作组为环带加上了9节“隐藏buffer”,这就是上面说的对于一个线路容量显示为n环带,其实际线路容量为n+9的原因。然而,这9节buffer除了没有统计在环带的属性面板上以外与普通的buffer并无不同。所以这种方式相当于仅仅是扩大了一下环带的线路容量而已,它的确让线路容量为10n+1以上的环带在具有n个货物时可以维持转动,但是与一个线路容量为原线路容量+9的环带并无区别。当环带上有n个货物时,由于这多加的9节buffer,很有可能多塞一个货物变成线路上有n+1个货物。然后就会发生所谓的爆带现象。

下附环带爆带前后过程:

图中蓝色区域为货物区域,白色区域为空隙区域,货物顺时针流动,插入点后的5buffer黄色竖线的中间区域即第二节(《传送带可推挤插入的机制》)附图中绿色的插入判定的空隙搜寻区域

这边演示的是蓝带环带上货物离线路末端只有不到5buffer时货物的运动情况。离底端不到5buffer空隙(此处为3buffer空隙)会使所有物块只能向前流动3buffer,而不是2buffer插入到线路前端,8buffer留在线路末端。
再之后,由于上一帧所有货物移动了3buffer,线路始端有13buffer的空隙,这一帧开始时货物输出到前10buffer,然后便和上一个货物之间产生了3buffer的空隙。当着3buffer空隙流动到插入点时,如果刚好处在起始节点空buffer+空隙数量>10buffer的情况下就会发生成功插入,如果其实节点只有5buffer的空隙,而流动空隙又不足5buffer,那就无法成功插入。这也是环带并带有的时候不会发生爆带而有时候会的原因。在推挤没有波及起始节点空隙的情况下连续货物在环带上流动时起始节点的空隙又5buffer和10buffer两种情况,相邻两帧这两种情况轮流更替。


6、环带爆带的游戏内预防方法及原理

关于这个bug,一种游戏内的处理思路就是为环带增加一种柔性缓存。这种柔性缓存需要在堵塞时可以比起畅通时额外容纳至少一个物块的能力。除了利用四向制造具有一定逻辑的线路之外(这点视频里可能会提到,但由于是不推荐的方法在此就不提了)。还可以利用一节建筑的特性制造更低卡顿、更具功能性的防爆带结构。以下推荐集装机法和物流塔法。

方便起见,我们将以类似原理防止环带爆带现象发生的结构状态分为三类:不饱和状态、饱和状态与堵塞状态。

不饱和状态下,线路中仍有许多空隙。考虑环带的起始节点,有的时候会产生10buffer以上的空隙后才有新的物块输出,此时后输出的物块与上一个物块之间就会产生空隙,给外部插入提供空间。

饱和状态时,起始节点只要一有10buffer以上的空隙就一定会有货物在顶到下一个货物的位置进行输出,这样就会把线路中的所有空隙锁在起始节点处,不会有任何空隙流经下游的外部输入口,防止外部输入的再次发生。这就是这些结构防止爆带的主要手段。

堵塞状态即为环带末端货物无法输出,环带始端空隙不足其它货物输入,防堵结构自身仓储已满或者没有仓储的情况。下述防爆带结构在提到的不适用情况之外不会出现这种情况。

注:下述例子中,环带内部所有节点传送带等级相同。

物流塔在不饱和状态下,因为传送带流速相等,所以输入的相邻两个物块的间隔帧足以让上一个输出的物块移动10buffer,故而在输出口端物块没有被外部推挤的情况下只要有输入口有输入即可在输出口马上输出,即使发生了外部推挤而导致物流塔内囤积一个货物,也会因为环线不饱和而很快在下一个空隙到达时讲内部的货物输出出去。

由此,只要空隙满足外部货物就会不停输入,直到某次输入以后堵塞了物流塔的输出口,让物流塔内有了一个货物的堆积,并且此时环线上总空隙数不满10buffer,此后,由于物流塔内通常一直有1个货物存在,物流塔将会连每帧都尝试输出,只要输出端口一有10buffer以上的空隙满上就会倍物流塔内部输出的物块顶上。不会有空隙往下游流动,于是直到环带上货物被外部取走之前,外部插入永远不会再次发生。

集装机与物流塔逻辑类似,实际上集装机也具有两格的存储空间,故而其也可以从不饱和状态进入饱和状态而非堵塞状态。由于集装机的断电状态、缺电状态、正常供电状态以及环带上货物连续/不连续等各种情况种类过于庞杂,这边不方便一个个详细解释。并且只要直到集装机的底层逻辑,集装状态的集装机在环带中运行时的情况并不难推论,故而这边只讲述集装状态的集装机(对应的解压状态的集装机逻辑并不能防止爆带,这边也不会解释)。

集装机内部具有两格的存储空间,其每帧更新逻辑如下:


集装机集装状态下的更新逻辑

集装状态的集装机在正常供电时的更新逻辑如图所示,每帧会尝试从输入端抓取货物,被抓到的货物会被放在第一格进行等待,等待时间与集装机所连的传送带等级有关。具体的在《集装机集装条件以及单带4集装方案》中讲过。

对于供电率off状态的集装机,可以认为他的等待时间只有1帧,黄电状态的集装机比较鬼畜,它在某些时候会罢工让谁都不能过,不过也不会因为黄电而导致放集装机的环带出现爆带现象,这点可以放心。

7、环带爆带的代码修复方案参考

代码上的修复方法有很多种,在此推荐个人认为最合适的一种:

最基础的更改环带自插入的判定条件,将原先货物顶到实际最大线路容量时才触发自插入判定的逻辑改为只要货物顶到显示线路容量的最大值时就触发尝试自插入的判定。并且将环带自插入时只输出到buffer0~9的逻辑改为输出到buffer0~14的可输出位置的最前端。

这样做的原理就是让环带末端本身具备货物容量缓冲的性质。当货物到达线路最大容量-9时便会被尝试输出到线路始端,倘若发生了一次堵塞,就不过是让货物在线路上多移动一帧,而在这一帧之后由于尝试输出到0~14buffer最下游可输入位置的特性可以保证货物的无缝隙输出,这样在环线下游就不会有空隙流经外部输入口。

然而这样并不能完全修复爆带bug,因为当外部输入目标为环带起始节点时由于目前常规的一格节点间距环带带起始节点的中心bufferid为10,故如果当id为0~10+x(0<x≤5)的buffer为空的时候起始节点有可能在被自插入之前被外部输入抢先塞入物块。所以这种方式只是避免了大部分环带的爆带现象,无法完全修复这个bug。

不过对于上述遗留问题对玩家来说有一种完全没有成本的解决方案——那就是将环带中所有传送带(或者至少环带中有外部输入的部分)替换为节点间距在1.5格赤道格长度以上传送带,使用蓝图可以比较轻松地达到这个目的。这样做就能保证有外部输入的起始节点的中心buffer的id一定在15以上。

如果不希望玩家需要了解这一机制并给出对应解决方法,想要从游戏代码上完全杜绝爆带情况,还有接下来两种方案。

第一种是更改环带生成时的数据结构,直接从代码上保障环带头节点的中心buffer在15以上,也即给环带的头节点前部加上固定15个buffer(有点类似现在加在环带末尾的隐藏buffer的意思)。这样即使是节点间距为0.2赤道格长度的环带始节点,其中心buffer的id也能在15以上。

另一种是继续更改逻辑,不过这工作量可能会很大,需要考虑各种各样的情况。基本思路是在环带自插入判定的过程中若找到10buffer空隙时代表当前bufferid的index值若<10则需要从环带末尾开始向前遍历那9节隐藏buffer,然后将空隙数减去非空的隐藏buffer的数量。因为这里的隐藏buffer可以看作是为了维持环带运作的不可占用的buffer,只不过有时环带始端会借用这隐藏buffer的空隙,故而在外部尝试插入向前遍历空隙时需要弄清起始节点向隐藏buffer的“借款”,防止错误估计起始节点的资产安排项目之后没有足够空隙周转造成坏账。

要让环带更真实、更符合背景设定,就像现实生活中中转圈圈的传送带一样的话,还要更改逻辑让外部输入环带时并不是每次向前遍历空隙到buffer0处就停止了,而是应该遍历一整圈或者2880buffer。不过这样就需要在更新逻辑中写下更多的情况了,包括“环带长度是否>2880buffer”、“插入点id区间为[0,2870]还是[2871,2880]还是[2880,∞)”等等分类讨论。不过这一问题对于玩家来说如果不刻意观察细节会很难发现,不像现在的环带爆带bug一样,基本接触过混带的玩家都深受其害。

混带玩法是目前游戏内上限和可玩性都比较高的一种蓝图流派,若果能修复这一bug可以给混带玩法的走线更多自由的空间(至少不用因为担心爆带而强塞一个集装机,有些走线可以更密甚至不落地),也能降低这种蓝图的卡顿(毕竟集装机单线程,卡顿也不低),作为玩家而言我还是比较希望能早日修复这个bug的。

【戴森球计划】游戏内传送带结构与刷新机制与环带爆带bug原理及游戏内外修复方法的评论 (共 条)

分享到微博请遵守国家法律