Mindustry MultiCrafter模组实战:打破原版合成限制,打造多输入多输出工业机器
简介面向Mindustry 6.0模组开发者的MultiCrafter工具包以JavaScript脚本形式提供自定义多方块合成器的快速实现方案。脚本封装了newCrafter()注册接口支持配置输入物品/液体槽位、数量以及“任意一种输入即可”或全部满足等判定模式大幅简化原先繁琐的合成逻辑配套的README说明文档给出在main.js中启用模块并调用该接口的典型示例适合有基础JS语法、希望为Mindustry添加自定义内容的玩家。资源整体仅2个文件分别为js脚本和md说明压缩包大小4KB轻量易用。目前已有166人学习可帮助模组作者快速上手自定义合成配方设计同时理解Mindustry mod脚本的基本组织与依赖调用方式减少反复调试底层接口的成本。 说个真实的场景我在Mindustry里造产线想把铜矿和铅矿同时送进一台机器出口出两个成品带一点废渣。原版的熔炉最多给你两个物品进、一个物品出液体更是只有一条路想要两进两出甚至三进三出游戏自带的合成器根本做不出来。后来我在社区挖到了MultiCrafter这个mod它是专门为Mindustry 6.0准备的一把合成器扩展钥匙底层用Java实现但对mod作者开放的是JavaScript接口也就是你经常听到的mod的js那部分。装好之后你可以通过脚本或配置文件批量定义多输入多输出的方块和配方再配合JS写点判断逻辑就能做出一台完全自定义的工业机器。这篇文章不是翻译文档是我实际用MultiCrafter写了几个mod之后攒下来的经验。从mod.json怎么配、JS脚本怎么挂到配方为什么不生效、贴图为什么乱转我都会按顺序讲清楚。适合两类人看一类是想给Mindustry写mod但没摸过脚本的新手另一类是已经写过mod但被原版合成逻辑卡住的进阶玩家。1. 先搞懂MultiCrafter到底解决了什么问题1.1 原版合成逻辑的边界在哪在Mindustry 6.0里GenericCrafter几乎是最常用的生产方块类型熔炉、硅冶炼厂、塑料加工机全是它的子类。它的基础逻辑很简单满足输入条件后按craftTime计算进度进度满了就生成输出。听起来没问题但原版对输出的处理非常克制——成品只有一个物品槽位液体输出也只有一条管线想再加一条副产物输出你得自己改build逻辑这对纯JS玩家来说门槛不低。有人会说那就多叠几台机器嘛。但实际部署工厂时产线最怕的就是副产品主产物进总线副产物卡在出口没人取整台机器就会堵住后面的工序全部停摆。所以mod里做多输出不是花活是产线设计里的刚需。我在不少mod社区都见过类似的求助帖最后都是用MultiCrafter这类方案解决的。1.2 MultiCrafter的核心能力MultiCrafter的思路很直接把配方从代码里抽出来变成一份份配置数据。你定义一个方块再给它挂一组配方每个配方可以拆成任意多个输入物品、输入液体、输出物品、输出液体工艺时间、合成特效、条件约束都可以独立配。用JS读取和切换这些配方也方便不需要碰底层Java。这样一来一台机器就不再是输入A输入B - 输出C的固定公式而是一张可以随时换的加工菜单。比如同一个精炼炉白天炼铅晚上炼铜只要在JS里根据时间判断切换配方就行。我用一句话总结MultiCrafter的价值它把能不能做多产物变成了想不想做多产物。为了让你直观理解原版和它的差距我整理了一个对比表能力原版GenericCrafterMultiCrafter输入物品数1-2种多种自定义输入液体数1种多种可同时输出物品数1种多种自动入槽输出液体数1种多种可独立管线配方切换需改代码配置文件/JS动态切换副产品处理容易堵住独立输出槽不互堵2. 开工前要准备的环境与工程结构2.1 mod.json里最容易踩坑的几个字段在6.0里mod固定在mods目录下一个mod就是一个文件夹。首先要写mod.json它相当于mod的身份证。我踩过的一个坑是mod怎么都加载不进来最后发现是name里用了中文——Mindustry对mod的name要求必须是英文字符否则依赖系统直接认不出。另外dependencies字段要写MultiCrafter的mod name不是显示名装完MultiCrafter后先打开它的mod.json核对再写进自己的依赖里。{ name: advanced-refinery, displayName: Advanced Refinery, author: YourName, description: 基于MultiCrafter的多产物精炼工厂, version: 1.0.0, minGameVersion: 120, dependencies: [multi-crafter] }关键点有两个。一是minGameVersion填120对应6.0系列别为了装新填太高否则6.0会报版本过低直接拒绝加载。二是name字段一旦写错后续依赖和脚本里的require都会跟着出错所以mod.json的每个字段我都建议在游戏主界面的Mods面板里看一眼加载日志确认。2.2 目录结构与脚本入口标准的JS mod结构长这样advanced-refinery/ ├── mod.json ├── icon.png ├── content/ │ └── blocks/ └── scripts/ ├── main.js └── lib.jsscripts/main.js是Mindustry固定加载的入口文件。如果想拆成多个文件不要用importRhino环境不支持ESModule而是用Mindustry提供的require(lib)它会从scripts目录下加载同名js文件。我拆文件的原则很简单凡是超过200行的逻辑就单独抽文件入口只保留require和初始化调用省得后期改配方时在几百行里翻。调试这块Mindustry没有独立的脚本控制台你在JS里写print(hello)输出会出现在游戏启动器或终端的日志流里而不是游戏内聊天框。我调试配方的习惯是每个关键节点都print一行比如配方ID、输出物品数量、当前进度宁可多打几行也不去猜问题在哪。3. 核心实操装配第一台多输入多输出合成机3.1 注册方块并绑定MultiCrafter我先放一个完整可跑的例子。假设我的mod叫advanced-refinery要做一台3x3大小的等离子精炼厂输入铅矿和水输出铜和矿渣。步骤分两段第一段在main.js里注册方块第二段给这个方块挂配制方。// scripts/main.js const Refinery extendContent(GenericCrafter, plasma-refinery, {}); Refinery.size 3; Refinery.health 640; Refinery.craftTime 80; Refinery.hasItems true; Refinery.hasLiquids true; Refinery.liquidCapacity 30; Refinery.craftEffect Fx.smeltsmoke; Refinery.updateEffect Fx.plasticburn; Refinery.requirements ItemStack.with(Items.copper, 200, Items.lead, 120, Items.titanium, 80); Refinery.buildType () new ObjectCraftBuild();这段是纯Mindustry 6.0的API逻辑很直白extendContent复制一个GenericCrafter的类型然后改数值。注意最后一行buildType6.0里如果你不显式返回构建类型部分自定义方块会没有实际能建造的build实例这是新手最容易漏的一步漏了之后方块在沙盒里根本摆不出来。3.2 用文件或JS注入配方挂配方这一步不同版本的MultiCrafter API略有差异。有的版本支持在content目录下放独立配置文件有的是在JS里调MultiCrafter.addRecipe。我以最常见的JS方式为例如果是用配置文件字段名也差不多对着mod自带的sample改就行const MultiCrafter require(multi-crafter); MultiCrafter.addRecipe(plasma-refinery, { craftTime: 80, inputs: [ { type: item, item: Items.lead, amount: 2 }, { type: liquid, liquid: Liquids.water, amount: 10 } ], outputs: [ { type: item, item: Items.copper, amount: 1 }, { type: item, item: Items.slag, amount: 4 } ] });这里要理解一件事MultiCrafter其实是接管了GenericCrafter的build逻辑输入缓存、进度、输出缓存全由它自己的build类处理配方只是数据源。所以它能做到多输出且不会像原版那样把副产品卡在出口因为它会把副产物按顺序推进输出槽而不是只留一个outputItem位。3.3 验证配方是否生效在游戏里建好方块后先用沙盒模式刷一台出来。确认三件事方块能正常建造不会被红字报错输入带和输出带都能识别物品合成进度跑完主产物和副产物都出来。我把这个流程称为三查任何mod机器都应该先过这三关再继续调数值。如果发现进度跑满但没输出优先怀疑输出槽满了或者输出物品ID和实际物品对不上这两种情况的表现完全不一样前者进度卡住后者直接跳回原位。4. 进阶用JS动态控制配方与行为4.1 在第三方mod基础上加入自定义逻辑MultiCrafter当依赖用很简单但它的方块默认是按配方表加工的通用逻辑真正的灵活要写到JS里。比如我想做一个条件配方白天产物A晚上产物B。可以给方块自定义build类型在update里切换。代码大致是Refinery.buildType () extendContent(GenericCrafter.GenericCrafterBuild, Refinery, { update() { this.super$update(); if (Vars.state.isDay()) { // 切到白天的配方 } else { // 切到晚上的配方 } } });super$update()是Mindustry的JavaAdapter里调用父类方法的固定写法和Java里的super.update()一个意思。在6.0的Rhino环境里自定义方法名如果和父类重名必须用super$方法名来调用父类实现否则会无限递归直接崩。这个坑我踩过一次当时日志刷得全是StackOverflow排查半天才发现是父类调用方式写错了。4.2 用Map管理多配方当你有一堆配方要管理时别用一串if/else。JS里的Map或普通对象配得好就能当配方注册表用const recipes { day: { craftTime: 60, output: Items.copper, amount: 2 }, night: { craftTime: 45, output: Items.lead, amount: 3 } }; function getRecipeForBuild(build) { return Vars.state.isDay() ? recipes.day : recipes.night; }这里的思路是配方选择逻辑和配方数据分离。后续想加新配方、改数值只需要维护recipes对象就行。在调试时print一下getRecipeForBuild的结果就能快速确认切换逻辑对不对。我在实际项目里一般会把配方分组然后用一个switch对象映射条件到配方组这样写起来比长篇if/else清爽得多。4.3 性能JS别做重活Mindustry 6.0的JS跑在Rhino上性能比Java调用差不少。如果每一帧都在update里遍历大量物品、频繁创建对象后期方块一多帧数会肉眼可见地掉。我给自己定了三条原则能配置化的不写代码普通配方全交给MultiCrafter的配置表动态逻辑尽量用局部变量避免在update里new数组事件型逻辑放onBuild、onDestroy里别每帧轮询。比如附近有冷却液就加速这种判断完全可以用build.nearby每几tick检查一次不需要每帧遍历。我见过有些mod作者把每帧做的事情堆得又重又多结果几十台机器一放游戏直接变PPT这其实不是Mindustry的问题是JS侧该做的优化没做。5. 常见问题与排查技巧实录5.1 配方不生效先查四个点MultiCrafter的配方不生效我遇到的10次里有9次是下面四个原因现象原因解决方块建出来没有配方方块ID和addRecipe第一个参数不一致两边用同一个ID字符串输入带不识别物品物品ID拼写错误或未在mod中定义检查是否存在该物品用Items.xxx进度卡在99%输出槽满了或输出物品无法进入缓存接上输出带或扩大输出缓存游戏里读不到moddependencies里的name与MultiCrafter实际name不一致打开MultiCrafter的mod.json核对排查的时候我习惯先看游戏日志比盲改配置快得多。日志里如果出现ScriptError相关的红字直接把报错行号对应到main.js对应位置基本都能定位到具体是哪个字段写错。5.2 贴图方向错乱和特效消失自定义方块没贴图或者贴图方向不对通常是region命名问题。Mindustry加载贴图时按方块名方向后缀找资源比如plasma-refinery.png是正面图team后缀的贴图用于队伍着色。如果mod里没有对应贴图方块会显示成紫色或直接没有模型。特效不显示则先检查craftEffect和updateEffect是否引用了存在的Fx再换成Fx.none排除干扰一步步缩小范围。我只能说贴图问题虽然不致命但非常影响观感。我自己习惯先随便拿一张游戏内置贴图复制改名占位确认逻辑跑通之后再请人画正式素材这样不会因为美术资源没到位就卡住开发。5.3 语法与加载顺序6.0的Rhino版本较老我在写脚本时踩过两次语法坑一是let在某些小版本下声明不生效二是箭头函数在部分环境里报错。稳妥做法是用var和function所有变量尽量在函数顶层定义。另外多脚本文件之间用require有先后顺序如果B文件用到了A文件里定义的东西一定要在B开头require(A)不要依赖加载顺序的巧合否则偶尔能跑、偶尔报错排查起来非常折磨。最后再分享一个我自己的使用习惯在沙盒模式下用Vars.state.rules关掉敌人和科技锁专心调试机器等配方稳定了再开生存档正式拉产线能省下大量重复读档的时间。从我用MultiCrafter写的那几个mod来看最值钱的地方不是多一个方块而是把配方的复杂度从代码转移到了数据写mod写到后面你维护的其实是配方表和一小撮逻辑而不是一大团if/else。本文还有配套的精品资源点击获取
