Иконка ресурса

Многоблочная структура

Версия(и) Minecraft
1.12.2
Многоблочная структура

В прошлой статье я рассказывал, как сделать хитбокс для блока, а в этот раз мы будем делать полноценную многоблочную структуру. Во время написания кода интерпретатор будет говорить об ошибках, но не стоит волноваться, в конце всё будет по фэншую.

Сначала, как всегда основной класс

Java:
public class BlockTable extends BlockHorizontal {
    public static final PropertyEnum<BlockTable.EnumPartType> PART = PropertyEnum.create("part", BlockTable.EnumPartType.class);
    protected static final AxisAlignedBB TABLE_AABB = new AxisAlignedBB(0.0, 0.0, 0.0, 1.0 ,0.5625, 1.0);
    public BlockTable(String name) {
        super(Material.WOOD);
        this.setRegistryName(name);
        this.setUnlocalizedName(name);
        this.setHardness(0.01f);
        this.setCreativeTab(CreativeTabs.BUILDING_BLOCKS);
        this.setDefaultState(this.blockState.getBaseState().withProperty(PART, EnumPartType.FOOT_LEFT));
    @Override
        public boolean isFullCube(IBlockState state) {
        return false;
}
    @Override
        public boolean isOpaqueCube(IBlockState state) {
        return  false;
}
    @Override
    public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos)
{
        return TABLE_AABB;
}
}
}

Как вы уже поняли в этот раз мы будем делать стол (заранее скажу, что он будет четырёхблочный).

PropertyEnum<BlockTable.EnumPartType> - это свойство, которое будет говорить, какой частью стола является блок.
setHardness - это твёрдость нашего блока, я её сделал минимальной, чтобы имитировать перенос стола с места на место, а не его разрушение, как со всеми блоками в minecraft.
setDefaultState - это мы делаем значение части по умолчанию левой нижней.

Регистрация свойства

Теперь наше свойство PART надо зарегистрировать.

Java:
@Override
    protected BlockStateContainer createBlockState()
{
        return new BlockStateContainer(this, new IProperty[] {PART});
}
    public static enum EnumPartType implements IStringSerializable
{
        HEAD_RIGHT("head_right"),
        HEAD_LEFT("head_left"),
        FOOT_RIGHT("foot_right"),
        FOOT_LEFT("foot_left");

        private final String name;

        private EnumPartType(String name)
        {
            this.name = name;
        }

        public String toString()
        {
            return this.name;
        }

        public String getName()
        {
            return this.name;
        }
}

BlockStateContainer createBlockState() - регистрация нашего свойства, привязка его к блоку.
public static enum EnumPartType - это класс, который перечисляет наши части.
implements IStringSerializable - дословный перевод "принадлежит строковому свойству объекта" и означает, что наше свойство будет передавать текстовые значения.

Дальше надо осуществить перевод свойства в мету и наоборот.

Java:
@Override
    public IBlockState getStateFromMeta(int meta)
    {
        if (meta == 0) {
            return this.getDefaultState().withProperty(PART, EnumPartType.FOOT_LEFT);
        }
        if (meta == 1) {
            return this.getDefaultState().withProperty(PART, EnumPartType.FOOT_RIGHT);
        }
        if (meta == 2) {
            return this.getDefaultState().withProperty(PART, EnumPartType.HEAD_LEFT);
        }
        return this.getDefaultState().withProperty(PART, EnumPartType.HEAD_RIGHT);
    }
    @Override
    public int getMetaFromState(IBlockState state)
    {
        int i = 0;
        if (state.getValue(PART) == EnumPartType.FOOT_RIGHT)
        {
            i = 1;
        }
        if (state.getValue(PART) == EnumPartType.HEAD_LEFT)
        {
            i = 2;
        }
        if (state.getValue(PART) == EnumPartType.HEAD_RIGHT)
        {
            i = 3;
        }

        return i;
    }

Теперь программа может установить следующие соответствия:
0 = FOOT_LEFT
1 = FOOT_RIGHT
2 = HEAD_LEFT
3 = HEAD_RIGHT

После этого можно приступать к связке блоков в одну структуру.

Создание полноценной структуры

Java:
@Override
    public void onBlockHarvested(World worldIn, BlockPos pos, IBlockState state, EntityPlayer player) {
        if (state.getValue(PART) == EnumPartType.FOOT_RIGHT) {
            worldIn.setBlockState(pos.north(1), Blocks.AIR.getDefaultState());
            worldIn.setBlockState(pos.west(1), Blocks.AIR.getDefaultState());
            worldIn.setBlockState(pos.north(1).west(1), Blocks.AIR.getDefaultState());
        }
        else if (state.getValue(PART) == EnumPartType.HEAD_RIGHT) {
            worldIn.setBlockState(pos.south(1), Blocks.AIR.getDefaultState());
            worldIn.setBlockState(pos.west(1), Blocks.AIR.getDefaultState());
            worldIn.setBlockState(pos.south(1).west(1), Blocks.AIR.getDefaultState());
        }
        else if (state.getValue(PART) == EnumPartType.HEAD_LEFT) {
            worldIn.setBlockState(pos.south(1), Blocks.AIR.getDefaultState());
            worldIn.setBlockState(pos.east(1), Blocks.AIR.getDefaultState());
            worldIn.setBlockState(pos.south(1).east(1), Blocks.AIR.getDefaultState());
        }
        else  {
            worldIn.setBlockState(pos.north(1), Blocks.AIR.getDefaultState());
            worldIn.setBlockState(pos.east(1), Blocks.AIR.getDefaultState());
            worldIn.setBlockState(pos.north(1).east(1), Blocks.AIR.getDefaultState());
        }
    }

onBlockHarvested - это событие, срабатывает после того, как блок был сломан.
setBlockState - установка блока.
up(), down(), north(), south(), west(), east() - меняет позицию на указанное направление, по указанному числу блоков (по умолчанию 1 блок).

В этой части кода сделали установку уничтожение фантомных блоков. А теперь надо сделать и установку.

EventHandler

Создаём класс EventHandler.

Java:
public class EventHandler
{
    @SubscribeEvent
    public void onSetTable(PlayerInteractEvent.RightClickBlock event)
    {
        if (event.getEntity() instanceof EntityPlayer)
        {
            EntityPlayer player = (EntityPlayer) event.getEntity();
            World worldIn = player.world;
            BlockPos pos = event.getPos().up();
            if (player.getHeldItem(EnumHand.MAIN_HAND).getItem() == Item.getItemFromBlock(BlocksRegister.Table)) {
                if (worldIn.isAirBlock(pos) && worldIn.isAirBlock(pos.north(1)) && worldIn.isAirBlock(pos.east(1)) && worldIn.isAirBlock(pos.north(1).east(1))) {
                    worldIn.setBlockState(pos.north(1), BlocksRegister.Table.getDefaultState().withProperty(PART, BlockTable.EnumPartType.HEAD_LEFT));
                    worldIn.setBlockState(pos.east(1), BlocksRegister.Table.getDefaultState().withProperty(PART, BlockTable.EnumPartType.FOOT_RIGHT));
                    worldIn.setBlockState(pos.north(1).east(1), BlocksRegister.Table.getDefaultState().withProperty(PART, BlockTable.EnumPartType.HEAD_RIGHT));
                }
                else {
                    event.setCanceled(true);
                }
            }
        }
    }
}

isAirBlock - проверка, является ли блок воздухом.
setCanceled - отменяем событие.

Мета

Наши модели

JSON:
{
    "textures": {
        "0": "minecraft:blocks/log_oak",
        "particle": "minecraft:blocks/log_oak"
    },
    "elements": [
        {
            "from": [ 0.0, 0.0, 14.0 ],
            "to": [ 2.0, 9.0, 16.0 ],
            "faces": {
                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 15.0 ] },
                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 15.0 ] },
                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 15.0 ] },
                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 15.0 ] },
                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] }
            }
        },
        {
            "from": [ 0.0, 9.0, 0.0 ],
            "to": [ 16.0, 10.0, 16.0 ],
            "faces": {
                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 1.0 ] },
                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 15.0, 1.0 ] },
                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 1.0 ] },
                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 15.0, 1.0 ] },
                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 15.0 ] },
                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 15.0 ] }
            }
        }
    ]
}
JSON:
{
    "textures": {
        "0": "minecraft:blocks/log_oak",
        "particle": "minecraft:blocks/log_oak"
    },
    "elements": [
        {
            "from": [ 14.0, 0.0, 14.0 ],
            "to": [ 16.0, 9.0, 16.0 ],
            "faces": {
                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 15.0 ] },
                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 15.0 ] },
                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 15.0 ] },
                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 15.0 ] },
                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] }
            }
        },
        {
            "from": [ 0.0, 9.0, 0.0 ],
            "to": [ 16.0, 10.0, 16.0 ],
            "faces": {
                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 1.0 ] },
                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 15.0, 1.0 ] },
                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 1.0 ] },
                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 15.0, 1.0 ] },
                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 15.0 ] },
                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 15.0 ] }
            }
        }
    ]
}
JSON:
{
    "textures": {
        "0": "minecraft:blocks/log_oak",
        "particle": "minecraft:blocks/log_oak"
    },
    "elements": [
        {
            "from": [ 0.0, 0.0, 0.0 ],
            "to": [ 2.0, 9.0, 2.0 ],
            "faces": {
                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 15.0 ] },
                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 15.0 ] },
                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 15.0 ] },
                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 15.0 ] },
                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] }
            }
        },
        {
            "from": [ 0.0, 9.0, 0.0 ],
            "to": [ 16.0, 10.0, 16.0 ],
            "faces": {
                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 1.0 ] },
                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 15.0, 1.0 ] },
                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 1.0 ] },
                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 15.0, 1.0 ] },
                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 15.0 ] },
                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 15.0 ] }
            }
        }
    ]
}
JSON:
{
    "textures": {
        "0": "minecraft:blocks/log_oak",
        "particle": "minecraft:blocks/log_oak"
    },
    "elements": [
        {
            "from": [ 14.0, 0.0, 0.0 ],
            "to": [ 16.0, 9.0, 2.0 ],
            "faces": {
                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 15.0 ] },
                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 15.0 ] },
                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 15.0 ] },
                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 15.0 ] },
                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] }
            }
        },
        {
            "from": [ 0.0, 9.0, 0.0 ],
            "to": [ 16.0, 10.0, 16.0 ],
            "faces": {
                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 1.0 ] },
                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 15.0, 1.0 ] },
                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 1.0 ] },
                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 15.0, 1.0 ] },
                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 15.0 ] },
                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 15.0 ] }
            }
        }
    ]
}

Blockstate

JSON:
{
  "variants": {
    "part=foot_right": { "model": "tutmod:foot_right" },
    "part=foot_left": { "model": "tutmod:foot_left" },
    "part=head_right": { "model": "tutmod:head_right" },
    "part=head_left": { "model": "tutmod:head_left" }
  }
}

Создадим json файл по пути tutmod/main/resources/models/item/table.json

Java:
{
  "parent": "item/generated",
  "textures": {
  "layer0":"tutmod:items/table"
  }
}

Локализация блока

tile.table.name=Стол
Финал. Регистрация

BlocksRegister

Java:
public static final Block Table = new BlockTable("table");

    public static void register()
    {
        setRegister(Table);
    }

    public static void registerRender()
    {
        setRender(Table);
    }

    private static void setRegister(Block block)
    {
        ForgeRegistries.BLOCKS.register(block);
        ForgeRegistries.ITEMS.register(new ItemBlock(block).setRegistryName(block.getRegistryName()));
    }
    private static void setRender(Block block)
    {
        Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(Item.getItemFromBlock(block), 0, new ModelResourceLocation(block.getRegistryName(), "inventory"));
    }
}
  • Like
Реакции: Nikitat0
Автор
ReyMagos
Просмотры
2,291
Первый выпуск
Обновление
Оценка
4.00 звёзд 4 оценок

Другие ресурсы пользователя ReyMagos

Последние рецензии

Полезная тема!
Статья написано понятно, структурировано.
Реализуемый пример подобран удачно - достаточно простой, чтобы сходу понять и достаточно сложный чтобы имело смысл использовать мультиблок.
Только нужно назвать ресурс более корректно
Если какая-то часть кода не имеет отношения к теме - ее не стоит включать в текст статьи(гитхаб же есть, полные сорцы можно туда залить).
Не нужно объяснять языковые особенности.
Есть некоторые нюансы по коду, напишу в комментариях ,если еще никто не успел.
Полезно, подробно, и... А на название фантазии не хватило? -1 звезда за отсутствие скриншотов, ещё -1 за название.
ReyMagos
ReyMagos
Это мой неудачный опыт, поэтому этот туториал находится в полигоне, и наверное, подлежит удалению, но об этом пока не думал.
Полезно, но желательно добавить скриншоты.
Очень полезный туториал!
Сверху