문서의 임의 삭제는 제재 대상으로, 문서를 삭제하려면 삭제 토론을 진행해야 합니다. 문서 보기문서 삭제토론 마인크래프트/모드/개발 (문단 편집) === 나무 만들기 === {{{+1 '''1.13/1.14 이후'''}}} 나무 원목과 나뭇잎은 각각 {{{LogBlock}}}과 {{{LeavesBlock}}}을 인스턴스화하여 만든다. {{{#!syntax java public static final Block NAMU_LOG = new LogBlock(MaterialColor.DIAMOND, Block.Properties.create(Material.WOOD, MaterialColor.CLAY).hardnessAndResistance(2.0F).sound(SoundType.WOOD)) .setRegistryName(MODID, "namu_log"); public static final Block NAMU_LEAVES = new LeavesBlock(Block.Properties.create(Material.LEAVES).hardnessAndResistance(0.2F).tickRandomly().sound(SoundType.PLANT)) .setRegistryName(MODID, "namu_leaves"); }}}이 블록들도 필요한 경우 아이템을 추가하도록 한다. {{{LogBlock}}}의 생성자에는 {{{MaterialColor}}}와 {{{Block.Properties}}}가 들어가는데, 첫번째 {{{MaterialColor}}}는 원목의 단면의 색이고, {{{Block.Properties}}} 내부의 {{{MaterialColor}}}는 원목 껍질의 색이다. 그 외의 다른 설정들은 마인크래프트 바닐라의 원목과 잎의 설정과 같다. 그리고 나무 원목과 잎의 텍스처와 모델링, 블록 스테이트를 설정하도록 한다. 원목의 경우 다른 블록들과 다르게 옆면과 단면의 텍스처가 다르므로 따로 넣어줘야 한다. 옆면 텍스처 이름은 블록 이름, 단면 텍스처 이름은 <블록 이름>_top으로 하는 것이 좋다. 모델링과 블록 스테이트는 실제 마인크래프트 내의 파일을 참조해서 적당히 수정해주도록 한다. {{{#!syntax json { "parent": "block/leaves", "textures": { "all": "modnamu:block/namu_leaves" } } }}}{{{파일명 : assets/modnamu/models/block/namu_leaves.json}}} {{{#!syntax json { "parent": "block/cube_column", "textures": { "end": "modnamu:block/namu_log_top", "side": "modnamu:block/namu_log" } } }}}{{{파일명 : assets/modnamu/models/block/namu_log.json}}} {{{#!syntax json { "variants": { "": { "model": "modnamu:block/namu_leaves" } } } }}}{{{파일명 : assets/modnamu/blockstates/namu_leaves.json}}} {{{#!syntax java { "variants": { "axis=y": { "model": "modnamu:block/namu_log" }, "axis=z": { "model": "modnamu:block/namu_log", "x": 90 }, "axis=x": { "model": "modnamu:block/namu_log", "x": 90, "y": 90 } } } }}}{{{파일명 : assets/modnamu/blockstates/namu_log.json}}} 그리고 나무 원목과 잎으로 구성되는 나무 객체 그 자체를 나타내는 {{{Tree}}} 클래스의 상속체를 구현해야 한다. {{{Tree}}} 클래스에서 구현해야 하는 메소드는 다음 하나다. {{{#!syntax java AbstractTreeFeature getTreeFeature(Random random) }}}위 메소드가 반환하는 {{{AbstractTreeFeature}}} 클래스는 나무가 어떻게 생성될 지를 결정한다. 자신이 만들 {{{Tree}}} 클래스의 상속체는 자신이 정의한 {{{AbstractTreeFeature}}}의 상속체를 반환하면 된다. {{{AbstractTreeFeature}}} 클래스에서 구현해야 하는 메소드는 다음 하나다. {{{#!syntax java boolean place(Set changedBlocks, IWorldGenerationReader worldIn, Random rand, BlockPos position, MutableBoundingBox box) }}} 위 메소드는 파라미터로 받은 세계 {{{worldIn}}} 내에서의 위치 {{{position}}}에서 나무가 생성될 수 있는지 확인하고 생성될 수 있으면 나무를 생성한 뒤 true를 반환하고 아니면 false를 반환한다. 위 메소드의 다양한 구현 방식은 {{{net.minecraft.world.gen.feature}}} 패키지 내의 {{{AbstractTreeFeature}}}의 상속체들에 정의되어 있으니 이를 참고하여 적당히 수정하면 된다. 가장 간단한 경우로 추가적인 가지가 없고 큰 나무가 자라지 않으며 위쪽 네 칸에 잎이 생기는 최소 5칸 최대 8칸 높이의 나무를 만드는 클래스는 다음과 같다. {{{#!syntax java package wiki.namu.mymod.world.gen; import com.mojang.datafixers.Dynamic; import net.minecraft.block.BlockState; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MutableBoundingBox; import net.minecraft.world.gen.IWorldGenerationReader; import net.minecraft.world.gen.feature.AbstractTreeFeature; import net.minecraft.world.gen.feature.NoFeatureConfig; import net.minecraftforge.common.IPlantable; import wiki.namu.mymod.NamuMain; import java.util.Random; import java.util.Set; import java.util.function.Function; public class NamuTreeFeature extends AbstractTreeFeature { private static final BlockState LOG = NamuMain.RegistryEvents.NAMU_LOG.getDefaultState(); private static final BlockState LEAF = NamuMain.RegistryEvents.NAMU_LEAVES.getDefaultState(); private static final int minimumHeight = 5; public NamuTreeFeature(Function, ? extends NoFeatureConfig> deserializeFunction, boolean doBlockNofityOnPlace) { super(deserializeFunction, doBlockNofityOnPlace); } @Override public boolean place(Set changedBlocks, IWorldGenerationReader worldIn, Random rand, BlockPos position, MutableBoundingBox box) { int height = minimumHeight + rand.nextInt(4); boolean place = true; // 생성 위치가 블록 설치 가능 범위 내에 있는지 확인 if(position.getY() >= 1 && position.getY() + height + 1 <= worldIn.getMaxHeight()) { // 주변에 충분한 공간이 있는지 확인 for(int y = position.getY(); y <= position.getY() + height + 1; y++) { int margin = 1; if(y == position.getY()) margin = 0; if(y >= position.getY() + height + 1 - 2) margin = 2; BlockPos.MutableBlockPos marginPos = new BlockPos.MutableBlockPos(); for(int x = position.getX() - margin; x <= position.getX() + margin; x++) for(int z = position.getZ() - margin; z <= position.getZ() + margin; z++) if(!func_214587_a(worldIn, marginPos.setPos(x, y, z))) // 해당 위치의 블록이 공기거나 나무가 자랄 수 있는 블록인지 확인 place = false; } if(!place) return false; else if(isSoil(worldIn, position.down(), getSapling()) && position.getY() + height + 1 < worldIn.getMaxHeight()) { // 나무 생성 setDirtAt(worldIn, position.down(), position); int[] widths = {1, 2, 2, 3}; for(int y = position.getY() + height - 3; y <= position.getY() + height; y++) { int relY = y - position.getY() - height; int width = widths[Math.abs(relY)]; for(int x = position.getX() - width; x <= position.getX() + width; x++) { int relX = x - position.getX(); for(int z = position.getZ() - width; z <= position.getZ() + width; z++) { int relZ = z - position.getZ(); if(Math.abs(relX) != width || Math.abs(relZ) != width || (rand.nextInt(2) != 0 && relY != 0)) { BlockPos placePos = new BlockPos(x, y, z); if(isAirOrLeaves(worldIn, placePos)) setLogState(changedBlocks, worldIn, placePos, LEAF, box); } } } } for(int y = 0; y < height; y++) { if(isAirOrLeaves(worldIn, position.up(y))) setLogState(changedBlocks, worldIn, position.up(y), LOG, box); } return true; } else return false; } else return false; } } }}}생성자는 {{{AbstractTreeFeature}}} 클래스의 생성자 그대로 만들었다. 위 코드의 첫번째 부분은 나무가 세계 높이 제한 내에 생성될 수 있으며, 나무가 생길 기둥 주위 1칸, 위에서 2칸에서는 기둥 주위 2칸이 비어있거나 나무가 생성될 수 있는 블록(잎, 흙, 잔디, 원목, 묘목, 덩굴)인지 확인한다. 나무가 생성될 수 있는 공간이 충분하다면 나무 생성 위치 아래 칸이 흙인지 확인하고 나무를 생성한다. 나무 생성 부분의 코드를 수정하면 다양한 모양의 나무를 만들 수 있다. {{{place()}}} 메소드에서 사용하면 유용한 메소드는 다음이 있다. {{{ static boolean func_214587_a(IWorldGenerationBaseReader p_214587_0_, BlockPos p_214587_1_) : 세계 내의 해당 위치에서 나무가 생성될 수 있는지 반환하는 AbstractTreeFeature의 정적 메소드다. static boolean isSoil(IWorldGenerationBaseReader reader, BlockPos pos, net.minecraftforge.common.IPlantable sapling) : 세계 내의 해당 위치의 블록이 흙인지 확인하여 반환하는 AbstractTreeFeature의 정적 메소드다. IPlantable 인스턴스는 AbstractTreeFeature 클래스의 getSapling() 인스턴스 메소드의 리턴값으로 주면 된다. void setDirtAt(IWorldGenerationReader reader, BlockPos pos, BlockPos origin) : 해당 위치의 잔디를 흙으로 바꾼다. 이 때, pos는 바꿀 흙 블록의 위치, origin은 나무 기둥이 시작되는 위치다. static boolean isAirOrLeaves(IWorldGenerationBaseReader p_214572_0_, BlockPos p_214572_1_) : 세계 내의 해당 위치의 블록이 공기 또는 잎인지 확인한다. void setLogState(Set changedBlocks, IWorldWriter worldIn, BlockPos p_208520_3_, BlockState p_208520_4_, MutableBoundingBox p_208520_5_) : 해당 위치의 블록을 자신이 생성할 블록으로 바꾼다. 나머지 파라미터는 place 메소드의 파라미터를 그대로 넣으면 되고, BlockPos는 생성할 블록의 위치, BlockState는 생성할 블록의 BlockState를 넣으면 된다. }}} {{{AbstractTreeFeature}}}를 구현했으면 {{{Tree}}}의 구현체가 위 클래스의 인스턴스를 반환하도록 하면 된다. {{{#!syntax java package wiki.namu.mymod.block.tree; import net.minecraft.block.trees.Tree; import net.minecraft.world.gen.feature.AbstractTreeFeature; import net.minecraft.world.gen.feature.NoFeatureConfig; import wiki.namu.mymod.world.gen.NamuTreeFeature; import java.util.Random; public class NamuTree extends Tree { @Override public AbstractTreeFeature getTreeFeature(Random random) { return new NamuTreeFeature(NoFeatureConfig::deserialize, true); } } }}}{{{doBlockNotifyOnPlace}}} 파라미터는 기본적으로 true로 하도록 하자. 묘목은 {{{SaplingBlock}}}을 인스턴스화하여 만들면 된다. 무슨 이유에서인지 {{{SaplingBlock}}} 클래스의 생성자가 {{{protected}}} 접근 한정자로 선언되어 있어 모드 내에서 접근이 불가능하므로 {{{SaplingBlock}}}을 상속해서 생성자에 접근이 가능하도록 해야 한다. {{{#!syntax java package wiki.namu.mymod.block; import net.minecraft.block.SaplingBlock; import net.minecraft.block.trees.Tree; public class ModSaplingBlock extends SaplingBlock { public ModSaplingBlock(Tree tree, Properties properties) { super(tree, properties); } } }}}위 클래스의 첫번째 파라미터에는 자신이 만든 {{{Tree}}} 클래스의 상속체의 인스턴스를 넣어주면 된다. {{{#!syntax java public static final Block NAMU_SAPLING = new ModSaplingBlock(new NamuTree(), Block.Properties.create(Material.PLANTS).doesNotBlockMovement().tickRandomly().hardnessAndResistance(0).sound(SoundType.PLANT)) .setRegistryName(MODID, "namu_sapling"); }}} 묘목을 만들었으면 아까 만든 {{{AbstractTreeFeature}}} 상속체의 생성자 내에서 {{{setSapling()}}} 메소드로 해당 클래스가 사용할 묘목을 자신이 만든 묘목으로 설정해줘야 한다. {{{#!syntax java public class NamuTreeFeature extends AbstractTreeFeature { //... public NamuTreeFeature(Function, ? extends NoFeatureConfig> deserializeFunction, boolean doBlockNofityOnPlace) { super(deserializeFunction, doBlockNofityOnPlace); this.setSapling((IPlantable)NamuMain.RegistryEvents.NAMU_SAPLING); } //... } }}}그리고 다른 블록과 마찬가지로 마인크래프트 바닐라의 묘목의 설정 파일을 참조해서 텍스처와 모델링, 블록스테이트를 설정해주면 된다. {{{#!syntax json { "parent": "block/cross", "textures": { "cross": "modnamu:block/namu_sapling" } } }}}{{{파일명 : assets/modnamu/models/block/namu_sapling.json}}} {{{#!syntax json { "variants": { "": { "model": "modnamu:block/namu_sapling" } } } }}}{{{파일명 : assets/modnamu/blockstates/namu_sapling.json}}} 이대로만 해도 묘목으로 나무를 자라게 하는 데에는 문제가 없지만 나무가 생성된 뒤 잎이 자동으로 사라진다. 이는 마인크래프트가 모드에서 만든 잎과 원목을 잎과 원목으로 인식하지 못하기 때문이다. 따라서 마인크래프트의 {{{leaves}}}와 {{{logs}}} 아이템 태그에 자신이 만든 블록을 추가해줘야 한다. 아이템 태그를 추가하기 위해서는 data 폴더 내의 <모드 아이디>.tags.blocks(블록 태그), <모드 아이디>.tags.items(아이템 태그) 폴더 내에 <태그 명>.json 파일을 만들어주면 된다. 여기서는 바닐라 마인크래프트의 태그에 새 블록을 추가할 것이므로 마인크래프트의 모드 아이디인 minecraft.tags.blocks 패키지 내에 파일을 추가한다. {{{#!syntax json { "replace": false, "values": [ "modnamu:namu_leaves" ] } }}}{{{파일명 : data/minecraft/tags/blocks/leaves.json}}} {{{#!syntax json { "replace": false, "values": [ "modnamu:namu_log" ] } }}}{{{파일명 : data/minecraft/tags/blocks/logs.json}}} 여기서 {{{replace}}} 프로퍼티는 기존의 태그 목록을 덮어쓰기 할 것인지 설정한다. 기존 태그에 덧붙일 것이므로 false로 설정한다. {{{values}}} 프로퍼티 내에는 해당 태그에 추가할 블록의 Registry 이름을 넣어주면 된다. 아이템 태그에 대한 더 자세한 설명은 [[https://minecraft.wiki/w/Tag|여기]] 마인크래프트 영문 위키 내의 Tag 문서에서 확인할 수 있다.저장 버튼을 클릭하면 당신이 기여한 내용을 CC-BY-NC-SA 2.0 KR으로 배포하고,기여한 문서에 대한 하이퍼링크나 URL을 이용하여 저작자 표시를 하는 것으로 충분하다는 데 동의하는 것입니다.이 동의는 철회할 수 없습니다.캡챠저장미리보기