1. 项目概述:从“能用”到“会玩”的模组自定义之路
如果你玩《我的世界》的模组整合包,尤其是那些大型科技或魔法向的,大概率见过一些奇奇怪怪的合成配方。比如,用四个钻石和一个下界之星合成一个“终极工具核心”,或者把原本需要八个黑曜石才能做的末影箱,改成只需要四个。这些改动,并不是模组作者预设的,而是整合包作者或者服务器管理员通过一个叫做CraftTweaker的工具实现的。CraftTweaker,直译过来就是“合成调整者”,它允许你通过编写脚本(ZenScript),对游戏内几乎所有的合成配方进行增、删、改。这不仅仅是改几个物品那么简单,它意味着你可以重新设计整个游戏的经济系统和进度曲线,让模组之间产生全新的化学反应。
我最初接触CraftTweaker,是因为自己搭建一个以“艰难生存”为主题的私人整合包。我发现原版和模组的一些合成配方,要么太简单破坏了挑战性,要么太繁琐让游戏体验变得枯燥。比如,工业模组(IndustrialCraft)的机器框架需要大量铁,而另一个魔法模组(Thaumcraft)的注魔台又需要很多稀有材料,两者之间毫无关联。我希望它们能产生联动,让玩家在探索一个模组时,获得的资源能服务于另一个模组,从而形成一个自洽的、有深度的科技树。CraftTweaker就是实现这个愿景的钥匙。它不像一些图形化修改工具那样有诸多限制,它提供了近乎底层的API接口,只要你懂一点逻辑,就能实现天马行空的想法。本教程基于1.18.2版本,这是目前长期支持且模组生态较为稳定的一个版本,其原理和思路同样适用于相近的版本。
2. 环境准备与核心概念解析
在开始写第一行脚本之前,我们需要把舞台搭建好。CraftTweaker本身只是一个框架,它需要对应的加载器(如Forge或Fabric)以及一些辅助模组才能发挥全部威力。理解这几个核心组件的角色,是避免后续各种“脚本不生效”问题的关键。
2.1 必需模组与工具选型
首先,你需要一个《我的世界》1.18.2的Forge客户端。Fabric端也有类似的工具(如KubeJS),但CraftTweaker在Forge端的生态更成熟,教程和社区支持也更丰富。安装好Forge后,你需要将以下模组放入游戏的mods文件夹:
- CraftTweaker:这是核心模组,提供了基础的脚本加载和API框架。没有它,你的脚本文件就只是一堆文本。
- ZenScript语言依赖:在1.18.2版本,CraftTweaker将ZenScript语言分离成了一个独立模组,通常名为
CraftTweaker-forge-1.18.2-9.1.xxx.jar这样的格式已经包含了语言支持,但请务必在模组下载页面确认其依赖关系。有时你可能需要单独下载一个ZenScript模组。 - JEI (Just Enough Items):这不是CraftTweaker的必需依赖,但它是你进行配方管理和调试的“眼睛”。JEI会在游戏内显示所有物品的合成表。当你用CraftTweaker修改配方后,JEI中的显示也会同步更新,这是验证脚本是否生效最直观的方式。
注意:务必确保所有模组的版本与你的游戏版本(1.18.2)以及彼此之间兼容。最稳妥的方法是使用一个成熟的整合包作为基础,或者从CurseForge等模组站点的“关系”页面查看模组的依赖和兼容版本。
安装完成后,启动一次游戏,如果能在模组列表里看到CraftTweaker,并且没有报错崩溃,那么基础环境就准备好了。接下来,你会在游戏根目录下发现一个新的文件夹:scripts。你所有自定义合成表的脚本文件(后缀名为.zs)都将放在这里。
2.2 ZenScript脚本基础与文件结构
ZenScript是CraftTweaker使用的专用脚本语言,它的语法类似于Java和JavaScript的混合体,但为了适配《我的世界》而做了大量简化。你不需要成为编程高手,只需要理解几个核心概念和结构就能上手。
脚本文件(.zs)的创建与加载顺序: 在scripts文件夹内,你可以创建任意多个.zs文件,例如start.zs,machines.zs,magic.zs。游戏加载时,会按字母顺序加载这些文件。这很重要,因为如果你在b.zs里使用了一个在a.zs里定义的变量,而b.zs先被加载了,就会报错。一种常见的做法是创建一个init.zs或00_init.zs文件,在里面定义所有后续脚本会用到的全局变量和函数,确保它最先被加载。
基础语法一览:
// 这是一行注释,不会被程序执行 /* 这是一个 多行注释 */ // 1. 导入语句:告诉脚本我要使用哪个模组的API import crafttweaker.api.item.IItemStack; // 导入物品栈类 import mods.industrialforegoing.DissolutionChamber; // 导入某个模组的特定机器配方接口 // 2. 变量定义:定义一个物品,比如“铁锭” val ironIngot = <item:minecraft:iron_ingot>; // val代表不可变的常量 // <item:modid:item_name> 是获取游戏内物品的标准格式,也叫Bracket Handler。 // 3. 打印日志:调试神器,信息会输出到游戏日志文件(logs/debug.log)和控制台 print("Hello CraftTweaker!");理解“物品表示法”<item:modid:item_name>是第一步。minecraft是原版的命名空间,iron_ingot是铁锭的内部名称。对于模组物品,你需要知道它的Mod ID和物品名,JEI可以帮你快速获取:在JEI界面将鼠标悬停在物品上,按Ctrl+U(显示用途)或Ctrl+R(显示配方)时,屏幕左上角通常会显示该物品的ID,格式如minecraft:iron_ingot,直接复制即可用在脚本里。
3. 核心操作:增、删、改合成配方实战
掌握了环境和语法基础,我们就可以进入最核心的实操环节:如何具体地移除、添加和修改合成配方。我将通过几个典型的例子,带你一步步走完流程。
3.1 移除配方:清理与简化
移除配方是最常见的操作之一,目的通常是为了禁用过于强大或破坏平衡的原版/模组配方,或者为自定义的新配方让路。
移除工作台合成配方: 假设我觉得原版的“信标”太容易获得,想移除它的合成配方。
// 移除信标的合成配方 recipes.remove(<item:minecraft:beacon>);这行代码会移除所有输出物为信标的工作台合成配方。但请注意,它不会移除通过村民交易、怪物掉落等其他方式获得信标的途径。
移除特定形状的配方: 有时一个物品有多个合成配方(例如木头可以合成4个木板,木板也可以合成回木头),我们只想移除其中一个。这时需要指定具体的配方输入。
// 移除用4个橡木木板合成橡木工作台的配方(即无序合成) recipes.removeShaped(<item:minecraft:crafting_table>, [[<item:minecraft:oak_planks>, <item:minecraft:oak_planks>], [<item:minecraft:oak_planks>, <item:minecraft:oak_planks>]]);recipes.removeShaped用于移除有序合成配方。方括号[][]定义了一个2x2的网格,每个位置都指定了必须是橡木木板。如果你想移除的是无序合成(即材料摆放位置无关),需要使用recipes.removeShapeless,并且传入一个材料列表。
// 移除用任意4个木板合成工作台的配方(无序合成) recipes.removeShapeless(<item:minecraft:crafting_table>, [<item:minecraft:oak_planks>, <item:minecraft:oak_planks>, <item:minecraft:oak_planks>, <item:minecraft:oak_planks>]);实操心得:在移除配方后,一定要立刻进入游戏,通过JEI查看该物品是否还有合成途径。如果移除失败,首先检查物品ID是否拼写正确(大小写、下划线),其次检查你的脚本文件是否有语法错误(游戏启动时控制台会报红字错误)。一个稳妥的做法是,先只写一条移除命令,进游戏测试成功后再添加更多。
3.2 添加配方:创造与联动
添加配方是发挥创意的核心。我们可以创建全新的合成路径,将不同模组的物品联系起来。
添加有序合成配方: 我想添加一个更昂贵的“终极工作台”,需要一圈下界合金锭和一个普通工作台。
// 添加一个有序合成配方:终极工作台 recipes.addShaped("ultimate_crafting_table", <item:minecraft:crafting_table>.withTag({CustomModelData: 1}), [ [<item:minecraft:netherite_ingot>, <item:minecraft:netherite_ingot>, <item:minecraft:netherite_ingot>], [<item:minecraft:netherite_ingot>, <item:minecraft:crafting_table>, <item:minecraft:netherite_ingot>], [<item:minecraft:netherite_ingot>, <item:minecraft:netherite_ingot>, <item:minecraft:netherite_ingot>] ]);"ultimate_crafting_table":这是配方的唯一名称标识符,可以任意取,但最好有意义且不重复。<item:minecraft:crafting_table>.withTag({CustomModelData: 1}):这里用了一个技巧。我们无法直接创建一个名字叫“终极工作台”的新物品,但可以利用NBT标签中的CustomModelData,配合资源包,让同一个工作台物品拥有不同的外观。输出在游戏里看起来还是一个普通工作台,但通过资源包可以将其渲染成不同的纹理。如果你想创建真正的新物品,需要借助内容添加模组(如ContentTweaker)。- 下面的三维数组定义了3x3的合成网格。空位用
null表示。
添加无序合成配方: 我想添加一个配方,用1个钻石和8个铁锭无序合成出1个下界合金碎片(这显然是个魔改配方)。
recipes.addShapeless("netherite_scrap_from_diamond", <item:minecraft:netherite_scrap>, [ <item:minecraft:diamond>, <item:minecraft:iron_ingot>, <item:minecraft:iron_ingot>, <item:minecraft:iron_ingot>, <item:minecraft:iron_ingot>, <item:minecraft:iron_ingot>, <item:minecraft:iron_ingot>, <item:minecraft:iron_ingot>, <item:minecraft:iron_ingot> ]);addShapeless只需要一个一维数组列出所有需要的材料即可,顺序无关。
3.3 修改配方:平衡与整合
很多时候,我们不想完全移除一个配方,而是想调整它的材料。这可以通过“先移除,后添加”的组合拳来实现。
假设我觉得工业模组(Immersive Engineering)的“电弧炉”初始造价太高,想把其中的“钢块”换成“铁块”。
// 首先,移除原版电弧炉的配方 recipes.remove(<item:immersiveengineering:arc_furnace>); // 然后,添加我们修改后的配方 recipes.addShaped("cheaper_arc_furnace", <item:immersiveengineering:arc_furnace>, [ [<item:immersiveengineering:steel_scaffolding>, <item:immersiveengineering:coil_mv>, <item:immersiveengineering:steel_scaffolding>], [<item:immersiveengineering:coil_mv>, <item:immersiveengineering:heavy_engineering>, <item:immersiveengineering:coil_mv>], [<item:immersiveengineering:steel_scaffolding>, <item:immersiveengineering:coil_mv>, <item:immersiveengineering:steel_scaffolding>] ]);在这个修改后的配方里,我移除了需要“钢块”的部分,用其他更前期的材料(钢脚手架、中型线圈)替代。你需要根据JEI查看到的原配方,精确地规划每个格子的替换物。
处理模组机器配方: 对于熔炉、切石机等原版设备,以及模组添加的各种机器(如打粉机、冶炼厂),它们的配方不属于工作台合成,需要使用专门的接口。以移除和添加熔炉配方为例:
// 移除铁矿石的熔炉烧炼配方(即不能再烧铁矿石得铁锭) furnace.removeRecipe(<item:minecraft:iron_ingot>); // 通过输出物移除 // 添加一个新的熔炉配方:用煤炭块烧制钻石(非常魔改) furnace.addRecipe("diamond_from_coal_block", <item:minecraft:diamond>, <item:minecraft:coal_block>, 10.0); // 参数说明:配方名,输出物品,输入物品,经验值(可选)每个模组的机器都有自己的接口,例如mods.thermal.Smelter.addRecipe(热力膨胀的感应炉),mods.immersiveengineering.Crusher.addRecipe(沉浸工程的粉碎机)。你需要查阅CraftTweaker的官方文档或该模组的支持文档来找到正确的接口名和参数格式。
4. 高级技巧与模组联动实战
当你熟悉了基础操作后,就可以尝试一些更高级的技巧,来实现复杂的整合包设计逻辑。
4.1 使用变量与函数提升效率
当你在多个配方中反复使用同一组复杂材料时,定义变量可以极大简化脚本,也便于后期统一修改。
// 定义一些常用的材料组合 val advancedCircuit = <item:thermal:upgrade_augment_3>; // 假设我们定义高级电路 val machineFrame = <item:industrialforegoing:machine_frame_pity>; // 机器框架 // 定义一个函数来添加同一系列机器的配方 function addMachineRecipe(machineName as string, outputItem as IItemStack, middleItem as IItemStack) { recipes.addShaped(machineName, outputItem, [ [advancedCircuit, <item:minecraft:redstone>, advancedCircuit], [<item:minecraft:iron_ingot>, middleItem, <item:minecraft:iron_ingot>], [machineFrame, <item:minecraft:gold_ingot>, machineFrame] ]); } // 使用函数快速添加多个机器配方 addMachineRecipe("electric_furnace", <item:techreborn:electric_furnace>, <item:minecraft:furnace>); addMachineRecipe("grinder", <item:techreborn:grinder>, <item:minecraft:iron_block>);这样,如果你想调整所有这类机器的合成框架,只需要修改advancedCircuit或machineFrame这两个变量的定义,或者修改函数体内的合成格局即可,无需逐个修改几十个配方。
4.2 实现阶段化解锁与合成表替换
这是大型整合包的核心设计思想。例如,在游戏前期禁用所有高级合金的合成,直到玩家解锁了“高炉”科技。
// 在游戏开始的脚本中,移除高级合金的配方 recipes.remove(<item:modern_industrialization:steel_ingot>); recipes.remove(<item:modern_industrialization:aluminum_ingot>); // 然后,为高炉添加专属的合金配方(这通常需要模组本身支持高炉配方接口) // 假设高炉的接口是 mods.modern_industrialization.BlastFurnace mods.modern_industrialization.BlastFurnace.addRecipe("steel_smelting", <item:modern_industrialization:steel_ingot>, 100, 1200, [ <item:minecraft:iron_ingot> * 2, <item:minecraft:coal> * 1 ]);这样,玩家在获得高炉之前,无法通过任何途径合成钢锭。他们必须按照你设计的科技树,先发展出高炉技术。你还可以配合任务模组(如FTB Quests),在玩家完成“建造高炉”任务后,通过命令或脚本动态加载另一个包含高级合金配方的.zs文件,实现真正的阶段化解锁。
4.3 跨模组资源统一与替代
大型整合包常有功能重复的模组。例如,多个模组都添加了铜、锡矿石。为了避免世界生成冗余和玩家困惑,通常会统一使用一种矿石,并让其他模组的物品能兑换成统一标准的物品。
// 假设我们决定以“热力膨胀”的铜锭为标准 val standardCopper = <item:thermal:copper_ingot>; // 移除其他模组铜锭的合成与获取途径(这里以“沉浸工程”为例) recipes.remove(<item:immersiveengineering:ingot_copper>); furnace.removeRecipe(<item:immersiveengineering:ingot_copper>); // 移除烧炼配方 // 添加兑换配方:1个沉浸工程铜锭可以无序合成成1个标准铜锭 recipes.addShapeless("convert_ie_copper", standardCopper, [<item:immersiveengineering:ingot_copper>]); // 你甚至可以修改矿石的掉落,但这通常需要更底层的模组(如LootTweaker)配合,或者使用熔炉配方统一烧炼产物。通过这种方式,玩家在探索世界中获得的任何模组的铜资源,最终都能汇流到同一个物品上,用于你设计的所有合成配方,保持了经济系统的一致性。
5. 调试、排查与最佳实践实录
脚本写完了,满怀期待地进入游戏,却发现JEI里配方没变,或者游戏直接崩溃了。别担心,这是每个CraftTweaker使用者都会经历的过程。高效的调试和遵循最佳实践能让你少走弯路。
5.1 常见问题与排查技巧
问题1:脚本加载了,但配方没有任何变化。
- 检查点1:语法错误。打开游戏目录下的
logs/crafttweaker.log文件。这是CraftTweaker的专属日志。如果脚本有错误,这里会有明确的错误信息,告诉你哪一行、哪个字符出了问题。常见的错误包括括号不匹配、缺少分号、物品ID拼写错误。 - 检查点2:配方冲突。你添加的新配方可能与未成功移除的原版配方,或其他模组添加的配方冲突。MC通常只显示一个合成途径。尝试在创造模式用你指定的材料摆放一下,看是否能合成出物品。
- 检查点3:脚本文件未加载。确认你的
.zs文件放在了正确的scripts文件夹内(在游戏根目录,与mods文件夹同级),并且没有放在子文件夹里(除非你使用了#loader等预处理指令进行了配置)。
问题2:游戏启动时崩溃。
- 检查点1:模组版本不兼容。确保CraftTweaker及其依赖的ZenScript模组与你的Forge版本完全匹配。1.18.2的Forge有多个版本(如40.2.0, 40.2.10),模组通常只兼容一个范围。
- 检查点2:脚本中存在致命错误。有时脚本错误太严重会导致加载失败。尝试逐个注释掉脚本文件中的内容(在行首加
//),来定位导致崩溃的特定行。 - 检查点3:使用了不存在的物品或方法。如果你引用的模组物品ID错误,或者调用的API方法在当前版本不存在,也可能导致崩溃。对照模组文档或使用
/ct dump命令(游戏内)输出的物品列表进行核对。
问题3:配方在JEI中显示,但实际无法合成。
- 检查点1:NBT标签限制。如果你在配方中使用了带有特定NBT标签的物品(如附魔工具),那么合成时输入物品必须拥有完全一致的NBT标签。通常,在定义配方输入时,应尽量避免使用带NBT的物品,除非你有特殊需求。使用
*通配符可以忽略NBT:<item:minecraft:diamond_pickaxe>*表示任意耐久的钻石镐。 - 检查点2:合成类型错误。你添加的是有序合成 (
addShaped),但尝试时摆放成了无序的形状,或者反之。仔细检查合成格局数组。
5.2 开发流程与最佳实践
- 备份!备份!备份!:在开始大规模修改前,备份你的
saves(存档)和scripts文件夹。一个错误的脚本可能导致存档物品丢失或损坏。 - 增量开发与测试:不要一次性写上千行脚本。写一小段(比如移除或添加一个配方),就保存文件,重启游戏测试。使用
print()函数输出调试信息,确认脚本段落被正确执行。 - 充分利用
/ct命令:在游戏中,你有权限的情况下,可以输入一系列调试命令:/ct recipes:查看某个物品的所有合成配方。例如/ct recipes minecraft:iron_ingot。/ct hand:获取手中物品的完整ID(包括NBT),直接复制到脚本里使用。/ct syntax:检查当前加载的所有脚本是否有语法错误。/ct dump:将游戏内所有物品、实体、配方等信息导出为文本文件,是编写脚本时最重要的参考资料。
- 代码组织与注释:将脚本按功能分文件存放,如
remove_vanilla.zs,add_machines.zs,tweaks_magic.zs。在每个文件开头和关键段落添加清晰的注释,说明这段代码的意图。几个月后当你回头修改时,会感谢自己。 - 版本控制:如果你使用Git等版本控制工具来管理你的整合包,那么
scripts文件夹无疑是需要纳入管理的核心部分。每次修改都有记录,可以轻松回滚到任何可用的版本。
我个人在实际操作中的体会是,CraftTweaker最强大的地方不在于它能改变单个配方,而在于它能让你像设计师一样,从全局视角重构整个模组包的体验。最开始可能会被语法和报错困扰,但一旦你成功让第一个自定义配方生效,那种“世界规则由我定义”的成就感是无与伦比的。从一个简单的“增加木板合成棍子的数量”开始,逐步尝试更复杂的模组联动,最终你将能打造出一个拥有独特灵魂、自洽经济系统和引人入胜进程的整合包。记住,所有复杂的修改,都是由一行行简单的addShaped和remove组成的。动手去试,多查文档(CraftTweaker的Wiki非常全面),多看看热门整合包的脚本是怎么写的,你会进步飞快。最后一个小技巧:在测试阶段,可以把游戏模式设为创造,并给自己一个“合成表修改器”之类的模组物品,可以随时重载脚本 (/reload命令),而无需重启游戏,这会极大提升你的调试效率。