Adding Custom Blocks

Now, we know how to add items to the game. Let's try something more challenging - adding blocks.

In this chapter, we will be adding this copper ore block to the game:

copper item

Registering our block

Just like last time, we will start by modifying our RegistryHandler.

package com.example.examplemod;

import ...;

public class RegistryHandler {
    // create DeferredRegister objects
+   public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, ExampleMod.MODID);
    public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, ExampleMod.MODID);

    public static void init() {
        // attach DeferredRegisters to the event bus
+       BLOCKS.register(FMLJavaModLoadingContext.get().getModEventBus());
        ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus());
    }


+   // register block
+   public static final RegistryObject<Block> COPPER_ORE = BLOCKS.register("copper_ore", () ->
+           new Block(
+                   Block.Properties
+                           .create(Material.IRON)
+                           .hardnessAndResistance(5.0f, 6.0f)
+                           .sound(SoundType.STONE)
+                           .harvestLevel(1)
+                           .harvestTool(ToolType.PICKAXE)
+           )
+   );

    // register item
    public static final RegistryObject<Item> COPPER = ITEMS.register("copper", () ->
            new Item(
                    new Item.Properties().group(ItemGroup.MATERIALS)
            )
    );
}

+ indicate modified lines

Following the same pattern, we defined another DeferredRegister for registering block objects, subscribed it to the event bus, and registered our block using it.

As you can see, creating a block object is quite a bit more involved than creating an item. Here are just some of the things we can specify:

  • .create(Material.IRON) sets the block material
  • .hardnessAndResistance(5.0f, 6.0f) sets the hardness (the time it takes to mine the block) and resistance (difficulty to blow up the block using TNT)
  • .sound(SoundType.STONE) sets the sound when mining the block and when walking on it
  • .harvestLevel(1) sets the tool level needed (wood = 0 / stone = 1 / iron = 2 / diamond = 3)
  • .harvestTool(ToolType.PICKAXE) sets the type of tool needed

More Registries: BlockItems

Unlike last time, we are not done with registries yet. We need to also register a corresponding item with our block. This is what's displayed when you hold a block in your hand.

package com.example.examplemod;

import ...;

public class RegistryHandler {
    // create DeferredRegister objects
    public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, ExampleMod.MODID);
    public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, ExampleMod.MODID);

    public static void init() {
        // attach DeferredRegisters to the event bus
        BLOCKS.register(FMLJavaModLoadingContext.get().getModEventBus());
        ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus());
    }

    // register block
    public static final RegistryObject<Block> COPPER_ORE = BLOCKS.register("copper_ore", () ->
            new Block(
                    Block.Properties
                            .create(Material.IRON)
                            .hardnessAndResistance(5.0f, 6.0f)
                            .sound(SoundType.STONE)
                            .harvestLevel(1)
                            .harvestTool(ToolType.PICKAXE)
            )
    );

    // register item
    public static final RegistryObject<Item> COPPER = ITEMS.register("copper", () ->
            new Item(
                    new Item.Properties().group(ItemGroup.MATERIALS)
            )
    );

+   public static final RegistryObject<Item> COPPER_ORE_ITEM = ITEMS.register("copper_ore", () ->
+           new BlockItem(
+                   COPPER_ORE.get(),
+                   new Item.Properties().group(ItemGroup.BUILDING_BLOCKS)
+           )
+   );
}

+ indicate modified lines

As you can see, the process is almost the same as registering an item. The only difference is that we need to use BlockItem instead of Item and pass a reference to the block that the item is linked with.

Defining texture, model, and lang translation (plus blockstate)

With our block registered, it's time to add all the info needed to display it. The src/main/resources folder should look something like this in the end.

src/main/resources
├── assets
│   └── examplemod
│       ├── blockstates
│       │   └── copper_ore.json*
│       ├── lang
│       │   └── en_us.json*
│       ├── models
│       │   ├── block
│       │   │   └── copper_ore.json*
│       │   └── item
│       │       ├── copper.json
│       │       └── copper_ore.json*
│       └── textures
│           ├── blocks
│           │   └── copper_ore.png*
│           └── items
│               └── copper.png
├── META-INF
│   └── mods.toml
└── pack.mcmeta

filenames with a * next to them are ones added or modified


copper_ore.png again will just be the png file display at the start of the chapter.

models/block/copper_ore.json will be the model file for the block:

> models/block/copper_ore.json
{
  "parent": "block/cube_all",
  "textures": {
    "all": "examplemod:blocks/copper_ore"
  }
}

models/item/copper_ore.json will be the model file for the corresponding item:

> models/item/copper_ore.json
{
  "parent": "examplemod:block/copper_ore"
}

en_us.json will be modified to add the English name of our block:

> en_us.json
{
  "item.examplemod.copper": "Copper",
+ "block.examplemod.copper_ore": "Copper Ore"
}

One new file type that we see is blockstates/copper_ore.json. The blockstate file helps us define how the block would look like under different situations. For example, oak logs will look different depending on the direction they are placed; therefore, their blockstate file would look something like this:

> oak_log.json
{
  "variants": {
    "axis=x": {
      "model": "minecraft:block/oak_log_horizontal",
      "x": 90,
      "y": 90
    },
    "axis=y": {
      "model": "minecraft:block/oak_log"
    },
    "axis=z": {
      "model": "minecraft:block/oak_log_horizontal",
      "x": 90
    }
  }
}

In our case, we only have one state for our block, so blockstates/copper_ore.json will be quite simple:

> blockstates/copper_ore.json
{
  "variants": {
    "": { "model": "examplemod:block/copper_ore" }
  }
}

Results

With the code and display info added, we can now see our block in game 💯💯💯

copper block in game
copper block in game

If you are the observant kind, you will notice that we are not able to get the block when mining it in survival. That is be something that we will fix in the next chapter with loot tables. Meanwhile, go build something cool with your new block!

results matching ""

    No results matching ""