这是我在网易版《我的世界》里做得最久的一个模组,模组码 7573464, 在模组中心搜「起床随机刷新」或者直接搜数字就能找到,免费。
一句话说玩法:你在一张超平坦世界里睡觉,睡醒的那一刻,脚底下的方块全被随机换掉。 不是换几块,是整片大陆。
听着挺简单,实际上我从开始写到能玩,中间推倒过两次。
先说说为什么会想做这个
我玩超平坦世界玩腻了。超平坦就那么几层草方块,一眼望到底,建完东西就没意思了。
有一天我想:如果每天早上醒来,世界都变一次呢?你昨天挖的矿洞没了,昨天搭的桥没了, 地形整个换了一遍。那这个存档就永远玩不腻。
这个想法我跟朋友说了,他说这不就是给自己找罪受。但我觉得挺有意思。
坑一:防活埋
第一版写完,我自己进去试,一觉醒来被埋在自己家里了。
因为我把「所有方块都换掉」当成了规则,包括空气。空气被换成石头之后, 屋子、洞穴、我挖的洞,全被填实了。玩家站在哪都一样,直接埋进去。
改法:刷新只替换「非空气」方块,空气保持空气。这样洞穴、房间都是原样保留的, 玩家站在哪都不会被包围。
这一条现在是整个模组的底线,动别的都不能动它。
坑二:怎么知道这是不是超平坦世界
模组只该在超平坦世界里生效。如果玩家在一个正常世界里睡觉,把地形全刷了, 那叫破坏存档。
我一开始想的是检查世界类型,但网易版的接口里没这么直接的东西。后来用的是笨办法:
- 从玩家脚底下取 5×5 一共 25 个点的高度
- 看这些高度里哪个值出现得最多(众数)
- 如果绝大多数都一样高,就认为是超平坦
为什么不用「全部一样高」?因为超平坦世界里也可能有玩家自己搭的房子、种的地, 不可能是完全平的。用众数判定才扛得住。
还有个细节:这个检测不能一口气跑完。一口气算 25 个点再刷新, 脚本一次性跑太久会被脚本监测盯上,所以我把它拆成好几帧,一帧算一点。
如果检测出来不是超平坦,模组不会硬刷,而是每 1.5 秒在屏幕上弹一次红色标题加响铃, 提醒玩家这个世界不对。不刷新,但也不装作没事。
坑三:整片大陆怎么刷才不卡
一开始我想的是一次性全刷完。但 512×512 的地图有多少格你算算—— 一次算完,游戏直接卡死。
后来改成流式刷新:追着玩家跑。玩家周围和离得近的区块优先刷, 走过去了再刷下一批。这样玩家感觉是「走到哪变到哪」,而不是站着等半天。
流式刷新最难的是别漏块、别重刷。我用的是给每个区块记一个线性索引: 这个区块刷到第几个位置了,下次从这儿接着往下。刷完的区块扔进一个集合里, 再遇到就跳过。
还有个小地方我改了三遍:玩家身体占的那两格要跳过。不然刷新的时候 正好把方块填在玩家身上,人就卡在方块里了。
坑四:有些方块必须保住
如果什么都不留,会出两个事:
一是末地进不去了。 末地传送门框架要是被换成石头,你就永远去不了末地。 所以框架必须保留。而且光留框架不行 —— 框架正下方的支撑方块也得留, 不然框架会掉。
二是床。 床是用来触发的,床没了就没法睡了。
现在的白名单是:基岩(世界底板)、床、末地传送门框架(含正下方支撑)、末地门。
大海、河流、湖泊这些不保留。一开始我留了,结果刷新完地图上还是大片原来的水, 看着像一块「泉水大陆」,很出戏。现在它们也算「非空气」,一起翻新,新的湖由方块池重新生成。
还有两个小设计
液体要用静止的。 方块池里要生成岩浆湖、水湖,如果放的是会流动的, 一刷新就是成百上千格流体同时算流向,帧率直接跪。所以用的是不流动的静止液体和固体岩浆。
每人发一套东西。 每次起床刷新之后,玩家会收到 12 个末地传送门框架 + 1 张床。 因为框架可能被别的东西占了位置,床也可能被刷掉,不发的话玩家迟早玩不下去。 另外每 20 个游戏日会补发一次,防止中间丢了。
网易版和国际版是两套代码
这个模组做了两个版本:
| 网易版 | 国际版 | |
|---|---|---|
| 脚本语言 | Python 2.7 | JavaScript |
| 接口 | 网易 MODSDK | @minecraft/server |
| 代码量 | 4 个模块,675 行 | 1 个文件,510 行 |
| 引擎要求 | — | 1.21.0 以上 |
网易版里内嵌的是 Python 2.7,print 后面不加括号的那种。
国际版是 JavaScript,写法完全不一样,所以这两套是分开写、分开测的,
不是一份代码编译两次。
我先把网易版做完上线了(就是 7573464),国际版是后来重写的。
最后
这个模组从想做到上线,中间隔了挺久。最花时间的不是写代码,是想清楚边界:
- 哪些方块能换,哪些不能
- 什么情况下该刷,什么情况下该拦住
- 刷新的时候玩家在哪、会不会出事
现在这几条都写在配置文件里,改一个数字就能调。
在网易版模组中心搜 7573464 就能直接下,免费的,玩完告诉我哪里还能改。