Basic Modding - First Block (1.8 and higher)

Goal

I want to show you how to create a very basic block and the associated model files in your mod.

Difficulty

2/10 - Quite simple

Prerequisites

Forge Version

This Tutorial was created using Forge version 11.14.0.1299 for Minecraft 1.8. If anything does not work for other versions, please contact me!


Creating a Block

To create Blocks we first want to have a special class to access them easily. We create a new class named "ModBlocks" in a new package named com.<yourname>.<yourmodname>.blocks. In this tutorial I will use the package com.bedrockminer.tutorial.blocks.

Mod Blocks should be final so that it can't be subclassed. Then we create a method called "createBlocks()" and make it public and static.

 

The class should now look like this:

ModBlocks.java:
package com.bedrockminer.tutorial.blocks;

public final class ModBlocks {

    public static void createBlocks() {
    }
}

Now we can create the object for our block. To do this, we just write this code in your class:

ModBlocks.java:
public final class ModBlocks {

    public static Block tutorialBlock;

    public static void createBlocks() {
    }
}

Once we have done this, we have to import the class Block. Press Ctrl+Shift+O and select net.minecraft.block.Block.

Now we have an object of the class Block which represents the block we are creating.

 

The method createBlocks has to be called inside the preInit method of the CommonProxy, because the Blocks need to be registered both on server and on client side.

CommonProxy.java:
public void preInit(FMLPreInitializationEvent e) {
    ModItems.createItems();
    ModBlocks.createBlocks();
}

Now we need to define a basic block type. Therefore, we create a new class for basic blocks. This class will be called BasicBlock and is located in the blocks package as well.

BasicBlock.java:
package com.bedrockminer.tutorial.blocks;

public class BasicBlock {
}

This class needs to extends the Block class, because we want it to be a special type of a block. Of course, we need to import Block again.

Also, we should create a few new constructors which contain the most important setter methods for a block. This makes the creation process of a block a lot easier.

BasicBlock.java:
public class BasicBlock extends Block {

    public BasicBlock(String unlocalizedName, Material material, float hardness, float resistance) {
        super(material);
        this.setUnlocalizedName(unlocalizedName);
        this.setCreativeTab(CreativeTabs.tabBlock);
        this.setHardness(hardness);
        this.setResistance(resistance);
    }

    public BasicBlock(String unlocalizedName, float hardness, float resistance) {
        this(unlocalizedName, Material.rock, hardness, resistance);
    }

    public BasicBlock(String unlocalizedName) {
        this(unlocalizedName, 2.0f, 10.0f);
    }
}

The most complex constructor, which is called by the other ones later, first calls the superclass Block's constructor and passes the block's material to this constructor. The material defines basic settings for the block.

Then, several setter methods are called to set the unlocalized name, the creative tab, the hardness (time to mine) and the resistance (resistance against explosions) of the block.

There are a lot more setters than these two, this table shows a few of them:

new Block(Material) constructor sets the material for the block which defines basic settings  
.setUnlocalizedName required sets the name of the block  
.setCreativeTab optional sets the creative tab to display the block in default: tabBlock
.setHardness recommended sets how long it takes to break the block default:depends on material; stone:1.5F; obsidian:50.0F
.setResistance recommended sets the block's resistance against explosions default:depends on material; stone:10.0F; obsidian:2000.0F
.setStepSound recommended sets the step sound of a block default:depends on material
.setLightOpacity optional sets how much light is subtracted when going through this block
This is only used if isOpaqueCube() returns false
default: 16 (completely opaque); maximum: 0 (100% translucent)
.setLightLevel optional sets how much light is emitted from the block default: 0.0F (nothing); maximum: 1.0F (like full sunlight)
.setHarvestLevel highly recommended
sets the tool and the tool level to break a block. If you don't use this, the break level is defined by the material

tool: "pickaxe", "axe", "shovel"

level: 0=wood; 1=stone; 2=iron; 3=diamond tool

.setBlockUnbreakable optional makes the block unbreakable in survival

only bedrock and barriers are unbreakable

.setTickRandomly optional if true the block receives random update ticks. Used for example for the decaying of leaves.

default: false

 

To play around with these settings, we'll make our block emitting light in a second.

 

Our Block class is already finished and we can proceed by creating our block.

ModBlocks.java:
public static void createBlocks() {
    GameRegistry.registerBlock(tutorialBlock = new BasicBlock("tutorial_block"), "tutorial_block");
}

This line does several things at once.

First, a new block is created using the constructor of the BasicBlock class. The new block is stored in our previously created object. Then, the block gets registered with the game by calling GameRegistry.registerBlock. During registration, the block gets a second name, the registration name. This should be the same as the unlocalized name.

 

To make the block emit light, we just add .setLightLevel(1.0f) behind the constructor:

ModBlocks.java:
public static void createBlocks() {
    GameRegistry.registerBlock(tutorialBlock = new BasicBlock("tutorial_block").setLightLevel(1.0f), "tutorial_block");
}

Once our block is registered, it is available ingame. It can be acquired from the creative tabs (if .setCreativeTab has been called at least once) or with the /give command. To get the block with the /give command, you need to use the following command:

/give @p modid:registration_name

In our mod, this would be:

/give @p tutorial:tutorial_block

Registering a model file

If we try to give ourselves the Block, we see that it has no texture attached to it. Instead, it is rendered as a purple-black cube.

To change this, we need to add a model file for the block. Before we do this, we need to register the model. Therefore, it's useful to create a special class. The one I created is called BlockRenderRegister and is located in com.bedrockminer.tutorial.client.render.blocks. This class is final and contains a single static method named registerBlockRenderer().

BlockRenderRegister.java:
package com.bedrockminer.tutorial.client.render.blocks;

public final class BlockRenderRegister {
        public static void registerBlockRenderer() {
        }
}

The registerBlockRenderer method is only used on client side, of course, thus we call it from the Client Proxy. It needs to be called during the init phase.

ClientProxy.java:
@Override
public void init(FMLInitializationEvent e) {
    super.init(e);

    ItemRenderRegister.registerItemRenderer();
    BlockRenderRegister.registerBlockRenderer();
}

If we want to register a model file for a Block, we need to run this line of code:

BlockRenderRegister.java:
Minecraft.getMinecraft().getRenderItem().getItemModelMesher()
    .register(Item.getItemFromBlock(block), meta, new ModelResourceLocation("modid:blockname", "inventory"));

This is actually similar to the method used to registering an item's model.

The first line above is used to get Minecraft's ItemModelMesher. This object is used to handle the different model files.

The register method takes several parameters: First, the block we want to register, converted to the item that is representing it. Then the metadata for which this registration should be valid. By default, this is 0. Finally, we need a ModelResourceLocation to define the location of the model file. The constructor of the ModelResourceLocation class itself takes two arguments: First, the identifier for the item. This is the modid, a colon and the unlocalized name. Second, the String "inventory".

 

For our tutorial Block this line would look like this:

BlockRenderRegister.java:
Minecraft.getMinecraft().getRenderItem().getItemModelMesher()
    .register(ModBlocks.tutorialBlock, 0, new ModelResourceLocation("tutorial:tutorial_block", "inventory"));

If we have multiple Blocks its useful to create a method for registering the model file. This saves a lot of code because the long method chain above is reduced to a single method call.

The method looks like this:

BlockRenderRegister.java:
public static String modid = Main.MODID;

public static void reg(Block block) {
    Minecraft.getMinecraft().getRenderItem().getItemModelMesher()
    .register(Item.getItemFromBlock(block), 0, new ModelResourceLocation(modid + ":" + block.getUnlocalizedName().substring(5), "inventory"));
}

This method runs the same code as above, with the only difference that the identifier is created using a static variable for the modid and the unlocalized name of the Block. For this one we need to call .substring(5) because the method getUnlocalizedName() returns "tile.unlocalizedName". The .substring(5) method cuts of the first five characters and leaves us with the unlocalized name we need. With this method, the registration process is as easy as calling the method:

BlockRenderRegister.java:
public static String modid = Main.MODID;

public static void registerBlockRenderer() {
    reg(ModBlocks.tutorialBlock);
}

public static void reg(Block block) {
    Minecraft.getMinecraft().getRenderItem().getItemModelMesher()
    .register(Item.getItemFromBlock(block), 0, new ModelResourceLocation(modid + ":" + block.getUnlocalizedName().substring(5), "inventory"));
}

The model file(s)

The main model file of a block is its state file. This one defines the different models for different states of the block (e.g. the different growing states of the wheat block).

For our Block this looks quite simple because we don't have different states.

The file needs to be named like the name you registered the block with to the GameRegistry and it is located in assets.<modid>.blockstates. The name you used in the BlockRenderRegister has nothing to do with the name of the blockstates file at all, it is only used for blocks as items. The blockstates file instead is used for block rendering in the world.

blockstates.tutorial_block.json:
{
    "variants": {
        "normal": { "model": "tutorial:tutorial_block" }
    }
}

This file assigns the model "tutorial_block" from the mod "tutorial" with the normal variant of the block. The normal variant is used if no variants are specified.

 

The specified model name is used for both the block and the item model. So we need to create a model file for the block in the package assets.<modid>.models.block as well as a model file for the item in assets.<modid>.models.item.

models.block.tutorial_block.json:
{
    "parent": "block/cube_all",
    "textures": {
        "all": "tutorial:blocks/tutorial_block"
    }
}
models.item.tutorial_block.json:
{
    "parent":"tutorial:block/tutorial_block",
    "display": {
        "thirdperson": {
            "rotation": [ 10, -45, 170 ],
            "translation": [ 0, 1.5, -2.75 ],
            "scale": [ 0.375, 0.375, 0.375 ]
        }
    }
}

These two files now define the rendering of the block. The first one is the actual block model. It uses the parent cube_all, so it is a full cube with the same texture on each side.

The item file uses the block file as it's parent and redefines the display for the third person view. This is a standard transformation that is applied to every block. If this is not done, the block would be nearly as big as the player. Unfortunately, we cannot create a default file here as we did with the item because a model can only have one parent.


We still need the texture the model file is pointing to. According to the value of "all" this file has to be in the package assets.tutorial.textures.blocks and has to be named tutorial_block.png. The texture I'll be using is this one:

If we place this in the correct package, refresh the workspace and run minecraft, our block should be textured properly.

Picture - Show

You can download the code used in this tutorial as a .zip file from here.


Recommended tutorials to continue with


Comments and Questions:

If you want to report modding problems, please make sure to include the code in a pastebin link or something else! Don't just write "It doesn't work", otherwise your post will be deleted. For more complicated problems, please use the troubleshooter form.