Skip to content

简单实现 1.12.2 Forge 模组

更新: 7/4/2026 字数: 0 字 时长: 0 分钟

本方案实现一个 modid 为 demometal 的模组,包含:蓝金属锭、蓝金属矿石方块、整套盔甲、一把剑,并让矿石在主世界自然生成。

一、开发环境要求(1.12.2 专用,版本必须对齐)

第一步:搭好开发环境

项目要求说明
JDKJDK 8(如 1.8.0_202、Adoptium 8u4xx)1.12.2 必须用 JDK 8。用 JDK 9+ 会导致 ForgeGradle 2.3 构建失败,这是最常见的翻车点
Forge MDK1.12.2 - 14.23.5.28xx(推荐 14.23.5.2860 稳定版)从 files.minecraftforge.net 选 1.12.2 的 Mdk 下载
Gradle用 MDK 自带的 gradlew(内含 ForgeGradle 2.3)不要自己装高版本 Gradle,用项目自带包装器最稳
IDEIntelliJ IDEA 或 Eclipse均可,下面以通用命令为准

环境初始化命令(在 MDK 解压目录执行):

bash
# 生成 IDE 工程 + 反混淆运行环境(首次较慢,需联网下载依赖)
./gradlew setupDecompWorkspace
./gradlew genIntellijRuns   # 若用 Eclipse 则执行 ./gradlew eclipse

修改根目录 build.gradle 的版本区段(关键,决定编译目标):

groovy
version = "1.0.0"
group = "com.example.demometal"
archivesBaseName = "demometal"

minecraft {
    version = "1.12.2-14.23.5.2860"   // 对齐你下载的 Forge 版本
    runDir = "run"
    // 1.12.2 官方混淆映射,snapshot 版本按 MDK 默认即可
    mappings = "snapshot_20171003"
}

二、项目结构与模组主类

第二步:搭建模组骨架

2.1 完整目录结构

src/main/
├── java/com/example/demometal/
│   ├── DemoMetal.java              # 模组主类(@Mod 入口)
│   ├── ModItems.java               # 物品:锭、剑、盔甲
│   ├── ModBlocks.java              # 方块:矿石
│   ├── ModMaterials.java           # 自定义工具/盔甲材质
│   ├── RegistryHandler.java        # 统一注册事件处理
│   └── world/OreGenerator.java     # 矿石世界生成
└── resources/
    ├── mcmod.info                  # 模组描述(1.12.2 用这个)
    └── assets/demometal/
        ├── lang/en_us.lang
        ├── lang/zh_cn.lang
        ├── models/item/*.json      # 物品模型
        ├── models/block/*.json     # 方块模型
        ├── blockstates/*.json      # 方块状态
        └── textures/...            # 纹理占位

2.2 模组主类 DemoMetal.java

java
package com.example.demometal;

import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.ItemStack;

// @Mod 是模组的身份声明,modid 全局唯一且必须全小写,Forge 靠它识别模组
@Mod(modid = DemoMetal.MODID, name = DemoMetal.NAME, version = DemoMetal.VERSION,
     acceptedMinecraftVersions = "[1.12.2]")
public class DemoMetal {

    public static final String MODID = "demometal";   // 唯一标识,资源路径也用它
    public static final String NAME  = "Demo Metal Mod";
    public static final String VERSION = "1.0.0";

    // 单例,方便别处引用
    @Mod.Instance(MODID)
    public static DemoMetal instance;

    // 自定义创造模式物品栏分页,让新内容集中显示
    public static final CreativeTabs DEMO_TAB = new CreativeTabs("demometal_tab") {
        @Override
        public ItemStack getTabIconItem() {
            // 用金属锭作为标签页图标(ModItems.INGOT 在注册后可用)
            return new ItemStack(ModItems.INGOT);
        }
    };

    // 预初始化阶段:适合做世界生成注册等早期工作
    @EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        OreGeneratorRegister.register();   // 注册矿石生成器(见第四节)
    }

    // 初始化阶段:可放合成表等(本 Demo 不额外加合成,保持精简)
    @EventHandler
    public void init(FMLInitializationEvent event) {
    }
}

说明:OreGeneratorRegister.register() 是第四节的世界生成注册入口,为清晰起见单独成类,你也可以直接内联。

三、注册逻辑:材质、物品、方块与注册事件

第三步:注册新方块与物品

1.12.2 的核心注册规范(务必遵守,否则物品不显示):

  • 物品/方块不能在主类里 new 完就完事,必须在 RegistryEvent.Register<T> 事件里注册;
  • 每个对象必须设 setRegistryName(modid, name),且要和 setUnlocalizedName 对应;
  • 物品模型(Model)必须在客户端 ModelRegistryEvent 里用 ModelLoader.setCustomModelResourceLocation 注册——这是 1.12.2 物品显示为"黑紫块"的头号原因。

3.1 自定义材质 ModMaterials.java

java
package com.example.demometal;

import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemArmor.ArmorMaterial;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.SoundEvents;
import net.minecraftforge.common.util.EnumHelper;

public class ModMaterials {

    // 自定义工具材质:名称、挖掘等级、耐久、挖掘速度、攻击加成、附魔能力
    public static final Item.ToolMaterial TOOL_DEMO = EnumHelper.addToolMaterial(
            "demometal_tool", 3, 1200, 8.0F, 3.5F, 14);

    // 自定义盔甲材质:名称、贴图前缀、耐久系数、各部位护甲值{脚,腿,胸,头}、
    //                附魔能力、装备音效、韧性
    public static final ArmorMaterial ARMOR_DEMO = EnumHelper.addArmorMaterial(
            "demometal_armor",                 // 内部名
            DemoMetal.MODID + ":demometal",    // 盔甲贴图前缀 -> textures/models/armor/demometal_layer_1/2.png
            18,                                // 耐久系数
            new int[]{2, 6, 7, 3},             // 护甲值:靴、腿、胸、头
            14,                                // 附魔能力
            SoundEvents.ITEM_ARMOR_EQUIP_IRON, // 装备音效(复用铁)
            2.0F);                             // 护甲韧性
}

3.2 物品 ModItems.java(锭、剑、盔甲)

java
package com.example.demometal;

import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemArmor;
import net.minecraft.item.ItemSword;
import net.minecraftforge.fml.common.registry.GameRegistry;

public class ModItems {

    // @ObjectHolder 会在注册完成后自动把实例回填到这些字段,供别处安全引用
    @GameRegistry.ObjectHolder(DemoMetal.MODID + ":demo_ingot")
    public static Item INGOT;

    // 金属锭:普通物品
    public static final Item DEMO_INGOT = new Item()
            .setUnlocalizedName(DemoMetal.MODID + ".demo_ingot")   // 语言键
            .setRegistryName(DemoMetal.MODID, "demo_ingot")        // 注册名
            .setCreativeTab(DemoMetal.DEMO_TAB);                   // 归入自定义标签页

    // 金属剑:用自定义工具材质
    public static final Item DEMO_SWORD = new ItemSword(ModMaterials.TOOL_DEMO)
            .setUnlocalizedName(DemoMetal.MODID + ".demo_sword")
            .setRegistryName(DemoMetal.MODID, "demo_sword")
            .setCreativeTab(DemoMetal.DEMO_TAB);

    // 四件盔甲:注意第二参数指定材质,第三参数是渲染索引,第四参数是部位
    public static final Item DEMO_HELMET = new ItemArmor(
            ModMaterials.ARMOR_DEMO, 1, EntityEquipmentSlot.HEAD)
            .setUnlocalizedName(DemoMetal.MODID + ".demo_helmet")
            .setRegistryName(DemoMetal.MODID, "demo_helmet")
            .setCreativeTab(DemoMetal.DEMO_TAB);

    public static final Item DEMO_CHESTPLATE = new ItemArmor(
            ModMaterials.ARMOR_DEMO, 1, EntityEquipmentSlot.CHEST)
            .setUnlocalizedName(DemoMetal.MODID + ".demo_chestplate")
            .setRegistryName(DemoMetal.MODID, "demo_chestplate")
            .setCreativeTab(DemoMetal.DEMO_TAB);

    public static final Item DEMO_LEGGINGS = new ItemArmor(
            ModMaterials.ARMOR_DEMO, 2, EntityEquipmentSlot.LEGS)  // 腿部渲染索引为2
            .setUnlocalizedName(DemoMetal.MODID + ".demo_leggings")
            .setRegistryName(DemoMetal.MODID, "demo_leggings")
            .setCreativeTab(DemoMetal.DEMO_TAB);

    public static final Item DEMO_BOOTS = new ItemArmor(
            ModMaterials.ARMOR_DEMO, 1, EntityEquipmentSlot.FEET)
            .setUnlocalizedName(DemoMetal.MODID + ".demo_boots")
            .setRegistryName(DemoMetal.MODID, "demo_boots")
            .setCreativeTab(DemoMetal.DEMO_TAB);
}

3.3 方块 ModBlocks.java(矿石)

java
package com.example.demometal;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.SoundType;

public class ModBlocks {

    // 矿石方块:石质材料、需镐挖掘
    public static final Block DEMO_ORE = new Block(Material.ROCK)
            .setUnlocalizedName(DemoMetal.MODID + ".demo_ore")
            .setRegistryName(DemoMetal.MODID, "demo_ore")
            .setHardness(3.0F)          // 硬度(挖掘时间)
            .setResistance(5.0F)        // 抗爆炸
            .setSoundType(SoundType.STONE)
            .setCreativeTab(DemoMetal.DEMO_TAB);

    static {
        // 需要铁镐及以上才能挖出,等级2对应铁镐
        DEMO_ORE.setHarvestLevel("pickaxe", 2);
    }
}

3.4 统一注册处理 RegistryHandler.java(核心中的核心)

java
package com.example.demometal;

import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

// @Mod.EventBusSubscriber 自动把本类注册到事件总线,无需手动 register
@Mod.EventBusSubscriber(modid = DemoMetal.MODID)
public class RegistryHandler {

    // === 注册所有物品(含盔甲、剑、锭) ===
    @SubscribeEvent
    public static void registerItems(RegistryEvent.Register<Item> event) {
        event.getRegistry().registerAll(
                ModItems.DEMO_INGOT,
                ModItems.DEMO_SWORD,
                ModItems.DEMO_HELMET,
                ModItems.DEMO_CHESTPLATE,
                ModItems.DEMO_LEGGINGS,
                ModItems.DEMO_BOOTS
        );

        // 方块必须同时注册它的 ItemBlock,否则物品栏里拿不到、也无法放置
        event.getRegistry().register(
                new ItemBlock(ModBlocks.DEMO_ORE)
                        .setRegistryName(ModBlocks.DEMO_ORE.getRegistryName())
        );
    }

    // === 注册所有方块 ===
    @SubscribeEvent
    public static void registerBlocks(RegistryEvent.Register<Block> event) {
        event.getRegistry().register(ModBlocks.DEMO_ORE);
    }

    // === 注册模型(仅客户端):1.12.2 物品显示的关键,缺了就是黑紫块 ===
    @SubscribeEvent
    @SideOnly(Side.CLIENT)
    public static void registerModels(ModelRegistryEvent event) {
        // 物品模型
        reg(ModItems.DEMO_INGOT);
        reg(ModItems.DEMO_SWORD);
        reg(ModItems.DEMO_HELMET);
        reg(ModItems.DEMO_CHESTPLATE);
        reg(ModItems.DEMO_LEGGINGS);
        reg(ModItems.DEMO_BOOTS);
        // 方块的物品模型
        regBlock(ModBlocks.DEMO_ORE);
    }

    // 把物品与其 json 模型绑定(inventory 变体)
    @SideOnly(Side.CLIENT)
    private static void reg(Item item) {
        ModelLoader.setCustomModelResourceLocation(item, 0,
                new ModelResourceLocation(item.getRegistryName(), "inventory"));
    }

    @SideOnly(Side.CLIENT)
    private static void regBlock(Block block) {
        Item item = Item.getItemFromBlock(block);
        ModelLoader.setCustomModelResourceLocation(item, 0,
                new ModelResourceLocation(block.getRegistryName(), "inventory"));
    }
}

四、矿石世界生成配置

第四步:让矿石自然生成

1.12.2 世界生成靠实现 IWorldGenerator 接口,再用 GameRegistry.registerWorldGenerator 注册。

4.1 OreGenerator.java

java
package com.example.demometal.world;

import com.example.demometal.ModBlocks;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.IChunkProvider;
import net.minecraft.world.gen.IChunkGenerator;
import net.minecraft.world.gen.feature.WorldGenMinable;
import net.minecraft.init.Blocks;
import net.minecraftforge.fml.common.IWorldGenerator;
import net.minecraft.block.state.pattern.BlockMatcher;

import java.util.Random;

public class OreGenerator implements IWorldGenerator {

    // WorldGenMinable:矿脉生成器,参数=生成的方块状态、每簇矿石数量、替换目标
    private final WorldGenMinable oreGen = new WorldGenMinable(
            ModBlocks.DEMO_ORE.getDefaultState(), 6,       // 每簇最多6个
            BlockMatcher.forBlock(Blocks.STONE));          // 只在石头里生成

    @Override
    public void generate(Random random, int chunkX, int chunkZ, World world,
                         IChunkGenerator chunkGenerator, IChunkProvider chunkProvider) {
        // dimension 0 = 主世界,只在主世界生成
        if (world.provider.getDimension() == 0) {
            runGenerator(world, random, chunkX, chunkZ,
                    8,    // 每区块尝试生成次数(矿脉数)
                    5,    // 最低高度 Y
                    45);  // 最高高度 Y
        }
    }

    private void runGenerator(World world, Random rand, int chunkX, int chunkZ,
                              int chancesPerChunk, int minY, int maxY) {
        for (int i = 0; i < chancesPerChunk; i++) {
            // 在当前区块内随机取坐标(*16 转成方块坐标,+8 居中避免跨区块接缝)
            int x = chunkX * 16 + rand.nextInt(16);
            int y = minY + rand.nextInt(maxY - minY);
            int z = chunkZ * 16 + rand.nextInt(16);
            oreGen.generate(world, rand, new BlockPos(x, y, z));
        }
    }
}

4.2 注册入口(配合主类 preInit 调用)

com.example.demometal 包下新建 OreGeneratorRegister.java

java
package com.example.demometal;

import com.example.demometal.world.OreGenerator;
import net.minecraftforge.fml.common.registry.GameRegistry;

public class OreGeneratorRegister {
    public static void register() {
        // 第二个参数是生成权重,数字越大越晚生成(越靠上层覆盖)
        GameRegistry.registerWorldGenerator(new OreGenerator(), 3);
    }
}

五、资源文件:目录结构与核心内容

第五步:配齐资源文件

1.12.2 资源路径全部小写assets/demometal/ 下按类型分目录。

5.1 mcmod.info(模组描述,放 resources 根目录)

json
[
  {
    "modid": "demometal",
    "name": "Demo Metal Mod",
    "description": "一个简易的自定义金属模组:锭、矿石、盔甲、剑。",
    "version": "1.0.0",
    "mcversion": "1.12.2",
    "authorList": ["YourName"]
  }
]

5.2 语言文件

assets/demometal/lang/zh_cn.lang

properties
itemGroup.demometal_tab=演示金属
item.demometal.demo_ingot.name=蓝金属锭
item.demometal.demo_sword.name=蓝金属剑
item.demometal.demo_helmet.name=蓝金属头盔
item.demometal.demo_chestplate.name=蓝金属胸甲
item.demometal.demo_leggings.name=蓝金属护腿
item.demometal.demo_boots.name=蓝金属靴子
tile.demometal.demo_ore.name=蓝金属矿石

assets/demometal/lang/en_us.lang

properties
itemGroup.demometal_tab=Demo Metal
item.demometal.demo_ingot.name=Blue Metal Ingot
item.demometal.demo_sword.name=Blue Metal Sword
item.demometal.demo_helmet.name=Blue Metal Helmet
item.demometal.demo_chestplate.name=Blue Metal Chestplate
item.demometal.demo_leggings.name=Blue Metal Leggings
item.demometal.demo_boots.name=Blue Metal Boots
tile.demometal.demo_ore.name=Blue Metal Ore

5.3 物品模型 JSON(assets/demometal/models/item/

锭/剑/盔甲都用标准平面物品模型。以 demo_ingot.json 为例(其余把 layer0 后缀换成对应物品名即可):

json
{
  "parent": "item/generated",
  "textures": {
    "layer0": "demometal:items/demo_ingot"
  }
}

需要创建的物品模型文件清单(内容同上,仅改 texture 名): demo_ingot.jsondemo_sword.jsondemo_helmet.jsondemo_chestplate.jsondemo_leggings.jsondemo_boots.json

5.4 方块相关 JSON

assets/demometal/blockstates/demo_ore.json

json
{
  "variants": {
    "normal": { "model": "demometal:demo_ore" }
  }
}

assets/demometal/models/block/demo_ore.json

json
{
  "parent": "block/cube_all",
  "textures": {
    "all": "demometal:blocks/demo_ore"
  }
}

assets/demometal/models/item/demo_ore.json(方块在物品栏的样子):

json
{
  "parent": "demometal:block/demo_ore"
}

5.5 纹理占位说明(assets/demometal/textures/

纹理路径尺寸用途
textures/items/demo_ingot.png16×16锭图标
textures/items/demo_sword.png16×16剑图标
textures/items/demo_helmet.png 等 4 个16×16盔甲物品图标
textures/blocks/demo_ore.png16×16矿石方块贴图
textures/models/armor/demometal_layer_1.png64×32盔甲穿身效果(头/胸/靴)
textures/models/armor/demometal_layer_2.png64×32盔甲穿身效果(腿部)

占位做法:先随便放同尺寸的纯色 PNG 保证不报错,后期再替换成正式美术。盔甲穿身贴图的 layer_1/layer_2 命名必须和 ModMaterials 里的贴图前缀 demometal 严格对应,否则穿上盔甲身体是隐形/紫黑。

六、打包步骤与常见报错排查

第六步:打包与排错

6.1 打包操作

bash
# 在项目根目录执行
./gradlew build

构建成功后,成品 jar 位于:

build/libs/demometal-1.0.0.jar

把这个 jar 放进 .minecraft/mods/ 文件夹(游戏必须是 1.12.2 + 对应 Forge),启动即可。

注意:build/libs 里若出现 demometal-1.0.0-sources.jar,那是源码包,不要放进 mods,只放主 jar。

6.2 上线前自检清单(对照排查,避免遗漏)

检查项确认点
注册名一致每个 Item/Block 都调了 setRegistryName,且和模型 json 文件名完全一致
模型已注册ModelRegistryEvent 里每个物品都调了 setCustomModelResourceLocation
ItemBlock 已注册矿石方块的 ItemBlock 在物品注册事件里注册了,否则物品栏拿不到
路径全小写assets/demometal/... 及所有 json/png 文件名全小写
语言键对应setUnlocalizedName 的值和 .lang 里的 key 严格对应

6.3 常见加载报错排查要点

  1. 游戏能进但物品是"黑紫方块 / MISSING TEXTURE" → 模型或纹理没对上。检查:ModelRegistryEvent 是否漏注册;models/item/*.json 文件名是否等于注册名;textures 路径是否写对(注意 json 里写 demometal:items/xxx,实际文件在 textures/items/xxx.png)。

  2. 启动崩溃,日志报 Registry Object not found / Missing registry name → 某个 Item/Block 忘了 setRegistryName,或在事件外提前注册。所有注册必须走 RegistryEvent.Register

  3. 启动崩溃,报 JDK / Gradle 相关错误 → 十有八九是用了 JDK 9+。改回 JDK 8,并确认 JAVA_HOME 指向 JDK 8。

  4. 物品栏找不到新内容setCreativeTab 没设,或设错标签页;确认物品确实注册成功(日志无报错)。

  5. 矿石不生成registerWorldGenerator 没在 preInit 调用;或维度判断写错(主世界是 getDimension() == 0);或高度区间 minY/maxY 设得不合理。注意已生成的旧区块不会补生成,需去新区块或新建世界验证。

  6. 盔甲能拿到但穿上身体隐形models/armor/ 下的 demometal_layer_1.png / demometal_layer_2.png 缺失或命名不符材质前缀。

  7. mods 里 jar 完全不被识别 → 检查 @Modmodid 是否全小写、acceptedMinecraftVersions 是否为 [1.12.2];确认放的是主 jar 不是 sources jar。

总结

按上述六步操作,最终你将得到一个 modid 为 demometal 的标准 1.12.2 Forge 模组,打包出的 demometal-1.0.0.jar 放入 mods 文件夹后可实现:

  • ✅ 创造模式物品栏"演示金属"分页下可找到:蓝金属锭、剑、整套盔甲、矿石方块;
  • ✅ 蓝金属矿石在主世界 Y=5~45 的石头层自然成簇生成,需铁镐及以上挖掘;
  • ✅ 剑具备自定义攻击/耐久属性,盔甲穿戴后护甲值、韧性、附魔能力正常生效。