Minecraft 1.12.2模组开发(十四) 建筑生成 (structure generation)
Minecraft 1.12.2模组开发(二十二) 多种建筑生成
通过命令让我们获得一个建筑方块:
/give @s minecraft:structure_block之后我们建造一个建筑 通过对建筑操作的坐标变换,将我们这个建筑完全包裹在区域内: 点击’Save’,建筑成功保存下来 保存的建筑会生成一个.nbt文件,我们需要将这个文件找到,路径为
开发包runsaves1_8version_test(你的存档名称)generatedminecraftstructures2.在data包中新建structures文件夹,将生成的.nbt文件放入其中。 3.在worldgen文件夹中新建template_pool(建筑模板库)文件夹 -> 新建一个与我们建筑名称对应的文件夹(以tank1为例) -> 在tank1中新建一个start_pool.json文件 在start_pool.json中编写:
{
"name": "re8joymod:tank1/start_pool", //格式:模组名:建筑名称/start_pool的文件路径
"fallback": "minecraft:empty",
"elements": [
{
"weight": 1,
"element": {
"location": "re8joymod:tank1", //格式:模组名:建筑名称
"processors": "minecraft:empty",
"projection": "rigid",
"element_type": "minecraft:single_pool_element"
}
}
]
}
4.在common文件夹下新建 world文件夹 -> world文件夹中新建structure文件夹 -> structure文件夹中新建 structures文件夹 -> structures中新建一个建筑物类(以Tank1Structure.java为例)
在Tank1Structure.java中编写:
package com.joy187.re8joymod.common.world.structure.structures; import com.google.common.collect.ImmutableList; import com.mojang.serialization.Codec; import net.minecraft.entity.EntityType; import net.minecraft.util.SharedSeedRandom; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.MutableBoundingBox; import net.minecraft.util.math.vector.Vector3i; import net.minecraft.util.registry.Registry; import net.minecraft.world.IBlockReader; import net.minecraft.world.biome.Biome; import net.minecraft.world.biome.MobSpawnInfo; import net.minecraft.world.biome.provider.BiomeProvider; import net.minecraft.world.gen.ChunkGenerator; import net.minecraft.world.gen.GenerationStage; import net.minecraft.world.gen.Heightmap; import net.minecraft.world.gen.feature.NoFeatureConfig; import net.minecraft.world.gen.feature.jigsaw.JigsawManager; import net.minecraft.world.gen.feature.structure.*; import net.minecraft.util.registry.DynamicRegistries; import net.minecraft.world.gen.feature.template.TemplateManager; import net.minecraft.block.BlockState; import net.minecraft.util.ResourceLocation; import com.joy187.re8joymod.Utils; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import java.util.List; public class Tank1Structure extends Structure5.在刚刚新建的 structure文件夹中新建 ModStructure.java 在ModStructure.java中编写:{ public Tank1Structure(Codec codec) { super(codec); } @Override public IStartFactory getStartFactory() { return Tank1Structure.Start::new; } @Override public GenerationStage.Decoration step() { return GenerationStage.Decoration.SURFACE_STRUCTURES; } private static final List STRUCTURE_MonSTERS = ImmutableList.of( new MobSpawnInfo.Spawners(EntityType.ILLUSIONER, 10, 4, 1), new MobSpawnInfo.Spawners(EntityType.VINDICATOR, 10, 4, 1) ); private static final List STRUCTURE_CREATURES = ImmutableList.of( new MobSpawnInfo.Spawners(EntityType.SHEEP, 10, 10, 1), new MobSpawnInfo.Spawners(EntityType.RABBIT, 10, 1, 2) ); @Override public List getDefaultSpawnList() { return STRUCTURE_MONSTERS; } @Override public List getDefaultCreatureSpawnList() { return STRUCTURE_CREATURES; } @Override protected boolean isFeatureChunk(ChunkGenerator chunkGenerator, BiomeProvider biomeSource, long seed, SharedSeedRandom chunkRandom, int chunkX, int chunkZ, Biome biome, ChunkPos chunkPos, NoFeatureConfig featureConfig) { BlockPos centerOfChunk = new BlockPos((chunkX << 4) + 7, 0, (chunkZ << 4) + 7); int landHeight = chunkGenerator.getbaseHeight(centerOfChunk.getX(), centerOfChunk.getZ(), Heightmap.Type.WORLD_SURFACE_WG); IBlockReader columnOfBlocks = chunkGenerator.getbaseColumn(centerOfChunk.getX(), centerOfChunk.getZ()); BlockState topBlock = columnOfBlocks.getBlockState(centerOfChunk.above(landHeight)); return topBlock.getFluidState().isEmpty(); } @Override public boolean getDefaultRestrictsSpawnsToInside() { return true; } public static class Start extends StructureStart { public Start(Structure structureIn, int chunkX, int chunkZ, MutableBoundingBox mutableBoundingBox, int referenceIn, long seedIn) { super(structureIn, chunkX, chunkZ, mutableBoundingBox, referenceIn, seedIn); } @Override public void generatePieces(DynamicRegistries dynamicRegistryManager, ChunkGenerator chunkGenerator, TemplateManager templateManagerIn, int chunkX, int chunkZ, Biome biomeIn, NoFeatureConfig config) { // Turns the chunk coordinates into actual coordinates we can use int x = chunkX * 16 + 7; int z = chunkZ * 16 + 7; BlockPos centerPos = new BlockPos(x, 0, z); //IBlockReader blockReader = chunkGenerator.getbaseColumn(blockpos.getX(), blockpos.getZ()); // All a structure has to do is call this method to turn it into a jigsaw based structure! JigsawManager.addPieces( dynamicRegistryManager, new VillageConfig(() -> dynamicRegistryManager.registryOrThrow(Registry.TEMPLATE_POOL_REGISTRY) // The path to the starting Template Pool JSON file to read. // // Note, this is "structure_tutorial:run_down_house/start_pool" which means // the game will automatically look into the following path for the template pool: // "resources/data/structure_tutorial/worldgen/template_pool/run_down_house/start_pool.json" // This is why your pool files must be in "data/ /worldgen/template_pool/ " // because the game automatically will check in worldgen/template_pool for the pools. .get(new ResourceLocation(Utils.MOD_ID, "tank1/start_pool")), // How many pieces outward from center can a recursive jigsaw structure spawn. // Our structure is only 1 piece outward and isn't recursive so any value of 1 or more doesn't change anything. // However, I recommend you keep this a decent value like 10 so people can use datapacks to add additional pieces to your structure easily. // But don't make it too large for recursive structures like villages or you'll crash server due to hundreds of pieces attempting to generate! 10), AbstractVillagePiece::new, chunkGenerator, templateManagerIn, centerPos, // Position of the structure. Y value is ignored if last parameter is set to true. this.pieces, // The list that will be populated with the jigsaw pieces after this method. this.random, false, // Special boundary adjustments for villages. It's... hard to explain. Keep this false and make your pieces not be partially intersecting. // Either not intersecting or fully contained will make children pieces spawn just fine. It's easier that way. true); // Place at heightmap (top land). Set this to false for structure to be place at the passed in blockpos's Y value instead. // Definitely keep this false when placing structures in the nether as otherwise, heightmap placing will put the structure on the Bedrock roof. // **THE FOLLOWING TWO LINES ARE OPTIONAL** // // Right here, you can do interesting stuff with the pieces in this.pieces such as offset the // center piece by 50 blocks up for no reason, remove repeats of a piece or add a new piece so // only 1 of that piece exists, etc. But you do not have access to the piece's blocks as this list // holds just the piece's size and positions. Blocks will be placed later in JigsawManager. // // In this case, we do `piece.offset` to raise pieces up by 1 block so that the house is not right on // the surface of water or sunken into land a bit. // // Then we extend the bounding box down by 1 by doing `piece.getBoundingBox().minY` which will cause the // land formed around the structure to be lowered and not cover the doorstep. You can raise the bounding // box to force the structure to be buried as well. This bounding box stuff with land is only for structures // that you added to Structure.NOISE_AFFECTING_FEATURES field handles adding land around the base of structures. // // By lifting the house up by 1 and lowering the bounding box, the land at bottom of house will now be // flush with the surrounding terrain without blocking off the doorstep. this.pieces.forEach(piece -> piece.move(0, 1, 0)); this.pieces.forEach(piece -> piece.getBoundingBox().y0 -= 1); // Since by default, the start piece of a structure spawns with it's corner at centerPos // and will randomly rotate around that corner, we will center the piece on centerPos instead. // This is so that our structure's start piece is now centered on the water check done in isFeatureChunk. // Whatever the offset done to center the start piece, that offset is applied to all other pieces // so the entire structure is shifted properly to the new spot. Vector3i structureCenter = this.pieces.get(0).getBoundingBox().getCenter(); int xOffset = centerPos.getX() - structureCenter.getX(); int zOffset = centerPos.getZ() - structureCenter.getZ(); for(StructurePiece structurePiece : this.pieces){ structurePiece.move(xOffset, 0, zOffset); } this.calculateBoundingBox(); //用作Debug,查看建筑生成的位置 LogManager.getLogger().log(Level.DEBUG, "Rundown House at " + this.pieces.get(0).getBoundingBox().x0 + " " + this.pieces.get(0).getBoundingBox().y0 + " " + this.pieces.get(0).getBoundingBox().z0); } } }
package com.joy187.re8joymod.common.world.structure;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.joy187.re8joymod.Utils;
import com.joy187.re8joymod.common.world.structure.structures.Tank1Structure;
import net.minecraft.util.registry.WorldGenRegistries;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import net.minecraft.world.gen.feature.structure.Structure;
import net.minecraft.world.gen.settings.DimensionStructuresSettings;
import net.minecraft.world.gen.settings.StructureSeparationSettings;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.RegistryObject;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import java.util.HashMap;
import java.util.Map;
public class ModStructures {
// STStructures.java
public static final DeferredRegister> STRUCTURES =
DeferredRegister.create(ForgeRegistries.STRUCTURE_FEATURES, Utils.MOD_ID);
// public static final RegistryObject> TANK1 = STRUCTURES.register("tank1", Tank1Structure::new);
public static final RegistryObject> TANK1 =
STRUCTURES.register("tank1", () -> (new Tank1Structure(NoFeatureConfig.CODEC)));
public static void setupStructures() {
setupMapSpacingAndLand(TANK1.get(),new StructureSeparationSettings(100,50, 1234567897), true);
}
public static > void setupMapSpacingAndLand(
F structure,
StructureSeparationSettings structureSeparationSettings,
boolean transformSurroundingLand)
{
Structure.STRUCTURES_REGISTRY.put(structure.getRegistryName().toString(), structure);
if(transformSurroundingLand){
Structure.NOISE_AFFECTING_FEATURES = ImmutableList.>builder()
.addAll(Structure.NOISE_AFFECTING_FEATURES)
.add(structure)
.build();
}
DimensionStructuresSettings.DEFAULTS =
ImmutableMap., StructureSeparationSettings>builder()
.putAll(DimensionStructuresSettings.DEFAULTS)
.put(structure, structureSeparationSettings)
.build();
WorldGenRegistries.NOISE_GENERATOR_SETTINGS.entrySet().forEach(settings -> {
Map, StructureSeparationSettings> structureMap = settings.getValue().structureSettings().structureConfig();
if(structureMap instanceof ImmutableMap){
Map, StructureSeparationSettings> tempMap = new HashMap<>(structureMap);
tempMap.put(structure, structureSeparationSettings);
settings.getValue().structureSettings().structureConfig = tempMap;
}
else{
structureMap.put(structure, structureSeparationSettings);
}
});
}
public static void register(IEventBus eventBus) {
STRUCTURES.register(eventBus);
}
}
如果你出现了诸如这样的问题:
输入tp指令进行坐标传送:
这说明你还没有配置accesstransformer.cfg文件(路径:**resources/meta-INF
//你可以添加多个
public static StructureFeature, ?> CONFIGURED_TANK1_HOUSE = ModStructures.TANK1.get().configured(IFeatureConfig.NONE);
public static void registerConfiguredStructures() {
Registrypackage com.joy187.re8joymod.common.world.gen;
import com.joy187.re8joymod.common.init.BiomeInit;
import com.joy187.re8joymod.common.world.structure.ModStructures;
import net.minecraft.util.RegistryKey;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.feature.IFeatureConfig;
import net.minecraft.world.gen.feature.StructureFeature;
import net.minecraftforge.common.BiomeDictionary;
import net.minecraftforge.event.world.BiomeLoadingEvent;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
public class ModStructureGeneration {
public static void generateStructures(final BiomeLoadingEvent event) {
RegistryKey
8.在ModWorldEvents.java中(没有则在world文件夹中新建)添加一个函数addDimensionalSpacing():
package com.joy187.re8joymod.common.world;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.joy187.re8joymod.Utils;
import com.joy187.re8joymod.common.world.gen.ModFlowerGeneration;
import com.joy187.re8joymod.common.world.gen.ModStructureGeneration;
import com.joy187.re8joymod.common.world.structure.ModStructures;
import com.joy187.re8joymod.common.world.structure.structures.Tank1Structure;
import com.mojang.serialization.Codec;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.WorldGenRegistries;
import net.minecraft.world.World;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.FlatChunkGenerator;
import net.minecraft.world.gen.feature.structure.Structure;
import net.minecraft.world.gen.settings.DimensionStructuresSettings;
import net.minecraft.world.gen.settings.StructureSeparationSettings;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.event.world.BiomeLoadingEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import org.apache.logging.log4j.LogManager;
import javax.swing.plaf.synth.SynthTreeUI;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
@Mod.EventBusSubscriber(modid = Utils.MOD_ID)
public class ModWorldEvents {
//StructureTutorialMain.java
@SubscribeEvent
public static void biomeLoadingEvent(final BiomeLoadingEvent event) {
//加入建筑的生成事件的声明
ModStructureGeneration.generateStructures(event);
// ModOreGeneration.generateOres(event);
// ModFlowerGeneration.generateFlowers(event);
// ModTreeGeneration.generateTrees(event);
}
//加入空间生成的相关设置
@SubscribeEvent
public static void addDimensionalSpacing(final WorldEvent.Load event) {
if(event.getWorld() instanceof ServerWorld) {
ServerWorld serverWorld = (ServerWorld) event.getWorld();
//建筑生成异常就会抛出相应错误
try {
Method GETCODEC_METHOD =
ObfuscationReflectionHelper.findMethod(ChunkGenerator.class, "func_230347_a_");
ResourceLocation cgRL = Registry.CHUNK_GENERATOR.getKey(
(Codec extends ChunkGenerator>)GETCODEC_METHOD.invoke(serverWorld.getChunkSource().generator));
if (cgRL != null && cgRL.getNamespace().equals("terraforged")) {
return;
}
} catch (Exception e) {
LogManager.getLogger().error("Was unable to check if " + serverWorld.getLevel()
+ " is using Terraforged's ChunkGenerator.");
}
// 放止建筑在超平坦世界生成
if (serverWorld.getChunkSource().generator instanceof FlatChunkGenerator &&
serverWorld.getLevel().equals(World.OVERWORLD)) {
return;
}
// 将我们的建筑添加到建筑生成地图中
Map
9.在Main.java进行修改(添加建筑的相关注册与初始化语句):
在setup()中添加:
event.enqueueWork(() -> {
ModStructures.setupStructures();
ModConfiguredStructures.registerConfiguredStructures();
});
在Main()中添加:
ModStructures.register(bus);
SurfaceBuilderInit.SURFACE_BUILDERS.register(bus);
10.代码的部分结束了,之后进入游戏进行测试:
新建一个世界 -> 进入后输入查找我们的建筑的指令:
/locate re8joymod:tank1
之后我们会得到建筑的具体坐标
tp @s 具体坐标
我们成功发现了我们自然生成的建筑!



