(This article is one of a series of commentary articles)
First article: Introduction Previous article: 8. Addition and generation of ore Next article: 99. Mod output
In the above, I learned about the addition of ore. Next, let's add a tree and let the world generate it. It's getting complicated, so let's do our best together.
First, add the log blocks that make up Qi. We will add blocks by referring to 2. Adding blocks, but there are some differences, so let's look at them in order.
BlockList.java
//...
public class BlockList {
public static Block ExampleLog = new LogBlock(
MaterialColor.CYAN,
Block.Properties.create(Material.WOOD, MaterialColor.BLUE)
.hardnessAndResistance(2.0F)
.sound(SoundType.WOOD)
.lightValue(6))
.setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_log"));
@SubscribeEvent
public static void registerBlocks(RegistryEvent.Register<Block> event) {
event.getRegistry().registerAll(
ExampleLog
);
}
@SubscribeEvent
public static void registerBlockItems(RegistryEvent.Register<Item> event) {
event.getRegistry().registerAll(
new BlockItem(ExampleLog, new Item.Properties().group(ExampleItemGroup.DEFAULT))
.setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_log"))
);
}
}
It's basically the same, but it's not just a Block
class, it's a LogBlock
class.
This is a subclass of the RotatedPillarBlock
, a subclass of the Block
class. RotatedPillarBlock
is Block
with the addition of rotation. And it seems that LogBlock
has added something related to the display on the map.
The argument of the constructor is incremented by 1 and passes the color to be displayed on the map in the MaterialColor
class. Block.Properties.create ()
also has more arguments and passes the color, but this is the color on the map at the time of non-vertical installation, and it seems that the previous one is the color at the time of vertical installation. Choose these appropriately.
As in the example, we will set resources
, but since the texture of the log block differs depending on the surface, this setting is shown below. These are based on the log block of minecraft, so this is fine unless you do something special.
\src\main\resources
├ assets
│ └ example_mod
│ ├ blockstates
│ │ └ example_log.json
│ ├ lang
│ │ └ en_us.json
│ │ └ ja_jp.json
│ ├ models
│ │ ├ block
│ │ │ └ example_log.json
│ │ └ item
│ │ └ example_log.json
│ └ textures
│ ├ blocks
│ │ ├ example_log.png
│ │ └ example_log_top.png
│ └ items
└ data
└ example_mod
└ loot_tables
└ blocks
└ example_log.json
blockstates\example_log.json
{
"variants": {
"axis=y": { "model": "example_mod:block/example_log" },
"axis=z": { "model": "example_mod:block/example_log", "x": 90 },
"axis=x": { "model": "example_mod:block/example_log", "x": 90, "y": 90 }
}
}
By writing like this, the member variable ʻaxis(normal direction of the surface) of the
LogBlock (strictly speaking,
RotatedPillarBlock) class will be conditional branched depending on either x, y, or z. I am. ([Official documentation](https://mcforge.readthedocs.io/en/1.14.x/models/blockstates/forgeBlockstates/) also says something like that.) Descriptions such as
" x ": 90
" y ": 90` are rotation angles. In short, it is rotated so that the display shows that the log is tilted down based on the vertical installation state.
models\block\example_log.json
{
"parent": "block/cube_column",
"textures": {
"end": "example_mod:blocks/example_log_top",
"side": "example_mod:blocks/example_log"
}
}
For parent
, specify block / cube_column
. This allows you to apply textures that are cubic and distinguish between top and bottom and sides. Let's specify the path to each texture file.
models\item\example_log.json
{
"parent": "example_mod:block/example_log"
}
This can be done by inheriting the model file of block
as before.
en_us.jp
{
"block.example_mod.example_log": "Example Log"
}
ja_jp.json
{
"block.example_mod.example_log": "Example log"
}
Language files are the same as before.
\loot_table\blocks\example_log.json
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "example_mod:example_log"
}
]
}
]
}
As usual.
You have added a block with directions like this.
** There is one more thing to do about log blocks. ** ** The block addition itself has been completed so far, but it will be necessary at the stage of constructing the tree later, so let's do it here.
Add the log block you just added to the minecraft: logs
tag.
As I mentioned briefly in 6. Add Recipes, "tags" are a group of objects that have common factors. In minecraft, there is a tag that summarizes the equivalent of logs, which is used when processing trees. Therefore, if you include this log block in this tag, you can avoid unnecessary implementation.
\src\main\resources
├ assets
└ data
├ example_mod
└ minecraft
└ tags
└ blocks
└ logs.json
** Create a ** \ src \ main \ resources \ data \ minecraft \ tags \ blocks
folder in your project folder and place logs.json
in it. Make sure this name is the same.
logs.json
{
"replace": false,
"values": [
"example_mod:example_log"
]
}
By giving false
to replace
, the description in this file will be integrated into minecraft: logs
of the same name. Let's specify the block in values
.
When you move the cursor to the block in debug mode, if # minecraft: logs
is displayed like the underlined part in red, it is complete.
Next, add the leaf blocks that make up the tree. Again, let's look at the base in order, referring to 2. Adding blocks.
BlockList.java
//...
public class BlockList {
public static Block ExampleLeaves = new LeavesBlock(
Block.Properties.create(Material.LEAVES)
.hardnessAndResistance(0.2F)
.tickRandomly()
.sound(SoundType.PLANT)
.lightValue(8))
.setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_leaves"));
@SubscribeEvent
public static void registerBlocks(RegistryEvent.Register<Block> event) {
event.getRegistry().registerAll(
ExampleLeaves
);
}
@SubscribeEvent
public static void registerBlockItems(RegistryEvent.Register<Item> event) {
event.getRegistry().registerAll(
new BlockItem(ExampleLeaves, new Item.Properties().group(ExampleItemGroup.DEFAULT))
.setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_leaves"))
);
}
}
Leaf blocks are made with Leaves Block
. Others are as usual.
Set up resources
.
\src\main\resources
├ assets
│ └ example_mod
│ ├ blockstates
│ │ └ example_leaves.json
│ ├ lang
│ │ └ en_us.json
│ │ └ ja_jp.json
│ ├ models
│ │ ├ block
│ │ │ └ example_leaves.json
│ │ └ item
│ │ └ example_leaves.json
│ └ textures
│ ├ blocks
│ │ └ example_leaves.png
│ └ items
└ data
└ example_mod
└ loot_tables
└ blocks
└ example_leaves.json
blockstates\example_leaves.json
{
"variants": {
"": { "model": "example_mod:block/example_leaves" }
}
}
models\block\example_leaves.json
{
"parent": "block/leaves",
"textures": {
"all": "example_mod:blocks/example_leaves"
}
}
Specify block / leaves
for parent
. You can apply translucent textures (depending on your graphic settings).
models\item\example_leaves.json
{
"parent": "example_mod:block/example_leaves"
}
en_us.jp
{
"block.example_mod.example_leaves": "Example Leaves"
}
ja_jp.json
{
"block.example_mod.example_leaves": "Example leaf"
}
These are as usual.
\loot_table\blocks\example_log.json
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:alternatives",
"children": [
{
"type": "minecraft:item",
"conditions": [
{
"condition": "minecraft:alternative",
"terms": [
{
"condition": "minecraft:match_tool",
"predicate": {
"item": "minecraft:shears"
}
},
{
"condition": "minecraft:match_tool",
"predicate": {
"enchantments": [
{
"enchantment": "minecraft:silk_touch",
"levels": {
"min": 1
}
}
]
}
}
]
}
],
"name": "example_mod:example_leaves"
},
{
"type": "minecraft:item",
"conditions": [
{
"condition": "minecraft:survives_explosion"
},
{
"condition": "minecraft:table_bonus",
"enchantment": "minecraft:fortune",
"chances": [
0.05,
0.0625,
0.083333336,
0.1
]
}
],
"name": "example_mod:example_sapling"
}
]
}
]
}
]
}
The loot_table
file is quite different so far. The leaf block (which I've skipped so far) has multiple drops and depends on enchantments and tools, so when implemented properly it looks like this.
For details, see [Reference Page](https://minecraft-ja.gamepedia.com/%E3%83%AB%E3%83%BC%E3%83%88%E3%83%86%E3%83%BC% I will give the explanation to E3% 83% 96% E3% 83% AB), but I will explain it briefly.
This is "a drop from a block, the number is one, it returns one of the children
. There are two children
s, and it returns the leaf block when first broken with scissors or Silk Touch I tool Things. Next, the seedlings are returned with a random probability (increased probability with lucky enchantment) in other cases. " Here, it will come out first and the order will change, but we will add seedlings in the next section.
This is based on the Ork leaves from minecraft. Let's look at each one and write as you like.
Finally, set the MaterialColor
. This has the effect that the leaves and grass change color depending on the biome. You can ignore this if it is troublesome. If you ignore it, the same color will be displayed in all biomes, so prepare a texture with that color.
BlockList.java
//...
public class BlockList {
//...
@SubscribeEvent
public static void registerBlockColors(ColorHandlerEvent.Block event) {
event.getBlockColors().register((p_210229_0_, p_210229_1_, p_210229_2_, p_210229_3_) -> {
return p_210229_1_ != null && p_210229_2_ != null ? BiomeColors.getFoliageColor(p_210229_1_, p_210229_2_) : FoliageColors.getDefault();
}, ExampleLeaves);
}
@SubscribeEvent
public static void registerBlockItemColors(ColorHandlerEvent.Item event) {
event.getItemColors().register((p_210235_1_, p_210235_2_) -> {
BlockState blockstate = ((BlockItem)p_210235_1_.getItem()).getBlock().getDefaultState();
return event.getBlockColors().getColor(blockstate, (IEnviromentBlockReader)null, (BlockPos)null, p_210235_2_);
}, ExampleLeaves);
}
}
This is also registered at the same time in BlockList.java
which is declared and registered. There is something called ColorHandlerEvent
, so it will be reflected by usingregister ()
. Do this for each block and item. Variables etc. are a list of characters that do not make sense, but this can be changed because it is just pulled from the obfuscated minecraft code.
For a block, BiomeColors.getFoliageColor ()
gets the color according to the biome in which the block exists and registers it.
For items, the default color will be retrieved and registered.
In both cases, pass the color (obtained by the lambda expression) as the first argument of register ()
and the object that sets the color as the second argument.
You don't have to understand this area deeply unless you try to do something difficult.
For the actual color, see [Reference Page](https://minecraft-ja.gamepedia.com/%E3%83%90%E3%82%A4%E3%82%AA%E3%83%BC%E3% 83% A0 # .E3.83.90.E3.82.A4.E3.82.AA.E3.83.BC.E3.83.A0.E3.82.AB.E3.83.A9.E3.83.BC It is described in detail in .E3.81.AE.E6.B1.BA.E5.AE.9A). Basically, it seems that a grayscale leaf block texture is prepared and the color is put on it, but when I prepared a file with a tint this time, the actual block also became that tint, so probably internally I wonder if they are adding.
These are difficult to explain in words, but both are classes that manage trees. The Tree
class is a class that manages the tree itself and is required in connection with the seedlings that will be added later. On the other hand, the TreeFeature
class only manages things related to tree generation, and you can also get the corresponding TreeFeature
from the Tree
class.
\src\main\java\jp\koteko\example_mod\
├ blocks
│ └ trees
│ └ ExampleTree.java
├ items
├ lists
├ world
│ └ features
│ └ ExampleTreeFeature.java
└ ExampleMod.java
(I'm going to refer to the file layout in various ways, but if you're worried, it's quite appropriate.)
ExampleTreeFeature.java
package jp.koteko.example_mod.world.features;
import jp.koteko.example_mod.lists.BlockList;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import net.minecraft.world.gen.feature.TreeFeature;
import net.minecraftforge.common.IPlantable;
public class ExampleTreeFeature extends TreeFeature {
public ExampleTreeFeature() {
super(NoFeatureConfig::deserialize, false, 4, BlockList.ExampleLog.getDefaultState(), BlockList.ExampleLeaves.getDefaultState(), false);
setSapling((IPlantable) BlockList.ExampleSapling);
}
}
Create a ʻExampleTreeFeature class by inheriting the
TreeFeatureclass. The arguments passed to the inheritance source constructor are as follows in order. The first argument is probably not particularly useful in an instance of
NoFeatureConfig. The second argument is the
boolean value that determines whether or not any notification is possible, and it was imitated because it was false in other trees. I will add it when I understand something. The third argument is the ʻint
value that determines the minimum height of the tree.
The fourth argument is the BlockState
of the trunk block.
The fifth argument is the BlockState
of the leaf block.
The sixth argument is the boolean
value that determines whether to extend the ivy to the tree.
Also, setSapling ()
sets the seedlings, and passes the seedling instance here. Seedlings will be implemented in the next section.
ExampleTree.java
package jp.koteko.example_mod.blocks.trees;
import jp.koteko.example_mod.world.features.ExampleTreeFeature;
import net.minecraft.block.trees.Tree;
import net.minecraft.world.gen.feature.AbstractTreeFeature;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import javax.annotation.Nullable;
import java.util.Random;
public class ExampleTree extends Tree {
@Nullable
protected AbstractTreeFeature<NoFeatureConfig> getTreeFeature(Random random) {
return new ExampleTreeFeature();
}
}
Create a ʻExampleTree class by inheriting the abstract class
Tree. Define a method called
getTreeFeature. Let's return an instance of ʻExampleTreeFeature
defined earlier.
I do not use it by receiving a random number as an argument, but this is used when an oak tree etc. is probable to be divided into a normal species and a huge species.
Next, we will add seedlings. First, prepare a seedling class.
\src\main\java\jp\koteko\example_mod\
├ blocks
│ ├ trees
│ └ ExampleSapling.java
├ items
├ lists
├ world
└ ExampleMod.java
BlockExampleSapling.java
package jp.koteko.example_mod.blocks;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.BushBlock;
import net.minecraft.block.IGrowable;
import net.minecraft.block.trees.Tree;
import net.minecraft.state.IntegerProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import java.util.Random;
public class BlockExampleSapling extends BushBlock implements IGrowable {
public static final IntegerProperty STAGE = BlockStateProperties.STAGE_0_1;
protected static final VoxelShape SHAPE = Block.makeCuboidShape(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D);
private final Tree tree;
public BlockExampleSapling(Tree p_i48337_1_, Block.Properties properties) {
super(properties);
this.tree = p_i48337_1_;
this.setDefaultState(this.stateContainer.getBaseState().with(STAGE, Integer.valueOf(0)));
}
public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) {
return SHAPE;
}
public void tick(BlockState state, World worldIn, BlockPos pos, Random random) {
super.tick(state, worldIn, pos, random);
if (!worldIn.isAreaLoaded(pos, 1)) return; // Forge: prevent loading unloaded chunks when checking neighbor's light
if (worldIn.getLight(pos.up()) >= 9 && random.nextInt(7) == 0) {
this.grow(worldIn, pos, state, random);
}
}
public void grow(IWorld worldIn, BlockPos pos, BlockState state, Random rand) {
if (state.get(STAGE) == 0) {
worldIn.setBlockState(pos, state.cycle(STAGE), 4);
} else {
if (!net.minecraftforge.event.ForgeEventFactory.saplingGrowTree(worldIn, rand, pos)) return;
this.tree.spawn(worldIn, pos, state, rand);
}
}
/**
* Whether this IGrowable can grow
*/
public boolean canGrow(IBlockReader worldIn, BlockPos pos, BlockState state, boolean isClient) {
return true;
}
public boolean canUseBonemeal(World worldIn, Random rand, BlockPos pos, BlockState state) {
return (double)worldIn.rand.nextFloat() < 0.45D;
}
public void grow(World worldIn, Random rand, BlockPos pos, BlockState state) {
this.grow(worldIn, pos, state, rand);
}
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder) {
builder.add(STAGE);
}
}
Regarding this code, ** (almost) everything is the same as the code for minecraft seedlings **. The reason why I made it is that the constructor of the Sapling
class in minecraft is protected
and I couldn't use it directly from within the mod code. I didn't have enough time to understand how minecraft handles this, so I won't think about it here, but I think there is probably a better way to implement it in a way that suits it.
Change only the package name, class name, and constructor.
BlockList.java
//...
public class BlockList {
public static Block ExampleSapling = new BlockExampleSapling(
new ExampleTree(),
Block.Properties.create(Material.PLANTS)
.doesNotBlockMovement()
.tickRandomly()
.hardnessAndResistance(0.0F)
.sound(SoundType.PLANT))
.setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_sapling"));
@SubscribeEvent
public static void registerBlocks(RegistryEvent.Register<Block> event) {
event.getRegistry().registerAll(
ExampleSapling
);
}
@SubscribeEvent
public static void registerBlockItems(RegistryEvent.Register<Item> event) {
event.getRegistry().registerAll(
new BlockItem(ExampleSapling, new Item.Properties().group(ExampleItemGroup.DEFAULT))
.setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_sapling"))
);
}
}
Add blocks in the seedling class you prepared earlier. Since the first argument passes the corresponding tree, pass the instance of the ʻExampleTree` class created earlier.
Set resources
as usual. The explanation is omitted for those that do not change in particular.
\src\main\resources
├ assets
│ └ example_mod
│ ├ blockstates
│ │ └ example_sapling.json
│ ├ lang
│ │ └ en_us.json
│ │ └ ja_jp.json
│ ├ models
│ │ ├ block
│ │ │ └ example_sapling.json
│ │ └ item
│ │ └ example_sapling.json
│ └ textures
│ ├ blocks
│ │ └ example_sapling.png
│ └ items
└ data
└ example_mod
└ loot_tables
└ blocks
└ example_sapling.json
blockstates\example_sapling.json
{
"variants": {
"": { "model": "example_mod:block/example_sapling" }
}
}
models\block\example_sapling.json
{
"parent": "block/cross",
"textures": {
"cross": "example_mod:blocks/example_sapling"
}
}
Specify block / cross
for parent
. This allows you to apply the texture as if the planes intersected (you can see what it means by actually looking at the shape).
models\item\example_sapling.json
{
"parent": "item/generated",
"textures": {
"layer0": "example_mod:blocks/example_sapling"
}
}
Instead of inheriting the model file of block
, specify it with ʻitem / generated`.
en_us.jp
{
"block.example_mod.example_sapling": "Example Sapling"
}
ja_jp.json
{
"block.example_mod.example_sapling": "Example seedling"
}
\loot_table\blocks\example_sapling.json
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "example_mod:example_sapling"
}
]
}
]
}
Let's start the game and check it. You can see that the seedlings have been added and the bone meal will grow the tree. Also, if you remove the trunk of the tree, you can see that the leaf blocks start to disappear naturally and drop seedlings with a probability when they disappear.
It's almost time to come here. Let's make the tree implemented so far automatically generated when the world is generated. This is almost the same as 8. Add and generate ore, so please refer to that as well.
\src\main\java\jp\koteko\example_mod\
├ blocks
├ items
├ lists
├ world
│ ├ WorldGenOres.java
│ └ WorldGenTrees.java
└ ExampleMod.java
Place WorldGenTrees.java
.
WorldGenOres.java
package jp.koteko.example_mod.world;
import jp.koteko.example_mod.world.features.ExampleTreeFeature;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.GenerationStage;
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.IFeatureConfig;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import net.minecraft.world.gen.placement.AtSurfaceWithExtraConfig;
import net.minecraft.world.gen.placement.Placement;
import net.minecraftforge.registries.ForgeRegistries;
public class WorldGenTrees {
public static void setup() {
addTreeToOverworld(new ExampleTreeFeature());
}
private static void addTreeToOverworld(Feature<NoFeatureConfig> featureIn) {
for(Biome biome : ForgeRegistries.BIOMES) {
if (!biome.getCategory().equals(Biome.Category.NETHER) && !biome.getCategory().equals(Biome.Category.THEEND)) {
biome.addFeature(
GenerationStage.Decoration.VEGETAL_DECORATION,
Biome.createDecoratedFeature(
featureIn,
IFeatureConfig.NO_FEATURE_CONFIG,
Placement.COUNT_EXTRA_HEIGHTMAP,
new AtSurfaceWithExtraConfig(2, 0.1F, 1)
)
);
}
}
}
}
ʻThe arguments of AtSurfaceWithExtraConfig` are the number of lottery per chunk, the possibility of additional lottery, and the number of additional lottery. In the case of this example, "Lottery will be held at two locations per chunk, and there is a 10% chance that one more lottery will be held."
Finally, call the just defined WorldGenOres.setup ()
in the setup
in the main file.
ExampleMod.java
//...
public class ExampleMod
{
//...
private void setup(final FMLCommonSetupEvent event)
{
WorldGenTrees.setup();
}
//...
}
Start the game and create a new world. I made it shine somehow, so it stands out even more at night.
** You have added and generated trees! ** **
1.14.3 Tags help - Modder Support - Forge Forums [Biome-Minecraft Wiki](https://minecraft-ja.gamepedia.com/%E3%83%90%E3%82%A4%E3%82%AA%E3%83%BC%E3%83%A0# .E3.83.90.E3.82.A4.E3.82.AA.E3.83.BC.E3.83.A0.E3.82.AB.E3.83.A9.E3.83.BC.E3.81 .AE.E6.B1.BA.E5.AE.9A) [Route Table --Minecraft Wiki](https://minecraft-ja.gamepedia.com/%E3%83%AB%E3%83%BC%E3%83%88%E3%83%86%E3%83%BC % E3% 83% 96% E3% 83% AB) [Map Item Format --Minecraft Wiki](https://minecraft-ja.gamepedia.com/%E5%9C%B0%E5%9B%B3%E3%82%A2%E3%82%A4%E3%83% 86% E3% 83% A0% E3% 83% 95% E3% 82% A9% E3% 83% BC% E3% 83% 9E% E3% 83% 83% E3% 83% 88)