开启辅助访问     
收藏本站

站内搜索

搜索

Minecraft(我的世界)苦力怕论坛

[闲聊] 无框玻璃板的测试版已开发完毕

 发表于 2024-9-8 18:18:52 来自手机|显示全部楼层|阅读模式 IP:天津
将近一个月没发闲聊帖了,我就跟人间蒸发了似的——这么说不准确,我最近还做了些其他事。



首先是无框玻璃。由于很多人,包括我自己,需要一个 1.21.20+ 的无框玻璃,我重做了这个附加包,用脚本实现功能,现在已经发布了。这自然很好,可是我还不怎么会写复杂的脚本,所以一次性放置太多无框玻璃时会卡。以前会造成计划刻堆积,导致服务端忙着处理那些计划刻,结果这个区域内的逻辑计算几乎停摆。现在不一样了,虽然放置时会卡一下,但卡完了就不会造成任何性能影响了。

但我不满足于此,能不能在一次性放置大量无框玻璃时不会卡那么一下呢?重构无框玻璃附加包时,我就想着这个问题的解决办法。——想到了,注册一个世界的动态属性,每次处理无框玻璃的连接时动态属性值加 1,检测值大于 99 时推迟到下一刻执行。但这太复杂了,我又不会动态属性,一时间陷入了迷茫。

看着一行又一行几乎是重复的代码,我想,也许可以用循环迭代之类的方法优化一下,也许性能就会提升一点。于是我优化了代码,处理方块的速度确实从以前的 260b/s 提高到了 370b/s 左右,但对于成千上万的方块来说,这点提升简直是杯水车薪。

这时,我突然看到了在 system 类下有一个 runJob,它的功能就是根据任务总量分配任务到若干刻中执行。它的参数是一个生成器,我不知道怎么写,后来发现在 function 后面加上一个 * 就是生成器函数。

结果就是,现在一次性放置大量无框玻璃的时候不会卡了,会根据 XYZ 轴向连接玻璃。比如放置一块这么大的玻璃:

会根据下面的演示过程放置。(注:以下是一个 gif 动图,大小约为 0.23MiB,加载可能较慢。以下动图仅展示连接顺序,经过4倍速处理,不可作为实际游戏体验参考!

无框玻璃相关的特性差不多就到这里了,我几乎没法再优化了。



暑假刚开始时,有人问我为什么不做无框玻璃板。我只想说,不是我不做,我很久以前就研究过,而是我真的不会做。玻璃板这东西挺特殊的,首先它有一根细柱子作为最基本的形态,然后它能向四个方向延伸。就做出这些功能来,我都要折腾半天,更别提怎么加入“无框”的功能了。

但我还是做了,而且尝试做好。开发任何特性,数据和代码不难,难的是需要一种有效的且尽可能高效的思路。所以我们首先来分析这个方块,正如上文所说,它是一根可以向四周连接的细柱子,这是肯定的。但要做无框玻璃板就不能只考虑这些了,要考虑每个个体是否在整体的边缘。也就是说,单拿连接到北方的无框玻璃板来说,需要三种状态:不连接到北方,连接到北方的固体方块,和连接到北方的另一个无框玻璃板。我们分别用数字 0 1 2 代表这三种状态,而且四个方向都需要这三种状态。于是就成了 "north":[0,1,2],"south":[0,1,2],"west":[0,1,2],"east":[0,1,2] 这样的数据。这还不够,还需要处理上方和下方的无框玻璃板。于是添加一个向上和向下连接的状态,false 表示不连接,true 表示连接,就有了 "up":[false,true],"down":[false,true] 这样的数据。再次地,这仍然不够,因为我们无法确定上方方块是否有北边的连接,如果有,那么这个方块的北面连接的顶部必须是透明的,如果没有,顶部就要有纹理。于是再添加一些数据,"up_north":[false,true],"up_south":[false,true],"up_west":[false,true],"up_east":[false,true],"down_north":[false,true],"down_south":[false,true],"down_west":[false,true],"down_east":[false,true]。

——好了,好了,方块状态实在太多了。这不是数据复杂不复杂的问题,这是超出限制的问题。根据基岩引擎的日志,我们已经超出了 65536 个方块排列,到达了无块折返之境。现在方块状态失控了,这一切也不是安排好的,我们需要删除一些方块状态。删哪些?反正我删掉了 "up":[false,true],"down":[false,true],现在正好在限制里。



思路就是这么个思路,现在我们来做模型。有了思路的指导,做模型就简单多了,虽然还是很繁杂,但至少不至于像无头苍蝇一样乱撞。我们还是用当时无框玻璃的思路做,给每个视觉上的边框做一个立方体,然后给它命名。我实在不知道怎么描述这个过程,总之这个模型混杂着各种立方体和 UV。而且为了简便,我还用了缩写,将每个方向的首字母作为它的简称。这样的话,原本的 base_up_north 就能缩写为 baseun,方便以后写骨骼可见性。



回到方块数据这里来,我们来写一点骨骼可见性。其实就是根据之前的方块状态决定哪部分模型可见,而这通常不会很难。写完了之后,就是做一点苦力活,是个人都能做,但我只是一个人。本来只要一个方块文件就大体能运作,但我总得给它一个纹理,决定渲染方法,指定本地化名称……我做好了十六色的无框染色玻璃板,接下来就是进游戏测试。

果然失败了,毕竟写一次就能实现功能是几乎不可能的。但我又修改了一番,加上了碰撞箱和判定箱,这样就好多了。染色玻璃板却很难,因为不同颜色之间会混合,两个相同的不透明度比较低的颜色叠加起来就会看起来不透明,所以无框染色玻璃板比普通的还难做。



以上其实不是按照描写的顺序发生的,比如考虑顶部渲染时无框玻璃板早发出去了测试版。现在公之于众的版本是还没有改进顶部渲染和其他细节的版本,我只好尽力做出一个差强人意的版本发布。



今天(2024 年 9 月 7 日)我更新了游戏版本到 1.21.40.20,进存档测试时,我突然发现存档加载得很慢,一开始那段时间 ServerTime 一直是 0。ServerTime 大概就是服务端响应的时间,值为 0 代表未响应。这时,突然弹出一大串内容日志,里面最显眼的就是原版的樱花木栅栏的 ID。后来倒加载得很快,就是在进入存档的最后一个阶段崩溃了。

于是我卸载了所有附加包,以上大部分问题仍然在,不过这次没有崩溃,而是成功进入了存档。我又把附加包装了回去,发现又弹出了内容日志,还是樱花木栅栏的 ID,不过这次我又看出了些文字,好像是哈希网络 ID 冲突之类的东西。我打开日志文件,才发现在樱花木栅栏 ID 之前还有无框黄绿色玻璃板的 ID。我把所有无框染色玻璃板都暂时移除了,这才能进存档了。

我见过哈希网络 ID 冲突,当时是两年前的暑假,我加入了导管这种方块,原计划实现物品的运输。它的方块状态用到了负值,结果后来某次游戏更新后提示哈希网络 ID 冲突。我不知道怎么解决这个问题——以前不知道,现在也不知道。



现在我正在被染色无框玻璃板的难题困扰,于是我决定转头去做点别的。结果我看到了基岩版的拼图方块成功的例子!与我的猜测一样,那是定义了一种全新的方块。不过它比我那个方块高级多了,用了脚本来计算一个结构中有没有拼图方块,如果有,获取这个拼图方块的坐标,并且放置一个新的结构,让它内部包含的拼图方块与上一个拼图方块对齐就行了。还能通过自定义组件和自定义 UI 做出与原版拼图方块相似的UI,可以自由填写目标池、目标名称和选择优先级之类的东西,那样就能做出一个完全还原的拼图方块了。

不过我没找到那个附加包的下载方式,也不能体验看看。于是我想自己做一个,名称和纹理就用之前那个方块的。首先查询一下有没有这样的 SAPI,我发现确实有。但是以我目前的水平,几乎不可能做出成品,而且我想先完成无框玻璃板,所以我废弃了这个计划。



现在是 9 月 8 日下午。经过今天好几个小时的努力,现在我差不多做好了染色的无框玻璃板。这实在是不容易,而且今天我又遇到一个差点难倒我的问题——性能。

最开始的无框玻璃,1.21.20 版本以下的无框玻璃,是通过一个叫 minecraft:queued_ticking 的已移除组件实现的。这个组件负责触发计划刻,方块被触发计划刻之后就会执行一系列逻辑判断,主要是根据周围同类方块的有无设置方块状态。之后,骨骼可见性字段负责根据方块状态决定渲染,这就成了最基础的无框玻璃。但这个方案有缺点,由于无框玻璃必须实时连接,我必须把触发计划刻的时间间隔调得很短。这样,每次游戏循环时都要尝试连接无框玻璃,无论实际上连接了没有。更不幸的是,查询周围有没有特定方块并设置方块状态不能用命令,只能用一个叫 q.block_neighbor_has_any_tag 的 Molang 查询,而这个查询非常消耗性能。总之,最初的无框玻璃只要大片放置就会导致服务器一直处理那些逻辑,结果其他逻辑得不到处理,或者处理得很慢,表现在游戏里就是沙子很久才落下之类的现象。

为了优化这点,我重构时把以前的 onTick 改成了其他触发器。由于脚本很适合执行这种复杂逻辑,用自定义组件实现无框玻璃也比以前高效得多,再加上 system.runJob 方法对一次性放置大量无框玻璃造成游戏冻结情况的改善,无框玻璃整体来说对性能没什么影响了。

但无框玻璃板不同。还原不完整而且会变形态的方块往往比体积完整的方块难数倍,而到了玻璃板这里,我不仅需要还原原版,还需要加上无框的功能,难度可以说是无框玻璃的十几倍。要命的是,无框玻璃板必须用 onTick 触发器(也就是计划刻相关的触发器)实现,这导致了无框玻璃板不可能同时存在很多,不然就会卡顿,未响应,崩溃。不过还是有办法避免 onTick 触发器的,通过订阅玩家放置方块和破坏方块的事件,我成功做出了可以大量存在而且不影响性能的无框玻璃板。

但就算是这样,就算用了 runJob 改善放置时的冻结,一次性放置大量无框玻璃板时还是会卡顿很久。具体数据是每秒只能处理大约 22 个无框玻璃板,也就是说,放置 4096 个无框玻璃板要卡大约 3.1 分钟。这段时间里区块不会加载,游戏逻辑不会运算,总之很难受。(这些黄绿色无框玻璃板的数量还只是命令一次放置方块数上限的八分之一,就要 3 分钟才能加载完?)

除此之外,还有个更大的问题——方块的哈希网络 ID 碰撞。这个问题很奇怪,很玄学,我捉摸不透。测试时我只添加了一个黄绿色无框玻璃板就提示它与樱花木栅栏碰撞了,而且进入存档时莫名其妙要卡好久,我不知道游戏能不能承受剩下 16 种无框玻璃板(15 种染色的和 1 种普通的)。这个问题大概与方块状态有关,目前的经验是方块状态有负值或者太复杂时就会这样。确实,无框玻璃板的方块状态直接达到了上限,但只有这样才能做出这个方块。

不过我还没有优化代码,如果优化了代码,说不定效率会提升一点。但看着好不容易才写好的一堆又一堆代码,优化?重构??……

苦力怕论坛,感谢有您~
 发表于 2024-9-9 09:13:30|显示全部楼层 IP:辽宁省
大佬加油!
3#2024-9-9 09:13:30回复收起回复
苦力怕论坛,感谢有您~
 发表于 2024-9-9 00:12:38|显示全部楼层 IP:河北省
辛苦了,加油!(看不懂,只能鼓励一下了qwq
2#2024-9-9 00:12:38回复收起回复
苦力怕论坛,感谢有您~

本版积分规则

本站
关于我们
联系我们
坛史纲要
官方
哔哩哔哩
技术博客
下载
网易版
安卓版
JAVA
反馈
意见建议
教程中心
更多
捐助本站
QQ群
QQ群

QQ群

访问手机版

访问手机版

手机版|小黑屋|系统状态|klpbbs.com

粤公网安备 44200002445329号 | 由 木韩网络 提供云服务 | GMT+8, 2024-9-27 06:46

声明:本站与Mojang以及微软公司没有从属关系

Powered by Discuz! X3.4 粤ICP备2023071842号