(This article is one of a series of commentary articles)
First article: Introduction Previous article: Add Block Next article:
Up to this point, we have taken the body of continuous articles and assumed that the matters not otherwise specified are the same as before, but please be especially careful as we have changed the environment ** from this article.
** Upgraded version of Minecraft Forge. ** This is because it is a version under development and there are many undeveloped parts. Also, please note that the version is still under development after the update, and updated versions are released every day.
version | |
---|---|
OS | Winsows 10 Home |
Oracle JDK | 8u212 |
Minecraft | 1.16.1 |
Minecraft Forge | 1.16.1 (32.0.108) -> 1.16.1 (33.0.22) |
InteliJ IDEA | 2020.1.3 (CE) |
Add logs, leaves, and seedling blocks by referring to Add Block.
Blocks.java
package jp.koteko.liveinwater.block;
import jp.koteko.liveinwater.LiveInWater;
import jp.koteko.liveinwater.block.trees.WaterTree;
import net.minecraft.block.*;
import net.minecraft.block.material.Material;
import net.minecraft.block.material.MaterialColor;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.RenderTypeLookup;
import net.minecraft.world.FoliageColors;
import net.minecraft.world.biome.BiomeColors;
import net.minecraftforge.client.event.ColorHandlerEvent;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import java.util.ArrayList;
import java.util.List;
@Mod.EventBusSubscriber(modid = LiveInWater.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD)
public class Blocks {
public static List<Block> blockList = new ArrayList<Block>();
public static Block WATERTREE_ROOT_BLOCK = register("watertree_root_block", new Block(AbstractBlock.Properties.create(Material.WOOD).hardnessAndResistance(2.5F).sound(SoundType.WOOD)));
public static Block WATERTREE_LOG = register("watertree_log", new RotatedPillarBlock(AbstractBlock.Properties.create(Material.WOOD, MaterialColor.WOOD).hardnessAndResistance(2.0F).sound(SoundType.WOOD)));
public static Block WATERTREE_LEAVES = register("watertree_leaves", new LeavesBlock(AbstractBlock.Properties.create(Material.LEAVES).hardnessAndResistance(0.2F).tickRandomly().sound(SoundType.PLANT).notSolid()));
public static Block WATERTREE_SAPLING = register("watertree_sapling", new SaplingBlock(new WaterTree(), AbstractBlock.Properties.create(Material.PLANTS).doesNotBlockMovement().tickRandomly().zeroHardnessAndResistance().sound(SoundType.PLANT)));
private static Block register(String key, Block blockIn){
blockList.add(blockIn);
return blockIn.setRegistryName(LiveInWater.MOD_ID, key);
}
@SubscribeEvent
public static void registerBlocks(RegistryEvent.Register<Block> event) {
for (Block block : blockList) {
event.getRegistry().register(block);
if (block instanceof SaplingBlock) {
RenderTypeLookup.setRenderLayer(block, RenderType.getCutout());
}
}
}
@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();
}, WATERTREE_LEAVES);
}
}
Logs are made in the Rotated Pillar Block
class, leaves are made in the Leaves Block
class, and seedlings are made in the Sapling Block
class. Detailed settings are common to Block
(for example, set hardness and resistance with.hardnessAndResistance ()
), so change them as appropriate. Each of the above is (should) be a standard value.
The first argument passed to the SaplingBlock
constructor is an instance of the tree generated by the seedling. I haven't defined it yet, but I'll explain it later.
In addition, a description has been added to the block registration part. This sets the render type in order to display the texture of the seedling block correctly. If this is not done, the part that should be transparent will be painted black. Reference
Finally, the color of the leaf block is set to change according to the biome color. I brought the description of at 1.14.4 as it is, so I haven't looked deeply inside. You can see more by observing the BlockColors
class. About biome color [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.E3 See .81.AE.E6.B1.BA.E5.AE.9A). If this is not set, the texture color will be used as is for all biomes and can be omitted.
The color of the leaves changes according to the biome (the tree is not generated in the world at this point because it is the state after implementation until the natural generation at the end of the article)
Items.java
package jp.koteko.liveinwater.item;
import jp.koteko.liveinwater.LiveInWater;
import jp.koteko.liveinwater.block.Blocks;
import net.minecraft.block.Block;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import java.util.ArrayList;
import java.util.List;
@Mod.EventBusSubscriber(modid = LiveInWater.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD)
public class Items {
public static List<Item> itemList = new ArrayList<Item>();
public static final Item WATERTREE_ROOT = register("watertree_root", new Item((new Item.Properties()).group(LiwItemGroup.DEFAULT)));
public static final Item WATERTREE_ROOT_BLOCK = register("watertree_root_block", Blocks.WATERTREE_ROOT_BLOCK, LiwItemGroup.DEFAULT);
public static final Item WATERTREE_LOG = register("watertree_log", Blocks.WATERTREE_LOG, LiwItemGroup.DEFAULT);
public static final Item WATERTREE_LEAVES = register("watertree_leaves", Blocks.WATERTREE_LEAVES, LiwItemGroup.DEFAULT);
public static final Item WATERTREE_SAPLING = register("watertree_sapling", Blocks.WATERTREE_SAPLING, LiwItemGroup.DEFAULT);
private static Item register(String key, Item itemIn) {
itemList.add(itemIn);
return itemIn.setRegistryName(LiveInWater.MOD_ID, key);
}
private static Item register(String key, Block blockIn, ItemGroup itemGroupIn) {
return register(key, new BlockItem(blockIn, (new Item.Properties()).group(itemGroupIn)));
}
@SubscribeEvent
public static void registerItems(RegistryEvent.Register<Item> event) {
for (Item item : itemList) {
event.getRegistry().register(item);
}
}
}
Declare and register BlockItem
. See also Add Item (https://qiita.com/koteko/items/578c3cfdfd7ef71df9c1).
Set resources
.
\src\main\resources
├ assets
│ └ example_mod
│ ├ blockstates
│ │ ├ watertree_leaves.json
│ │ ├ watertree_log.json
│ │ └ watertree_sapling.json
│ ├ lang
│ │ └ en_us.json
│ │ └ ja_jp.json
│ ├ models
│ │ ├ block
│ │ │ ├ watertree_leaves.json
│ │ │ ├ watertree_log.json
│ │ │ └ watertree_sapling.json
│ │ └ item
│ │ ├ example_leaves.json
│ │ ├ example_log.json
│ │ └ example_sapling.json
│ └ textures
│ ├ block
│ │ ├ watertree_leaves.json
│ │ ├ watertree_log.json
│ │ └ watertree_sapling.json
│ └ item
│ └ watertree_sapling.json
└ data
└ liveinwater
└ loot_tables
└ blocks
├ example_leaves.json
├ example_log.json
└ example_sapling.json
blockstates\watertree_leaves.json
{
"variants": {
"": { "model": "liveinwater:block/watertree_leaves" }
}
}
blockstates\watertree_log.json
{
"variants": {
"axis=y": { "model": "liveinwater:block/watertree_log" },
"axis=z": { "model": "liveinwater:block/watertree_log", "x": 90 },
"axis=x": { "model": "liveinwater:block/watertree_log", "x": 90, "y": 90 }
}
}
Rotate the model depending on the installation direction.
blockstates\watertree_sapling.json
{
"variants": {
"": { "model": "liveinwater:block/watertree_sapling" }
}
}
en_us.jp
{
"item.liveinwater.watertree_log": "WaterTree Log",
"item.liveinwater.watertree_leaves": "WaterTree Leaves",
"item.liveinwater.watertree_sapling": "WaterTree Sapling",
"block.liveinwater.watertree_log": "WaterTree Log",
"block.liveinwater.watertree_leaves": "WaterTree Leaves",
"block.liveinwater.watertree_sapling": "WaterTree Sapling"
}
ja_jp.json
{
"item.liveinwater.watertree_log": "Log of water tree",
"item.liveinwater.watertree_leaves": "Water tree leaves",
"item.liveinwater.watertree_sapling": "Water tree seedlings",
"block.liveinwater.watertree_log": "Log of water tree",
"block.liveinwater.watertree_leaves": "Water tree leaves",
"block.liveinwater.watertree_sapling": "Water tree seedlings"
}
models\block\watertree_leaves.json
{
"parent": "block/leaves",
"textures": {
"all": "liveinwater:block/watertree_leaves"
}
}
models\block\watertree_log.json
{
"parent": "block/cube_column",
"textures": {
"end": "liveinwater:block/watertree_log_top",
"side": "liveinwater:block/watertree_log"
}
}
Specify block / cube_column
for parent
, and apply a texture that distinguishes between the top and bottom and sides of the cube. Let's specify the path to each texture file.
models\block\watertree_sapling.json
{
"parent": "block/cross",
"textures": {
"cross": "liveinwater:block/watertree_sapling"
}
}
models\item\watertree_leaves.json
{
"parent": "liveinwater:block/watertree_leaves"
}
models\item\watertree_log.json
{
"parent": "liveinwater:block/watertree_log"
}
models\item\watertree_sapling.json
{
"parent": "item/generated",
"textures": {
"layer0": "liveinwater:item/watertree_sapling"
}
}
\loot_table\blocks\watertree_leaves.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": "liveinwater:watertree_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": "liveinwater:watertree_sapling"
}
]
}
]
}
]
}
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% E3% 83% 96% E3% 83% AB).
\loot_table\blocks\watertree_log.json
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "liveinwater:watertree_log"
}
]
}
]
}
\loot_table\blocks\watertree_sapling.json
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "liveinwater:watertree_sapling"
}
]
}
]
}
Add the added log block to the minecraft: logs
tag. This is used to determine the disappearance of leaf blocks (although it may be used in other parts as well), so if this is not done, the generated leaf blocks will start to disappear immediately.
\src\main\resources
├ assets
└ data
├ liveinwater
└ 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": [
"liveinwater:watertree_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
.
Similarly, add leaf blocks to the minecraft: leaves
tag. This is necessary so that the leaf block is not judged as an obstacle when the tree grows from the seedling (if it is not in the leaves tag, it will not be overwritten when the tree is created).
\src\main\resources
├ assets
└ data
├ liveinwater
└ minecraft
└ tags
└ blocks
├ leaves.json
└ logs.json
leaves.json
{
"replace": false,
"values": [
"liveinwater:watertree_leaves"
]
}
When you press F3 in the game to display the debug display and move the cursor to the block, check that the tag (for example, # mineraft: logs
) is displayed around the center right of the screen.
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 manages things related to tree generation, and you can also get the corresponding TreeFeature
from the Tree
class.
\src\main\java\jp\koteko\liveinwater\
├ block
│ └ trees
│ └ WaterTree.java
├ item
├ world
│ └ gen
│ └ feature
│ └ WaterTreeFeature.java
└ LiveInWater.java
WaterTree.java
package jp.koteko.liveinwater.block.trees;
import jp.koteko.liveinwater.world.gen.TreeGenerator;
import net.minecraft.block.trees.Tree;
import net.minecraft.world.gen.feature.BaseTreeFeatureConfig;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import javax.annotation.Nullable;
import java.util.Random;
public class WaterTree extends Tree {
@Nullable
protected ConfiguredFeature<BaseTreeFeatureConfig, ?> getTreeFeature(Random randomIn, boolean p_225546_2_) {
return TreeGenerator.WATERTREE.setConfiguration();
}
}
Extends the Tree
abstract class to define the class. Let's define the abstract method getTreeFeature
to return an instance of the BaseTreeFeatureConfig
class with the config given to the WaterTreeFeature
described below (TreeGenerator
will be described in a later section). It is designed to receive random numbers, but this is used when the tree grows into a huge species with a probability, so change the value returned according to the random number if necessary.
WaterTreeFeature.java
package jp.koteko.liveinwater.world.gen.feature;
import com.google.common.collect.ImmutableList;
import com.mojang.serialization.Codec;
import jp.koteko.liveinwater.block.Blocks;
import net.minecraft.world.gen.blockstateprovider.SimpleBlockStateProvider;
import net.minecraft.world.gen.feature.*;
import net.minecraft.world.gen.foliageplacer.BlobFoliagePlacer;
import net.minecraft.world.gen.placement.AtSurfaceWithExtraConfig;
import net.minecraft.world.gen.placement.Placement;
import net.minecraft.world.gen.treedecorator.BeehiveTreeDecorator;
import net.minecraft.world.gen.trunkplacer.StraightTrunkPlacer;
public class WaterTreeFeature extends TreeFeature {
public WaterTreeFeature(Codec<BaseTreeFeatureConfig> codec) {
super(codec);
}
public ConfiguredFeature<?, ?> configure() {
return this.setConfiguration().withPlacement(Placement.field_242902_f.configure(new AtSurfaceWithExtraConfig(10, 0.1F, 1)).func_242728_a());
}
public ConfiguredFeature<BaseTreeFeatureConfig, ?> setConfiguration() {
return this.withConfiguration(
new BaseTreeFeatureConfig.Builder(
new SimpleBlockStateProvider(Blocks.WATERTREE_LOG.getDefaultState()),
new SimpleBlockStateProvider(Blocks.WATERTREE_LEAVES.getDefaultState()),
new BlobFoliagePlacer(FeatureSpread.func_242252_a(2), FeatureSpread.func_242252_a(0), 3),
new StraightTrunkPlacer(5, 2, 0),
new TwoLayerFeature(1, 0, 1)
).func_236700_a_().func_236703_a_(ImmutableList.of(new BeehiveTreeDecorator(0.002F))).build());
}
}
Define the class by extending the TreeFeature
class.
Two methods are defined to deal with the restrictions imposed by generics (<BaseTreeFeatureConfig,?>
Part) when using Features from other classes.
First, the setConfiguration ()
below sets what shape this tree will have (withConfiguration ()
).
A builder is prepared in the config, and the arguments are, in order, the provider of the trunk block, the provider of the leaf block, the leaf layout shape, the trunk layout shape, (argument ʻAbstractFeatureSizeTypeof unknown use). The name is hard to understand, but
func_236700_a_ () gives a function that makes ʻignore_vines
true, andfunc_236703_a_ ()
gives an argument (in this example, one that creates a beehive with a probability of 0.002) as a decorator. Each is defined in the builder as a function. Finally, call build ()
to create a BaseTreeFeatureConfig
and pass it as an argument towithConfiguration ()
.
Next, configure ()
gives the Feature that wassetConfiguration ()
more setting of the placement location (withPlacement ()
).
It is known that there are many types of Placement
, and it is also possible to use multiple types in layers. Therefore, you will need to take a closer look at the contents of the Placement
to create your favorite placement. This time I checked and showed only one example. For example, Placement.field_242902_f
determines the number of draws,func_242728_a ()
finally appliesSquarePlacement
and the getPosisions
method returns a list of random coordinates in the chunk. Seems to be doing. If I understand this area a little deeper, I may summarize it in another article.
Finally, let's make the implemented tree automatically generated when the world is generated.
\src\main\java\jp\koteko\liveinwater\
├ block
├ item
├ world
│ └ gen
│ ├ feture
│ └ TreeGenerator.java
└ LiveInWater.java
WorldGenOres.java
package jp.koteko.liveinwater.world.gen;
import com.google.common.collect.Lists;
import jp.koteko.liveinwater.world.gen.feature.WaterTreeFeature;
import net.minecraft.util.RegistryKey;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.WorldGenRegistries;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeGenerationSettings;
import net.minecraft.world.gen.GenerationStage;
import net.minecraft.world.gen.feature.BaseTreeFeatureConfig;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
public class TreeGenerator {
public static WaterTreeFeature WATERTREE;
public static ConfiguredFeature<?, ?> CONFIGURED_WATERTREE;
public static void init() {
WATERTREE = Registry.register(Registry.FEATURE, "liveinwater:watertree", new WaterTreeFeature(BaseTreeFeatureConfig.field_236676_a_));
CONFIGURED_WATERTREE = Registry.register(WorldGenRegistries.field_243653_e, "liveinwater:watertree", WATERTREE.configure());
}
public static void setup() {
addTreeToOverworld(CONFIGURED_WATERTREE);
}
private static void addTreeToOverworld(ConfiguredFeature<?, ?> featureIn){
for(Map.Entry<RegistryKey<Biome>, Biome> biome : WorldGenRegistries.field_243657_i.func_239659_c_()) {
if(!biome.getValue().getCategory().equals(Biome.Category.NETHER) && !biome.getValue().getCategory().equals(Biome.Category.THEEND)) {
addFeatureToBiome(biome.getValue(), GenerationStage.Decoration.VEGETAL_DECORATION, featureIn);
}
}
}
public static void addFeatureToBiome(Biome biome, GenerationStage.Decoration decoration, ConfiguredFeature<?, ?> configuredFeature) {
List<List<Supplier<ConfiguredFeature<?, ?>>>> biomeFeatures = new ArrayList<>(biome.func_242440_e().func_242498_c());
while (biomeFeatures.size() <= decoration.ordinal()) {
biomeFeatures.add(Lists.newArrayList());
}
List<Supplier<ConfiguredFeature<?, ?>>> features = new ArrayList<>(biomeFeatures.get(decoration.ordinal()));
features.add(() -> configuredFeature);
biomeFeatures.set(decoration.ordinal(), features);
ObfuscationReflectionHelper.setPrivateValue(BiomeGenerationSettings.class, biome.func_242440_e(), biomeFeatures, "field_242484_f");
}
}
It's hard to understand everything, so decide what to change if you need to (because I did). In version 1.16.1, [the same way as before](https://qiita.com/koteko/items/aebfc47cf73d7e49baa6#%E6%9C%A8%E3%81%AE%E7%94%9F%E6%88 It was not possible to register the Feature in Biome at% 90). Therefore, look for a reference code and refer to the code of BluePower. I was allowed to.
By defining ʻaddFeatureToBiomeat the bottom, it is possible to register a Feature in Biome in the same way as before. To briefly explain, it seems that the member variable
field_242484_f of the
BiomeGenerationSettings` class has a list of Features, so it seems that we are doing the work of overwriting and adding.
Once addFeatureToBiome is defined, it can be implemented in a manner similar to the previous method. Defined ʻaddTreeToOverworld
method to register in Overworld Biome and call it in setup
method. In addition, each Feature is declared in advance and registered with the ʻinit` method. At this time, it seems that it will not work unless you register the Feature and the Configured Feature as described above (although it was quite packed, I could not understand it completely).
Finally, call the just defined TreeGenerator.init ()
TreeGenerator.setup ()
in the main file.
LiveInWater.java
package jp.koteko.liveinwater;
import jp.koteko.liveinwater.world.gen.TreeGenerator;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent;
import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@Mod(LiveInWater.MOD_ID)
public class LiveInWater
{
public static final String MOD_ID = "liveinwater";
private static final Logger LOGGER = LogManager.getLogger();
public LiveInWater() {
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::enqueueIMC);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::processIMC);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doClientStuff);
TreeGenerator.init();
MinecraftForge.EVENT_BUS.register(this);
}
private void setup(final FMLCommonSetupEvent event)
{
LOGGER.info("SETUP START");
TreeGenerator.setup();
LOGGER.info("SETUP END");
}
private void doClientStuff(final FMLClientSetupEvent event) {
// do something that can only be done on the client
}
private void enqueueIMC(final InterModEnqueueEvent event)
{
// some example code to dispatch IMC to another mod
}
private void processIMC(final InterModProcessEvent event)
{
// some example code to receive and process InterModComms from other mods
}
@SubscribeEvent
public void onServerStarting(FMLServerStartingEvent event) {
LOGGER.info("server starting");
}
}
Start the game and create a new world.
A tree is being generated (as described so far)
A tree is being generated (when Placement and Config are changed)
[Java] Let's create a mod for Minecraft 1.14.4 [9. Add and generate trees] --Qiita BluePower/BPWorldGen.java at master · Qmunity/BluePower · GitHub 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) [SOLVED] [1.15.2] A texture issue with cross models? - Modder Support - Forge Forums
For reference, I will record an example of implementing it to a level that works for the time being before the version upgrade of Forge.
\src\main\java\jp\koteko\liveinwater\
├ block
├ item
├ world
│ └ gen
│ ├ feature
│ │ └ WaterTreeFeature.java
│ └ TreeGenerator.java
└ LiveInWater.java
WaterTreeFeature.java
package jp.koteko.liveinwater.world.gen.feature;
import com.mojang.serialization.Codec;
import net.minecraft.world.gen.feature.BaseTreeFeatureConfig;
import net.minecraft.world.gen.feature.TreeFeature;
public class WaterTreeFeature extends TreeFeature {
public WaterTreeFeature(Codec<BaseTreeFeatureConfig> codec) {
super(codec);
}
}
This is pointless at this point, as it's actually no different from the parent class yet. Unlike around 1.14, it seems that an interface called Codec
has been introduced. Somehow, when I look at it, it seems to be for general-purpose handling of objects of various classes.
TreeGenerator.java
package jp.koteko.liveinwater.world.gen;
import com.google.common.collect.ImmutableList;
import jp.koteko.liveinwater.world.gen.feature.WaterTreeFeature;
import net.minecraft.block.Blocks;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.GenerationStage;
import net.minecraft.world.gen.blockstateprovider.SimpleBlockStateProvider;
import net.minecraft.world.gen.feature.BaseTreeFeatureConfig;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.feature.TwoLayerFeature;
import net.minecraft.world.gen.foliageplacer.BlobFoliagePlacer;
import net.minecraft.world.gen.placement.AtSurfaceWithExtraConfig;
import net.minecraft.world.gen.placement.Placement;
import net.minecraft.world.gen.treedecorator.BeehiveTreeDecorator;
import net.minecraft.world.gen.trunkplacer.StraightTrunkPlacer;
import net.minecraftforge.registries.ForgeRegistries;
import java.util.OptionalInt;
public class TreeGenerator {
public static void setup() {
addTreeToOverworld(
new WaterTreeFeature(BaseTreeFeatureConfig.field_236676_a_)
.withConfiguration(
(new BaseTreeFeatureConfig.Builder(
new SimpleBlockStateProvider(Blocks.ACACIA_WOOD.getDefaultState()),
new SimpleBlockStateProvider(Blocks.BLUE_WOOL.getDefaultState()),
new BlobFoliagePlacer(2, 0, 0, 0, 3),
new StraightTrunkPlacer(5, 2, 0),
new TwoLayerFeature(0, 0, 0, OptionalInt.of(4)))
).func_236700_a_().func_236703_a_(ImmutableList.of(new BeehiveTreeDecorator(0.002F))).build())
.withPlacement(Placement.COUNT_EXTRA_HEIGHTMAP.configure(new AtSurfaceWithExtraConfig(10, 0.1F, 1)))
);
}
private static void addTreeToOverworld(ConfiguredFeature<?, ?> 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, featureIn);
}
}
}
}
It is basically the same as in 1.14, and it is generated by adding the feature of the tree to the biome you want to generate by Biome # addFeature
. Many of the names were difficult to understand, and the treatment of Feature
and ConfiguredFeature
seemed to change, and Biome # createDecoratedFeature
was also missing. The above code was written by observing the vanilla code.
For ConfiguredFeature
(new WaterTreeFeature ()
), config (in this case, the block or shape of the tree) and placement (lottery at the time of natural generation) withwithConfiguration ()
andwithPlacement ()
Location) is set.
A builder was prepared for the config, so use this. In order, the trunk block (provider), the leaf block (provider), the leaf arrangement shape, the trunk arrangement shape, and the minimum size type (details unknown). Providers should pass an instance of the SimpleBlockStateProvider
class unless they do something special. Change only the block type as appropriate. As for the arrangement shape of the leaves and trunk, some types are used in vanilla, so use the appropriate one. Observe the subclasses of FoliagePlacer
, ʻAbstractTrunkPlacer. Specify the height etc. with the argument. Regarding the 5th argument of the builder, it seems to be an object of the ʻAbstractFeatureSizeType
class managed by the name minimum_size
, but I was not sure how it works, so I will match it with other code. Returns an instance of the config by calling build ()
to the builder. func_236700_a_ ()
specifies that ivy is not generated, and func_236703_a_ ()
specifies that a beehive is generated by passing an argument like this.
Select the appropriate type of placement (see the Placement
class) and set it withconfigure ()
.