Mission · 最终要交付什么
不是只让代码“没有红线”,而是完成一条可验证的道具生产线。
自定义道具看起来很小,却正好覆盖 Forge 模组最重要的基础:版本配套、注册表、事件总线、命名空间、客户端资源和构建流程。学完后,你不只会复制一行 new Item,还会知道游戏为什么能找到它、为什么贴图必须放在固定路径、出现紫黑方块时应该查哪一个文件。
搭好指定版本
让 Minecraft 1.20.1、Forge 47.4.20、ForgeGradle、JDK 17 和 IntelliJ 使用同一套工程配置。
注册真正的物品
用 DeferredRegister 和 RegistryObject 创建 teacherma_item:star_crystal。
连接代码与资源
添加中英文翻译、物品模型 JSON、透明 PNG 贴图,并理解每一层路径对应的资源 ID。
运行、排错和发布
使用 runClient 验证 Mods 列表与游戏效果,再通过 build 得到可安装 JAR。
/give @s teacherma_item:star_crystal 后,玩家手中出现星辉水晶;./gradlew.bat clean build 最终显示 BUILD SUCCESSFUL。Version lock · 先锁版本再写代码
本课只讲这一套组合;不要把 1.21、NeoForge 或旧 Forge 教程拼进来。
Minecraft 模组 API 会随游戏版本变化。同样叫“注册物品”,1.16、1.20.1、1.21 和 NeoForge 的导入路径、事件写法与资源格式都可能不同。本课完全依据 Forge 1.20.1-47.4.20 MDK 的实际模板编写,并已用 JDK 17 完整构建和启动。
| 组件 | 本课版本 | 负责什么 | 检查位置 |
|---|---|---|---|
| Minecraft Java | 1.20.1 | 目标游戏与资源格式 | gradle.properties |
| Forge | 47.4.20 | 加载器、事件与注册 API | forge_version |
| Forge MDK | 1.20.1-47.4.20 | Gradle 工程模板与示例源码 | 压缩包文件名 |
| JDK | 17(64 位) | 编译、运行 Gradle 与开发客户端 | java -version |
| IntelliJ IDEA | Community 2025.x | 编辑、Gradle 同步、运行和调试 | Help → About |
| Gradle | 由 MDK Wrapper 固定 | 下载依赖、处理资源和构建 JAR | gradle/wrapper |
先建立注册表思维
游戏不是通过 Java 变量名认识物品,而是通过唯一资源 ID。我们的 ID 是 teacherma_item:star_crystal:冒号前是模组命名空间 teacherma_item,冒号后是物品路径 star_crystal。翻译键、模型位置、贴图路径和命令都围绕这两个部分连接。
DeferredRegister
先收集“稍后要注册”的对象,等 Forge 到正确生命周期再统一注册。它能避免在注册表还没准备好时过早创建对象。
RegistryObject
保存一个安全引用。对象真正注册完成后,通过 STAR_CRYSTAL.get() 获取物品,而不是在任何地方再 new Item。
MOD Event Bus
物品注册和创造模式物品栏构建属于模组加载生命周期,所以要连接到 context.getModEventBus()。
客户端资源
Java 负责“它是什么”,JSON 和 PNG 负责“它叫什么、怎么显示”。代码编译成功不代表资源路径一定正确。
Setup · 准备 MDK 与 IntelliJ
把压缩包正确解到空目录,再用 JDK 17 导入 Gradle 工程。
安装 64 位 JDK 17
Forge 1.20.1 官方文档要求 JDK 17。安装后打开 PowerShell,分别执行下面两条命令;两条都应显示 17,而不是只有 java 有结果、javac 找不到。
java -version
javac -version确认 MDK 文件
从 Forge 官方 Files 页面选择 Minecraft 1.20.1,在 All Versions 中找到 47.4.20,下载这一行的 Mdk,不是 Installer。官方列出的 MDK 校验值为:
MD5 F3F4C354CB2955DCA6580713442EE4B1
SHA1 AE89B7ADEC05802FB805C9345B509029C6952EBBWindows 可用 Get-FileHash 文件路径 -Algorithm SHA1 检查。校验值不一致时重新下载,不要继续使用损坏文件。
解压到简单路径
新建一个空目录,例如 C:\Projects\teacherma-forge-item,把 ZIP 里的内容直接解到这里。打开目录后应该立即看到 build.gradle、gradlew.bat、gradle.properties 和 src,不能再多套一层同名文件夹。
用 IntelliJ 打开工程根目录
选择 File → Open,点包含 build.gradle 的根目录。若 IntelliJ 询问是否信任项目,确认它来自你刚验证的 Forge 官方 MDK 后选择 Trust Project。第一次同步会下载 Forge、Minecraft 和映射文件,可能需要数分钟。
src。External Libraries 中能看到 ms-17,说明 JDK 17 已被 IntelliJ 识别。设置 Project SDK = 17
按 Ctrl + Alt + Shift + S 打开 Project Structure,选择 Project。将 SDK 选为 JDK 17,Language level 选 17。JDK 名字可能叫 Temurin-17、ms-17 或 jdk-17,只要实际版本是 17 即可。
ms-17 是本机 JDK 的显示名称,不要求你安装同一个发行商。设置 Gradle JVM = Project SDK 17
打开 Settings → Build, Execution, Deployment → Build Tools → Gradle,把 Gradle JVM 设为 Project SDK。这一项决定真正执行构建的 Java;只改 Project SDK、却让 Gradle 继续使用 21,仍可能遇到版本错误。
Wrapper;Gradle JVM 选择 Project SDK ms-17。点击 OK 后,在 Gradle 工具窗口执行 Reload All Gradle Projects。./gradlew.bat build。空 MDK 能构建成功,才说明网络、JDK 与 Gradle 基础环境正确;之后出错就更容易定位到自己的改动。Identity · 给模组换成自己的身份
先改 gradle.properties,再创建与包名一致的 Java 目录。
打开根目录的 gradle.properties。Minecraft 与 Forge 版本保持 MDK 原值,只替换 Mod Properties 区域。mod_id 必须是小写英文、数字和下划线;不要写中文、空格或连字符。
minecraft_version=1.20.1
forge_version=47.4.20
mapping_channel=official
mapping_version=1.20.1
mod_id=teacherma_item
mod_name=TeacherMa Item Tutorial
mod_license=All Rights Reserved
mod_version=1.0.0
mod_group_id=de.teacherma.itemtutorial
mod_authors=Teacher Ma
mod_description=A small Forge 1.20.1 tutorial mod that adds the Star Crystal item.| 字段 | 本课值 | 容易出错的地方 |
|---|---|---|
mod_id | teacherma_item | 必须与 @Mod 常量和资源命名空间完全一致 |
mod_group_id | de.teacherma.itemtutorial | 与 Java 顶部 package 和目录层级一致 |
mod_name | TeacherMa Item Tutorial | 这是 Mods 菜单显示名,可以有空格 |
mod_version | 1.0.0 | 修改代码后是否升级由你决定,不等于 Forge 版本 |
删除示例 Java 包
MDK 原本带有 com.example.examplemod 包、ExampleMod.java 和 Config.java。确认空模板已经构建成功后,可以删除这个示例包,再创建 de.teacherma.itemtutorial。不要保留两个带不同 @Mod ID 的入口,否则你会同时加载不需要的示例模组。
mods.toml 版本:47.4.20 MDK 使用占位符,例如 ${mod_id}。Gradle 在 processResources 阶段把 gradle.properties 的值填进去。保持模板结构,只修改属性文件更不容易漏项。File map · 先把所有文件放对位置
路径就是资源 ID 的一部分;一个字母错位都会让贴图或翻译失联。
在 src/main 下建立下面的结构。IntelliJ 中右键目录选择 New → Package 创建 Java 包;资源目录用 New → Directory。所有目录名均使用小写。
teacherma-forge-item/
├─ build.gradle
├─ gradle.properties
├─ gradlew.bat
└─ src/main/
├─ java/de/teacherma/itemtutorial/
│ ├─ TeacherMaItemMod.java
│ └─ item/
│ └─ ModItems.java
└─ resources/
├─ META-INF/
│ └─ mods.toml
├─ assets/teacherma_item/
│ ├─ lang/
│ │ ├─ en_us.json
│ │ └─ zh_cn.json
│ ├─ models/item/
│ │ └─ star_crystal.json
│ └─ textures/item/
│ └─ star_crystal.png
└─ data/teacherma_item/recipes/
└─ star_crystal.jsonassets 是显示资源
语言、模型、贴图主要由客户端读取。路径里的 teacherma_item 必须等于 mod_id。
data 是游戏数据
配方、战利品表、标签等由数据包系统读取。配方文件也使用相同命名空间。
Java 包防止类冲突
de.teacherma.itemtutorial 应当换成你拥有或能保证唯一的包名,不要在正式项目继续使用 com.example。
文件名决定路径部分
star_crystal.json、star_crystal.png 与注册名 star_crystal 保持同名,排错会简单很多。
Java · 注册道具并放进创造模式
分成两个类:入口类负责接线,ModItems 专门管理物品。
第一步:创建 ModItems.java
在 de.teacherma.itemtutorial.item 包中创建 ModItems。下面是本课完整代码,可以直接复制;先保证包名与自己的目录一致。
package de.teacherma.itemtutorial.item;
import de.teacherma.itemtutorial.TeacherMaItemMod;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.Rarity;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
public final class ModItems {
public static final DeferredRegister<Item> ITEMS =
DeferredRegister.create(ForgeRegistries.ITEMS, TeacherMaItemMod.MOD_ID);
public static final RegistryObject<Item> STAR_CRYSTAL = ITEMS.register(
"star_crystal",
() -> new Item(new Item.Properties()
.stacksTo(16)
.rarity(Rarity.UNCOMMON))
);
private ModItems() {
}
public static void register(IEventBus eventBus) {
ITEMS.register(eventBus);
}
}注册表类型
ForgeRegistries.ITEMS 表示我们要注册的是物品。方块、实体、声音各有自己的注册表,不能混用。
最终资源 ID
MOD_ID 是 teacherma_item,注册名是 star_crystal,合起来就是 teacherma_item:star_crystal。
Supplier 延迟创建
() -> new Item(...) 不是立刻运行,而是把创建方法交给 Forge,在正确注册阶段调用。
道具属性
stacksTo(16) 限制一格最多 16 个;UNCOMMON 让名称使用稀有度颜色。两行都可继续修改。
item → ModItems。第二步:创建入口类 TeacherMaItemMod.java
入口类必须有 @Mod 注解。47.4.20 MDK 的构造函数接收 FMLJavaModLoadingContext context,直接从参数取得 MOD 事件总线。旧教程可能使用静态 get();本课跟随指定 MDK,不混用旧写法。
package de.teacherma.itemtutorial;
import de.teacherma.itemtutorial.item.ModItems;
import net.minecraft.world.item.CreativeModeTabs;
import net.minecraftforge.event.BuildCreativeModeTabContentsEvent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
@Mod(TeacherMaItemMod.MOD_ID)
public class TeacherMaItemMod {
public static final String MOD_ID = "teacherma_item";
public TeacherMaItemMod(FMLJavaModLoadingContext context) {
IEventBus modEventBus = context.getModEventBus();
ModItems.register(modEventBus);
modEventBus.addListener(this::addCreativeTabItems);
}
private void addCreativeTabItems(BuildCreativeModeTabContentsEvent event) {
if (event.getTabKey() == CreativeModeTabs.INGREDIENTS) {
event.accept(ModItems.STAR_CRYSTAL);
}
}
}ModItems.register(modEventBus)?只声明 DeferredRegister 相当于填好报名表,还没有提交。把它注册到 MOD Event Bus,Forge 才会在物品注册阶段处理列表。漏掉这一行时,代码可能编译成功,但游戏完全不知道这个物品。BuildCreativeModeTabContentsEvent 只负责把已经注册的物品显示在“原材料(Ingredients)”标签页。它不负责注册物品本身。命令依然可以用唯一 ID 取得道具。
Client resources · 名称、模型和贴图
Java 已经创造“对象”,现在让玩家真正看见和读懂它。
1. 中文与英文名称
在 assets/teacherma_item/lang 创建两个 JSON。物品翻译键规则是 item.命名空间.路径,因此本课必须写成 item.teacherma_item.star_crystal。
{
"item.teacherma_item.star_crystal": "星辉水晶"
}{
"item.teacherma_item.star_crystal": "Star Crystal"
}2. 物品模型 JSON
在 assets/teacherma_item/models/item 创建 star_crystal.json。minecraft:item/generated 是常见的平面物品模型;layer0 指向贴图资源,注意这里不写 .png。
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "teacherma_item:item/star_crystal"
}
}3. 放入透明 PNG 贴图
把道具图保存为 assets/teacherma_item/textures/item/star_crystal.png。本课示例使用 64×64 透明 PNG。16×16、32×32 或 64×64 都可以,但像素风贴图缩放时不要使用模糊插值;透明区域必须真的带 Alpha,不能只是黑色或白色背景。
64x64 PNG (32-bit color),说明尺寸与透明通道都正确。底部面包屑完整显示 assets → teacherma_item → textures → item → star_crystal.png。| 代码或文件 | 组合后的含义 |
|---|---|
MOD_ID + "star_crystal" | teacherma_item:star_crystal |
| 翻译键 | item.teacherma_item.star_crystal |
| 模型文件 | assets/teacherma_item/models/item/star_crystal.json |
模型中的 layer0 | teacherma_item:item/star_crystal |
| 最终贴图 | assets/teacherma_item/textures/item/star_crystal.png |
// 说明,也不要在最后一项后多写逗号。文件必须保存为 UTF-8。推荐用 IntelliJ 的 Reformat Code,让缩进和括号一眼可查。Optional data · 添加一个可生存获得的配方
注册让物品“存在”,配方让它在生存模式中“可获得”。
这一步不是显示道具的必要条件,但能帮助你理解 data 目录。创建 data/teacherma_item/recipes/star_crystal.json,让四个紫水晶碎片包围一个荧石粉,合成一颗星辉水晶。
{
"type": "minecraft:crafting_shaped",
"pattern": [
" A ",
"AGA",
" A "
],
"key": {
"A": {
"item": "minecraft:amethyst_shard"
},
"G": {
"item": "minecraft:glowstone_dust"
}
},
"result": {
"item": "teacherma_item:star_crystal",
"count": 1
}
}result.item 是 1.20.1 可用写法。不要从更新版本文档复制已经改变的配方结果结构。数据包格式是最容易被“新版教程”悄悄影响的部分之一。Run & verify · 启动并逐项验收
第一次启动的目标不是“玩一会儿”,而是收集四个明确证据。
先保存所有文件,然后在 IntelliJ 右侧 Gradle 工具窗口找到 Tasks → fg_runs → runClient(不同 IDEA 版本分组名可能稍有不同)并双击。也可以在工程根目录的 Terminal 运行 Wrapper 命令:
./gradlew.bat runClient证据 1:版本正确
证据 2:Forge 找到了模组入口
在主菜单点击 Mods,再选择 TeacherMa Item Tutorial。右侧应显示版本 1.0.0、Mod ID teacherma_item、作者和描述。这些内容由 gradle.properties 经 mods.toml 展开后提供。
@Mod、Mod ID、资源处理和入口加载都已经通过。证据 3:命令能取得注册物品
创建一个允许作弊的测试世界。按 T 打开聊天框,输入以下命令。输入到 teacherma_item: 时应出现自动补全;执行后玩家获得一颗星辉水晶。
/give @s teacherma_item:star_crystal
证据 4:创造模式原材料标签中可见
切换到创造模式,打开物品栏并进入 Ingredients(原材料)标签,或在搜索框输入 Star Crystal / 星辉水晶。能找到它,说明 BuildCreativeModeTabContentsEvent 也正确执行。
注册 ID
/give 自动补全并成功,证明注册表中存在 teacherma_item:star_crystal。
翻译
中文语言下显示“星辉水晶”,而不是 item.teacherma_item.star_crystal。
模型和贴图
手中与快捷栏都显示水晶图案,不是紫黑错误纹理。
物品属性
同一格最多堆叠 16 个,名称使用 Uncommon 稀有度颜色。
创造模式标签
Ingredients 标签能找到物品,证明标签事件判断与 accept 正确。
配方
四个紫水晶碎片加一个荧石粉能合成,证明 data 资源已载入。
Debug clinic · 按症状定位
一次只处理第一条根本错误,不要同时改 Java、JSON 和版本。
Gradle 报 Java 版本、toolchain 或 class file 错误
先查三处:PowerShell 的 java -version、Project SDK、Gradle JVM 都应指向 JDK 17。改完 Gradle JVM 后执行 Reload All Gradle Projects。不要因为电脑已有 JDK 21 就把本课 MDK 的 java.toolchain 改成 21。
net.minecraftforge... 全部变红或 Gradle 无法解析依赖
通常是 Gradle 同步没有完成、网络下载中断或错误地用“普通 Java 项目”打开了 src 子目录。重新用包含 build.gradle 的根目录打开,确认 Gradle JVM 17,点击 Reload。网络恢复后可运行 ./gradlew.bat --refresh-dependencies build。
启动时提示 Failed to create mod instance
检查 @Mod(TeacherMaItemMod.MOD_ID)、常量 teacherma_item、gradle.properties 的 mod_id 和 Java 包路径。还要确认构造函数是本 MDK 的 TeacherMaItemMod(FMLJavaModLoadingContext context),没有混入别的版本教程。
/give 找不到 teacherma_item:star_crystal
先确认 Mods 列表有当前模组,再检查入口构造函数是否调用 ModItems.register(modEventBus)。若只声明了 DeferredRegister 而没有连接事件总线,Java 会编译,但物品不会进入注册表。
物品存在,但显示紫黑错误贴图
注册代码已经成功,集中检查三个位置:模型文件是否为 assets/teacherma_item/models/item/star_crystal.json;layer0 是否为 teacherma_item:item/star_crystal;PNG 是否为 assets/teacherma_item/textures/item/star_crystal.png。Windows 隐藏扩展名时尤其要防止文件其实叫 star_crystal.png.png。
物品名显示为 item.teacherma_item.star_crystal
这是翻译缺失,不是注册失败。检查当前游戏语言对应的文件是否存在,例如简体中文必须是小写 zh_cn.json;键名必须与注册 ID 一致;JSON 不能有尾逗号或注释,并保存为 UTF-8。
命令能获得物品,但创造模式标签没有
检查入口类是否监听 addCreativeTabItems,判断是否使用 CreativeModeTabs.INGREDIENTS,并调用 event.accept(ModItems.STAR_CRYSTAL)。这只影响标签展示,不影响注册表和 /give。
日志很长,不知道看哪里
从控制台第一条红色 ERROR、第一个 Caused by 或 JSON 文件名开始读。后面几百行往往只是同一个根因造成的连锁失败。复制完整的第一段错误和它前后约 20 行,远比只截最后一屏有效。
./gradlew.bat build 判断编译与资源语法;再看 Mods 列表判断入口;再用 /give 判断注册;最后判断翻译、模型、贴图和配方。每一步都只测试一层。Build & install · 构建可安装 JAR
开发客户端验证通过后,用 Wrapper 做一次干净构建。
先退出开发客户端,避免 Windows 锁定文件。在工程根目录运行:
./gradlew.bat clean build成功后打开 build/libs,本课生成的安装文件是 teacherma_item-1.0.0.jar。如果还有带 -sources 的文件,它只用于查看源码,不放进普通客户端。
安装匹配的 Forge 客户端
普通游戏启动器需要 Minecraft 1.20.1 的 Forge 47.4.20。MDK 只用于开发,不等于已经给日常启动器安装 Forge。
找到 mods 目录
Windows 默认常见位置是 %appdata%\.minecraft\mods。不同启动器可以使用独立实例目录,以启动器显示的游戏路径为准。
只保留一个版本
把 teacherma_item-1.0.0.jar 放入 mods。同一个 Mod ID 不要同时放多个版本,否则 Forge 会拒绝启动。
先用测试世界
启动 Forge 47.4.20 配置,先检查 Mods 列表和新建测试世界。安装、升级或移除模组前备份重要存档。
下载本课完整可编译工程
包含 Forge 47.4.20 MDK Wrapper、两个 Java 类、翻译、模型、原创星辉水晶贴图和配方。下载后请仍然设置 JDK 17,并让 IntelliJ 完成 Gradle 同步。
Practice · 从照做变成会改
每次只改一个变量,预测结果,再运行验证。
修改堆叠数量
把 stacksTo(16) 改成 8。重新运行后用命令给予 16 个,观察物品栏如何分成两格。
创建第二件道具
注册 moon_shard,补齐翻译、模型和贴图。列出所有必须保持同名的位置。
自定义创造模式标签
阅读 Forge 1.20.1 Items 文档,注册自己的 CreativeModeTab,用星辉水晶作为图标。
五分钟自测
1. 为什么注册名只写 star_crystal,命令却写完整 ID?
DeferredRegister 创建时已经绑定命名空间 teacherma_item,注册方法只需提供路径部分。游戏和资源系统对外使用完整 ResourceLocation:teacherma_item:star_crystal。
2. 为什么不能直接保存一个全局 new Item(...)?
可注册对象必须在 Forge 的注册生命周期中进入对应注册表。DeferredRegister 推迟创建并返回 RegistryObject 安全引用,避免过早初始化和未注册对象。
3. ModItems.register(modEventBus) 漏掉会怎样?
待注册列表没有接到 MOD Event Bus,Forge 不会处理它。代码可能编译,甚至入口模组能出现在 Mods 页面,但 /give 找不到自定义物品。
4. 命令成功但贴图紫黑,应该先改 Java 吗?
不应该。命令成功说明 Java 注册已完成。应检查模型 JSON 的路径、layer0 资源 ID、PNG 文件名和透明贴图位置。
5. 为什么 Project SDK 与 Gradle JVM 都要检查?
Project SDK 服务于编辑器分析,Gradle JVM 真正执行构建。两者可以指向不同 Java;只改一个会产生“编辑器看起来正常,Gradle 却失败”的情况。
术语卡
| 术语 | 一句话解释 |
|---|---|
| MDK | Forge 提供的 Mod Developer Kit,包含 Gradle 工程、Wrapper、配置与示例源码。 |
| Mod ID | 模组的唯一小写标识,也是资源命名空间,例如 teacherma_item。 |
| Registry | 游戏把唯一资源 ID 映射到物品、方块、实体等对象的系统。 |
| DeferredRegister | Forge 推荐的延迟注册辅助类,在正确生命周期创建并注册对象。 |
| RegistryObject | 注册对象的安全引用,在注册完成后通过 get() 取得实例。 |
| Event Bus | 发布和接收生命周期或游戏事件的通道;本课使用 MOD Event Bus。 |
| Wrapper | 工程自带的 gradlew.bat,确保使用项目规定的 Gradle 版本。 |
Official references · 官方资料
版本、注册方式和物品 API 均以 Forge 官方资料为基线。
- Forge 1.20.1 官方下载页:找到 47.4.20 的 MDK、校验值与 Gradle 坐标。
- Forge 1.20.1 Getting Started:JDK 17、MDK 解压、IDE 与基础构建。
- Structuring Your Mod:唯一 Java 包名与工程组织建议。
- Registries:
DeferredRegister、RegistryObject与注册生命周期。 - Items:基础物品属性、创造模式标签和自定义标签。
- Mod Files:
mods.toml的位置、字段与依赖含义。
Lesson complete
你已经完成了 Forge 模组最小但完整的一次闭环。
现在你手里不只是一个水晶图标,而是一套可复用的方法:固定版本、验证空工程、注册对象、连接资源、逐层验收、构建交付。下一次做食物、武器或工具时,仍然从唯一 ID 和注册流程出发,再逐步增加自定义类与行为。