我们今天在1.18的模组中实现一个建筑的生成。
往期教程:
1.首先我们按照往常要制作一个建筑的并保存为nbt文件。B站教程
2.在Java包中新建一个world包 -> world包中新建一个 structure包 -> structure包中新建一个建筑类ChurchStructure
ChurchStructure.java
package com.joy187.re8joymod.world.structure;
import java.util.Optional;
import org.apache.logging.log4j.Level;
import com.joy187.re8joymod.Main;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.levelgen.GenerationStep;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.feature.StructureFeature;
import net.minecraft.world.level.levelgen.feature.configurations.JigsawConfiguration;
import net.minecraft.world.level.levelgen.structure.BuiltinStructureSets;
import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece;
import net.minecraft.world.level.levelgen.structure.PostPlacementProcessor;
import net.minecraft.world.level.levelgen.structure.pieces.PieceGenerator;
import net.minecraft.world.level.levelgen.structure.pieces.PieceGeneratorSupplier;
import net.minecraft.world.level.levelgen.structure.pools.JigsawPlacement;
public class ChurchStructure extends StructureFeature<JigsawConfiguration>{
public ChurchStructure() {
// Create the pieces layout of the structure and give it to the game
super(JigsawConfiguration.CODEC, ChurchStructure::createPiecesGenerator, PostPlacementProcessor.NONE);
}
@Override
public GenerationStep.Decoration step() {
return GenerationStep.Decoration.SURFACE_STRUCTURES;
}
private static boolean isFeatureChunk(PieceGeneratorSupplier.Context<JigsawConfiguration> context) {
// Grabs the chunk position we are at
ChunkPos chunkpos = context.chunkPos();
// Checks to make sure our structure does not spawn within 10 chunks of an Ocean Monument
// to demonstrate how this method is good for checking extra conditions for spawning
return !context.chunkGenerator().hasFeatureChunkInRange(BuiltinStructureSets.VILLAGES, context.seed(), chunkpos.x, chunkpos.z, 10);
}
public static Optional<PieceGenerator<JigsawConfiguration>> createPiecesGenerator(PieceGeneratorSupplier.Context<JigsawConfiguration> context) {
// Check if the spot is valid for our structure. This is just as another method for cleanness.
// Returning an empty optional tells the game to skip this spot as it will not generate the structure.
if (!ChurchStructure.isFeatureChunk(context)) {
return Optional.empty();
}
// Turns the chunk coordinates into actual coordinates we can use. (Gets center of that chunk)
BlockPos blockpos = context.chunkPos().getMiddleBlockPosition(0);
// Find the top Y value of the land and then offset our structure to 60 blocks above that.
// WORLD_SURFACE_WG will stop at top water so we don't accidentally put our structure into the ocean if it is a super deep ocean.
int topLandY = context.chunkGenerator().getFirstFreeHeight(blockpos.getX(), blockpos.getZ(), Heightmap.Types.WORLD_SURFACE_WG, context.heightAccessor());
//方块开始建造的位置,如果你改成 topLandY+40 的话就可以生成天空岛
blockpos = blockpos.above(topLandY);
Optional<PieceGenerator<JigsawConfiguration>> structurePiecesGenerator =
JigsawPlacement.addPieces(
context, // Used for JigsawPlacement to get all the proper behaviors done.
PoolElementStructurePiece::new, // Needed in order to create a list of jigsaw pieces when making the structure's layout.
blockpos, // Position of the structure. Y value is ignored if last parameter is set to true.
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.
false // 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.
);
if(structurePiecesGenerator.isPresent()) {
// 方便我们进入游戏后找到建筑.
Main.LOGGER.log(Level.DEBUG, "Church Structrue is at {}", blockpos);
}
return structurePiecesGenerator;
}
}
3.在structure包中新建一个ModStructures类对我们所有的建筑进行注册:
ModStructures.java
package com.joy187.re8joymod.world.structure;
import com.joy187.re8joymod.Main;
import net.minecraft.world.level.levelgen.feature.StructureFeature;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
public class ModStructures {
public static final DeferredRegister<StructureFeature<?>> DEFERRED_REGISTRY_STRUCTURE =
DeferredRegister.create(ForgeRegistries.STRUCTURE_FEATURES, Main.MOD_ID);
//我们刚刚的建筑类
public static final RegistryObject<StructureFeature<?>> CHURCH_STRUCTURE =
DEFERRED_REGISTRY_STRUCTURE.register("church_structure", ChurchStructure::new);
//public static final RegistryObject<StructureFeature<?>> Tank_STRUCTURE =
// DEFERRED_REGISTRY_STRUCTURE.register("tank_structure", TankStructure::new);
public static void register(IEventBus eventBus) {
DEFERRED_REGISTRY_STRUCTURE.register(eventBus);
}
}
在我们的项目主类的主函数中对所有建筑进行注册事件
public Main() {
IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus();
ItemInit.ITEMS.register(bus);
BlockInit.BLOCKS.register(bus);
EntityInit.ENTITY_TYPES.register(bus);
EffectInit.EFFECTS.register(bus);
PotionInit.POTIONS.register(bus);
SoundInit.SOUNDS.register(bus);
//添加这个
ModStructures.register(bus);
MinecraftForge.EVENT_BUS.register(this);
}
4.代码部分结束,来到资源包制作环节:
数据包文件树
1.在resources\data\你的modid 中新建structures 包并将第一步的nbt建筑放进入其中
2.新建一个tags包 -> 包中新建worldgen包 -> 新建biome包 -> biome包中新建has_structure包 -> 新建我们的建筑文件用来指明建筑在哪些地形生成:
church.json
{
"replace": false,
"_comment": " This biome tag can specify the biome directly. Or specify another biome tag by starting with # ",
"values": [
"#minecraft:is_jungle",
"#minecraft:is_forest",
"#minecraft:is_taiga",
"minecraft:desert",
"minecraft:plains",
"minecraft:savanna",
"minecraft:savanna_plateau"
]
}
3.新建一个worldgen包 -> 包中新建configured_structure_feature包 -> 包中新建一个建筑文件来
church.json
{
// 第三步我们ModStructure.java中定义的建筑类
"type": "re8joymod:church_structure",
"config": {
// 指明我们的建筑生成文件
"start_pool": "re8joymod:church/start_pool",
// This is how many pieces away from the starting piece a piece of the structure can spawn
// Think of it like the length of the branch of the structure
"size": 2
},
// 上一步我们的地形生成文件
"biomes": "#re8joymod:has_structure/church",
// If true, land will be add around the bottom of the structure. (Based on the starting piece's y value)
"adapt_noise": true,
//建筑中可以生成什么生物
"spawn_overrides": {
"creature": {
"bounding_box": "piece",
"spawns": [
{
"type": "minecraft:bat",
//生成权重
"weight": 1,
//最少生成几个
"minCount": 1,
//最多几个
"maxCount": 2
}
]
}
}
}
4.在worldgen包中新建一个structure_set包 -> 包中新建一个建筑文件说明这个建筑内生成的所有建筑:
church.json
{
// What structures to pick to try and spawn if a spot passes the placement check.
// If two or more structures in this list can spawn in a biome at a spot, a random one based on weight is chosen to spawn
"structures": [
{
//这个建筑是我们的nbt文件名称
"structure": "re8joymod:church",
"weight": 1
}
],
"placement": {
// 这个数介于int范围之内,请设置一个独一无二的大小
"salt": 2036674,
// The average distance apart in chunks for spawn attempts
"spacing": 20,
// Minimum distance apart in chunks for spawn attempts
// MUST ALWAYS BE SMALLER THAN spacing ABOVE
"separation": 6,
// The kind of placement to use. The other kind is ring based like strongholds use.
"type": "minecraft:random_spread"
}
}
5.worldgen包中新建一个template_pool包 -> 包中新建我们的建筑包church -> 包中新建start_pool.json 文件说明我们建筑从哪里开始生成。
start_pool.json
{
//我们建筑的开始生成的文件
"name": "re8joymod:church/start_pool",
"fallback": "minecraft:empty",
"elements": [
{
"weight": 1,
"element": {
"location": "re8joymod:church",
"processors": "minecraft:empty",
"projection": "rigid",
"element_type": "minecraft:single_pool_element"
}
}
]
}
5.保存所有文件 -> 进入游戏
我们新建一个世界进行测试:
按T键进入命令行输入指令找到我们的建筑
/locate re8joymod:church
之后传送到建筑的位置
我们成功找到了生成的建筑!
|