= 创作分享 =
编程开发
Minecraft 1.16.5模组开发(五十一) 方块实体 (Tile Enti ...
我才是joy

Minecraft 1.16.5模组开发(五十一) 方块实体 (Tile Entity)

我才是joy 于 2022-9-3 21:34 ( 1年前 ) [复制链接] [只看楼主] [打印]
1327 1
本帖最后由 QQ酱71683 于 2022-9-3 21:44 编辑

Minecraft1.12.2 方块实体教程

Minecraft1.18.2 方块实体教程



MC中有许多很有趣的方块实体如告示牌、酿造台、附魔台...我们今天在1.16的版本下实现一个类似于熔炉的方块实体。
1.在blocks包中新建一个我们的方块实体包virusgenerator,包中新建一个我们的方块类`VirusGeneratorBlock`:
VirusGeneratorBlock.java
```
package com.joy187.re8joymod.common.blocks.virusgenerator;


import net.minecraft.block.*;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.DirectionProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.*;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraftforge.fml.network.NetworkHooks;

import javax.annotation.Nullable;

public class VirusGeneratorBlock extends ContainerBlock {

    //方块实体的摆放朝向的参数
    public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
    public static final BooleanProperty LIT = BlockStateProperties.LIT;
    private static final VoxelShape SHAPE =  Block.box(0, 0, 0, 16, 15, 16);

    public VirusGeneratorBlock(AbstractBlock.Properties properties) {
        super(properties);
        this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(LIT, Boolean.valueOf(false)));
    }


    @Override
    public VoxelShape getShape(BlockState pState, IBlockReader pLevel, BlockPos pPos, ISelectionContext pContext) {
        return SHAPE;
    }

    /* FACING */
    @Override
    public BlockState getStateForPlacement(BlockItemUseContext pContext) {
        return this.defaultBlockState().setValue(FACING, pContext.getHorizontalDirection().getOpposite());
    }

    @Override
    public BlockState rotate(BlockState pState, Rotation pRotation) {
        return pState.setValue(FACING, pRotation.rotate(pState.getValue(FACING)));
    }

    @Override
    public BlockState mirror(BlockState pState, Mirror pMirror) {
        return pState.rotate(pMirror.getRotation(pState.getValue(FACING)));
    }

    @Override
    protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> pBuilder) {
        pBuilder.add(FACING,LIT);
    }


    @Override
    public BlockRenderType getRenderShape(BlockState pState) {
        return BlockRenderType.MODEL;
    }



    @Override
    public void onRemove(BlockState pState, World pLevel, BlockPos pPos, BlockState pNewState, boolean pIsMoving) {
        if (pState.getBlock() != pNewState.getBlock()) {
            TileEntity blockEntity = pLevel.getBlockEntity(pPos);
            if (blockEntity instanceof VirusGeneratorBlockEntity) {
                ((VirusGeneratorBlockEntity) blockEntity).dropContents(); //.drops();
                //InventoryHelper.dropContents(pLevel, pPos, (VirusGeneratorBlockEntity)blockEntity);
            }
        }
        super.onRemove(pState, pLevel, pPos, pNewState, pIsMoving);
    }

    @Override
    public ActionResultType use(BlockState pState, World pLevel, BlockPos pPos,
                                PlayerEntity pPlayer, Hand pHand, BlockRayTraceResult pHit) {
        if (!pLevel.isClientSide()) {
            TileEntity entity = pLevel.getBlockEntity(pPos);
            if(entity instanceof VirusGeneratorBlockEntity) {
                //执行打开gui的操作
                NetworkHooks.openGui(((ServerPlayerEntity)pPlayer), (VirusGeneratorBlockEntity) entity, pPos);
            } else {
                throw new IllegalStateException("Our Container provider is missing!");
            }
        }

        return ActionResultType.sidedSuccess(pLevel.isClientSide());
    }

    @Nullable
    @Override
    public TileEntity newBlockEntity(IBlockReader p_196283_1_) {
        return new VirusGeneratorBlockEntity();
    }

}
```
在BlockInit中注册我们的方块:
```
    public static RegistryObject<Block> VIRUS_GENERATOR_BLOCK = BLOCKS.register("virus_generator",
            ()-> new VirusGeneratorBlock(AbstractBlock.Properties.copy(Blocks.IRON_BLOCK).harvestTool(ToolType.PICKAXE).harvestLevel(1).requiresCorrectToolForDrops()));

```
2.在virusgenerator包中新建一个我们的实体类`VirusGeneratorBlockEntity`:
VirusGeneratorBlockEntity.java
```
package com.joy187.re8joymod.common.blocks.virusgenerator;


import com.joy187.re8joymod.common.init.BlockEntityInit;
import com.joy187.re8joymod.common.init.ModItems;
import com.joy187.re8joymod.common.recipe.VirusGeneratorRecipe;
import com.joy187.re8joymod.common.screen.VirusGeneratorMenu;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.Inventory;
import net.minecraft.inventory.InventoryHelper;
import net.minecraft.inventory.ItemStackHelper;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.INamedContainerProvider;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.LockableTileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.IIntArray;
import net.minecraft.util.NonNullList;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemStackHandler;
import org.jetbrains.annotations.NotNull;

import javax.annotation.Nonnull;
import java.util.Optional;

public class VirusGeneratorBlockEntity extends LockableTileEntity implements INamedContainerProvider, ITickableTileEntity {

    protected NonNullList<ItemStack> items = NonNullList.withSize(4, ItemStack.EMPTY);
    protected IRecipeType<? extends VirusGeneratorRecipe> recipeType;

    private final ItemStackHandler itemHandler = new ItemStackHandler(4) {
        @Override
        protected void onContentsChanged(int slot) {
            setChanged();
        }
    };

    private LazyOptional<IItemHandler> lazyItemHandler = LazyOptional.empty();
    //我们的方块有当前反应进度和总反应进度两个数据
    protected IIntArray data = new IIntArray() {
        public int get(int index) {
            switch (index) {
                case 0: return VirusGeneratorBlockEntity.this.progress;
                case 1: return VirusGeneratorBlockEntity.this.maxProgress;
                default: return 0;
            }
        }

        public void set(int index, int value) {
            switch(index) {
                case 0: VirusGeneratorBlockEntity.this.progress = value; break;
                case 1: VirusGeneratorBlockEntity.this.maxProgress = value; break;
            }
        }

        public int getCount() {
            return 2;
        }

    };

    private int progress = 0;
    private int maxProgress = 72;


    public VirusGeneratorBlockEntity() {
        super(BlockEntityInit.VIRUS_GENERATOR_BLOCK_ENTITY.get());
        this.data=new IIntArray() {
            public int get(int index) {
                switch (index) {
                    case 0: return VirusGeneratorBlockEntity.this.progress;
                    case 1: return VirusGeneratorBlockEntity.this.maxProgress;
                    default: return 0;
                }
            }

            public void set(int index, int value) {
                switch(index) {
                    case 0: VirusGeneratorBlockEntity.this.progress = value; break;
                    case 1: VirusGeneratorBlockEntity.this.maxProgress = value; break;
                }
            }

            public int getCount() {
                return 2;
            }

        };
    }



    @Override
    public int getContainerSize() {
        return this.items.size();
    }
    //你机器的名称
    @Override
    public ITextComponent getDisplayName() {
        return new TranslationTextComponent("container.virus_generator");
    }

    @Override
    protected ITextComponent getDefaultName() {
        return new TranslationTextComponent("container.virus_generator");
    }

    @Override
    protected Container createMenu(int pContainerId, PlayerInventory pInventory) {
        return new VirusGeneratorMenu(pContainerId, pInventory, this, this.data);
    }


    @Nonnull
    @Override
    public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @javax.annotation.Nullable Direction side) {
        if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) {
            return lazyItemHandler.cast();
        }

        return super.getCapability(cap, side);
    }

    @Override
    public void onLoad() {
        super.onLoad();
        lazyItemHandler = LazyOptional.of(() -> itemHandler);
    }

    @Override
    public void invalidateCaps()  {
        super.invalidateCaps();
        lazyItemHandler.invalidate();
    }

    //将你机器的当前反应进度存储到nbt标签中,virus_generator.progress里面存放progress数据
    @Override
    public CompoundNBT save(@NotNull CompoundNBT tag) {
        tag.put("inventory", itemHandler.serializeNBT());
        tag.putInt("virus_generator.progress", progress);
        super.save(tag);
        return tag;
    }

    @Override
    public void load(BlockState p_230337_1_,CompoundNBT nbt) {
        super.load(p_230337_1_,nbt);
        this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY);
        ItemStackHelper.loadAllItems(nbt, this.items);
        itemHandler.deserializeNBT(nbt.getCompound("inventory"));
        progress = nbt.getInt("virus_generator.progress");
    }

    public void dropContents() {
        Inventory inventory = new Inventory(itemHandler.getSlots());
        for (int i = 0; i < itemHandler.getSlots(); i++) {
            inventory.setItem(i, itemHandler.getStackInSlot(i));
        }
        InventoryHelper.dropContents(this.level, this.getBlockPos(), inventory);
    }

//    public void drops() {
//        SimpleContainer inventory = new SimpleContainer(itemHandler.getSlots());
//        for (int i = 0; i < itemHandler.getSlots(); i++) {
//            inventory.setItem(i, itemHandler.getStackInSlot(i));
//        }
//
//        Containers.dropContents(this.level, this.worldPosition, inventory);
//    }

    //在每一个时间刻对我们的机器的状态进行监视
    @Override
    public void tick() {
        if(hasRecipe(this)) {
            this.getBlockState().setValue(VirusGeneratorBlock.LIT, Boolean.valueOf(true));
            this.progress++;
            //setChanged(pLevel, pPos, pState);
            setChanged();
            //当前进度已经超过了最大进度,说明要产出产物了
            if(this.progress > this.maxProgress) {
                //调用生成产物函数
                craftItem(this);
            }
        } else {
            this.getBlockState().setValue(VirusGeneratorBlock.LIT, Boolean.valueOf(false));
            this.resetProgress();
            //setChanged(pLevel, pPos, pState);
            setChanged();
        }
    }

    //判断我们放的原料是否是一个正确的配方
    private boolean hasRecipe(VirusGeneratorBlockEntity entity) {
        World level = entity.level;
        Inventory inventory = new Inventory(entity.itemHandler.getSlots());
        for (int i = 0; i < entity.itemHandler.getSlots(); i++) {
            inventory.setItem(i, entity.itemHandler.getStackInSlot(i));
        }

        Optional<VirusGeneratorRecipe> match = level.getRecipeManager()
                .getRecipeFor((IRecipeType)VirusGeneratorRecipe.Type.INSTANCE, inventory, level);

        return match.isPresent() && canInsertAmountIntoOutputSlot(inventory)
                && canInsertItemIntoOutputSlot(inventory, match.get().getResultItem())
                && hasFuelSlot(entity); // && has0Slot(entity) && has1Slot(entity);

    }

    //判断我们的燃料是否放入
    private static boolean hasFuelSlot(VirusGeneratorBlockEntity entity) {
        return entity.itemHandler.getStackInSlot(2).getItem() == ModItems.BLACKSHEEP.get();
    }

    //产出产物,同时所有原料数量-1
    private static void craftItem(VirusGeneratorBlockEntity entity) {
        World level = entity.level;
        Inventory inventory = new Inventory(entity.itemHandler.getSlots());
        for (int i = 0; i < entity.itemHandler.getSlots(); i++) {
            inventory.setItem(i, entity.itemHandler.getStackInSlot(i));
        }

        //IRecipe<?> match = level.getRecipeManager().getRecipeFor(RecipeInit.VIRUS_GENERATOR_SERIALIZER., inventory, level).orElse(null);

//        Optional<VirusGeneratorRecipe> match = level.getRecipeManager()
//                .getRecipeFor((IRecipeType)RecipeInit.VIRUS_GENERATOR_SERIALIZER.get(), inventory, level);
        Optional<VirusGeneratorRecipe> match = level.getRecipeManager()
                .getRecipeFor((IRecipeType)VirusGeneratorRecipe.Type.INSTANCE, inventory, level);
        //return this.level.getRecipeManager().getRecipeFor((IRecipeType)this.recipeType, new Inventory(p_217057_1_), this.level).isPresent();

        if(match.isPresent()) {
            //if(has0Slot(entity) && has1Slot(entity) && hasFuelSlot(entity)) {
            entity.itemHandler.extractItem(0,1, false);
            entity.itemHandler.extractItem(1,1, false);
            entity.itemHandler.extractItem(2,1, false);

            entity.itemHandler.setStackInSlot(3, new ItemStack(match.get().getResultItem().getItem(),
                    entity.itemHandler.getStackInSlot(3).getCount() + 1));

            //产出后就重置我们的进度,开始生产下一个产物
            entity.resetProgress();
        }
    }

    private void resetProgress() {
        this.progress = 0;
    }

    private boolean canInsertItemIntoOutputSlot(Inventory inventory, ItemStack output) {
        return this.getItem(3).getItem() == output.getItem() || this.getItem(3).isEmpty();
    }

    private boolean canInsertAmountIntoOutputSlot(Inventory inventory) {
        return this.getItem(3).getMaxStackSize() > this.getItem(3).getCount();
    }

    private static boolean notReachLimit(VirusGeneratorBlockEntity entity) {
        return entity.itemHandler.getStackInSlot(3).getCount()<64;
    }

    @Override
    public boolean isEmpty() {
        for(ItemStack itemstack : this.items) {
            if (!itemstack.isEmpty()) {
                return false;
            }
        }
        return true;
    }

    @Override
    public ItemStack getItem(int p_70301_1_) {
        return this.items.get(p_70301_1_);
    }

    @Override
    public ItemStack removeItem(int p_70298_1_, int p_70298_2_) {
        return ItemStackHelper.removeItem(this.items, p_70298_1_, p_70298_2_);
    }

    @Override
    public ItemStack removeItemNoUpdate(int p_70304_1_) {
        return ItemStackHelper.takeItem(this.items, p_70304_1_);
    }

    @Override
    public void setItem(int p_70299_1_, ItemStack p_70299_2_) {
        ItemStack itemstack = this.items.get(p_70299_1_);
        boolean flag = !p_70299_2_.isEmpty() && p_70299_2_.sameItem(itemstack) && ItemStack.tagMatches(p_70299_2_, itemstack);
        this.items.set(p_70299_1_, p_70299_2_);
        if (p_70299_2_.getCount() > this.getMaxStackSize()) {
            p_70299_2_.setCount(this.getMaxStackSize());
        }

        if (p_70299_1_ == 0 && !flag) {
            this.progress = 0;
            this.setChanged();
        }

    }

    @Override
    public boolean stillValid(PlayerEntity p_70300_1_) {
        if (this.level.getBlockEntity(this.worldPosition) != this) {
            return false;
        } else {
            return p_70300_1_.distanceToSqr((double)this.worldPosition.getX() + 0.5D, (double)this.worldPosition.getY() + 0.5D, (double)this.worldPosition.getZ() + 0.5D) <= 64.0D;
        }    }

    @Override
    public void clearContent() {
        this.items.clear();
    }

}
```
在init包中新建一个`BlockEntityInit`类,注册我们模组中所有的方块实体:
`BlockEntityInit.java`
```
package com.joy187.re8joymod.common.init;


import com.joy187.re8joymod.Utils;
import com.joy187.re8joymod.common.blocks.virusgenerator.VirusGeneratorBlockEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.RegistryObject;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;

public class BlockEntityInit {
    public static final DeferredRegister<TileEntityType<?>> BLOCK_ENTITIES =
            DeferredRegister.create(ForgeRegistries.TILE_ENTITIES, Utils.MOD_ID);

    //注册我们的方块实体
    public static RegistryObject<TileEntityType<VirusGeneratorBlockEntity>> VIRUS_GENERATOR_BLOCK_ENTITY =
            BLOCK_ENTITIES.register("virus_generator_block_entity", () -> TileEntityType.Builder.of(
                    VirusGeneratorBlockEntity::new, ModBlocks.VIRUS_GENERATOR_BLOCK.get()).build(null));

    public static void register(IEventBus eventBus) {
        BLOCK_ENTITIES.register(eventBus);
    }
}
```
#### 在我们的项目主类中的Main函数中将`BlockEntityInit`类进行注册:
```
    public Main(){
        IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus();

        bus.addListener(this::setup);
        bus.addListener(this::doClientStuff);

        SoundInit.SOUND_TYPES.register(bus);
        EntityInit.ENTITY_TYPES.register(bus);
        ModItems.ITEMS.register(bus);
        ModBlocks.BLOCKS.register(bus);

        //添加这个
        BlockEntityInit.register(bus);

    }
```
### 3.在Java包中新建一个我们的配方包`recipe` -> `recipe`包中新建`VirusGeneratorRecipe`类:
`VirusGeneratorRecipe.java`
```
package com.joy187.re8joymod.common.recipe;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.joy187.re8joymod.Utils;
import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.*;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;

import javax.annotation.Nullable;

public class VirusGeneratorRecipe implements IRecipe<Inventory> {
    private final ResourceLocation id;
    private final ItemStack output;
    private final NonNullList<Ingredient> recipeItems;

    public VirusGeneratorRecipe(ResourceLocation id, ItemStack output,
                                NonNullList<Ingredient> recipeItems) {
        this.id = id;
        this.output = output;
        this.recipeItems = recipeItems;
    }


    @Override
    public NonNullList<Ingredient> getIngredients() {
        return recipeItems;
    }

//    @Override
//    public ItemStack assemble(ItemStackHandler pContainer) {
//        return output;
//    }
    //判断我们两个原料是否和配方中对应的上
    @Override
    public boolean matches(Inventory pContainer, World p_77569_2_) {
        return recipeItems.get(0).test(pContainer.getItem(1))
                && recipeItems.get(1).test(pContainer.getItem(0));
    }

    @Override
    public ItemStack assemble(Inventory p_77572_1_) {
        return output;
    }

    @Override
    public boolean canCraftInDimensions(int pWidth, int pHeight) {
        return true;
    }

    @Override
    public ItemStack getResultItem() {
        return output.copy();
    }

    @Override
    public ResourceLocation getId() {
        return id;
    }

    @Override
    public IRecipeSerializer<?> getSerializer() {
        return Serializer.INSTANCE;
    }

    @Override
    public IRecipeType<?> getType() {
        return Type.INSTANCE;
    }

    public static class Type implements IRecipeType<VirusGeneratorRecipe> {
        private Type() { }
        public static final Type INSTANCE = new Type();
        public static final String ID = "virus_generator";
    }

    //我们配方是将.json文件进行转换后在游戏进行判断
    public static class Serializer implements IRecipeSerializer<VirusGeneratorRecipe> {
        public static final Serializer INSTANCE = new Serializer();
        //这个名称很重要,因为我们把这个配方类型定义为了virus_generator
        public static final ResourceLocation ID = new ResourceLocation(Utils.MOD_ID,"virus_generator");

        @Override
        public VirusGeneratorRecipe fromJson(ResourceLocation id, JsonObject json) {
            ItemStack output = ShapedRecipe.itemFromJson(JSONUtils.getAsJsonObject(json, "output"));

            JsonArray ingredients = JSONUtils.getAsJsonArray(json, "ingredients");
            //因为我们有两个原料槽,所以这里是2
            NonNullList<Ingredient> inputs = NonNullList.withSize(2, Ingredient.EMPTY);

            for (int i = 0; i < inputs.size(); i++) {
                inputs.set(i, Ingredient.fromJson(ingredients.get(i)));
            }

            return new VirusGeneratorRecipe(id, output, inputs);
        }

        @Override
        public VirusGeneratorRecipe fromNetwork(ResourceLocation id, PacketBuffer buf) {
            NonNullList<Ingredient> inputs = NonNullList.withSize(buf.readInt(), Ingredient.EMPTY);

            for (int i = 0; i < inputs.size(); i++) {
                inputs.set(i, Ingredient.fromNetwork(buf));
            }

            ItemStack output = buf.readItem();
            return new VirusGeneratorRecipe(id, output, inputs);
        }

        @Override
        public void toNetwork(PacketBuffer buf, VirusGeneratorRecipe recipe) {
            buf.writeInt(recipe.getIngredients().size());
            for (Ingredient ing : recipe.getIngredients()) {
                ing.toNetwork(buf);
            }
            buf.writeItemStack(recipe.getResultItem(), false);
        }

        @Override
        public IRecipeSerializer<?> setRegistryName(ResourceLocation name) {
            return INSTANCE;
        }

        @Nullable
        @Override
        public ResourceLocation getRegistryName() {
            return ID;
        }

        @Override
        public Class<IRecipeSerializer<?>> getRegistryType() {
            return Serializer.castClass(IRecipeSerializer.class);
        }

        @SuppressWarnings("unchecked") // Need this wrapper, because generics
        private static <G> Class<G> castClass(Class<?> cls) {
            return (Class<G>)cls;
        }
    }
}
```
在init包中新建`RecipeInit`类,将我们的配方类进行注册:
`RecipeInit.java`
```
package com.joy187.re8joymod.common.init;


import com.joy187.re8joymod.Utils;
import com.joy187.re8joymod.common.recipe.VirusGeneratorRecipe;
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.RegistryObject;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;

public class RecipeInit
{
    public static final DeferredRegister<IRecipeSerializer<?>> SERIALIZERS =
            DeferredRegister.create(ForgeRegistries.RECIPE_SERIALIZERS, Utils.MOD_ID);

    //注册我们的配方类
    public static final RegistryObject<IRecipeSerializer<?>> VIRUS_GENERATOR_SERIALIZER =
            SERIALIZERS.register("virus_generator", () -> VirusGeneratorRecipe.Serializer.INSTANCE);

    public static void register(IEventBus eventBus) {
        SERIALIZERS.register(eventBus);
    }
}
```
#### 在我们的项目主类中的Main函数中将`BlockEntityInit`类进行注册:
```
    public Main(){
        IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus();

        bus.addListener(this::setup);
        bus.addListener(this::doClientStuff);

        SoundInit.SOUND_TYPES.register(bus);
        EntityInit.ENTITY_TYPES.register(bus);
        ModItems.ITEMS.register(bus);
        ModBlocks.BLOCKS.register(bus);

        BlockEntityInit.register(bus);
        //添加这个
        RecipeInit.register(bus);
    }
```
4.方块和实体的代码部分结束,进入到GUI的制作环节。在Java包中新建一个screen包 -> screen包中新建`VirusGeneratorScreen`类指明我们GUI贴图存放的位置:
`VirusGeneratorScreen.java`
```
package com.joy187.re8joymod.common.screen;


import com.joy187.re8joymod.Utils;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;

public class VirusGeneratorScreen extends ContainerScreen<VirusGeneratorMenu> {
    //我们的gui图片
    private static final ResourceLocation TEXTURE =
            new ResourceLocation(Utils.MOD_ID, "textures/gui/virus_generator.png");

    public VirusGeneratorScreen(VirusGeneratorMenu pMenu, PlayerInventory pPlayerInventory, ITextComponent pTitle) {
        super(pMenu, pPlayerInventory, pTitle);
    }

    //渲染我们的背景图片
    @Override
    protected void renderBg(MatrixStack pPoseStack, float pPartialTick, int pMouseX, int pMouseY) {
        //RenderSystem.(GameRenderer::getPositionTexShader);
        RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
        this.minecraft.getTextureManager().bind(TEXTURE);
        //RenderSystem.(0, TEXTURE);
        int x = (width - imageWidth) / 2;
        int y = (height - imageHeight) / 2;

        this.blit(pPoseStack, x, y, 0, 0, imageWidth, imageHeight);

        if(this.menu.isCrafting()) {
            //这里渲染的是我们的进度条,就像熔炉一样,坐标对应关系请参考最上方1.12.2的教程
            blit(pPoseStack, x + 8, y + 54+12-13, 176, 12-13, 14, this.menu.getScaledProgress());
        }
    }

    @Override
    public void render(MatrixStack pPoseStack, int mouseX, int mouseY, float delta) {
        renderBackground(pPoseStack);
//        this.getMinecraft().getTextureManager().bind(TEXTURE);
//        this.getMinecraft().getTextureManager().getTexture(TEXTURE);
        super.render(pPoseStack, mouseX, mouseY, delta);
        //renderTooltip(pPoseStack, mouseX, mouseY);
    }
}
```
screen包中新建`VirusGeneratorMenu`类将所有的槽位的位置都指出来:
`VirusGeneratorMenu.java`
```
package com.joy187.re8joymod.common.screen;


import com.joy187.re8joymod.common.blocks.virusgenerator.VirusGeneratorBlockEntity;
import com.joy187.re8joymod.common.init.MenuInit;
import com.joy187.re8joymod.common.init.ModBlocks;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketBuffer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IIntArray;
import net.minecraft.util.IWorldPosCallable;
import net.minecraft.util.IntArray;
import net.minecraft.world.World;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.SlotItemHandler;

public class VirusGeneratorMenu extends Container {
    private final VirusGeneratorBlockEntity blockEntity;
    private final World level;
    private final IIntArray data;

    public VirusGeneratorMenu(int pContainerId, PlayerInventory inv, PacketBuffer extraData) {
        this(pContainerId, inv, inv.player.level.getBlockEntity(extraData.readBlockPos()), new IntArray(2));
    }

    public VirusGeneratorMenu(int pContainerId, PlayerInventory inv, TileEntity entity, IIntArray data) {
        super(MenuInit.VIRUS_GENERATOR_MENU.get(), pContainerId);
        //我们一共有4个槽,一个燃料槽,两个原料槽,一个产物槽
        checkContainerSize(inv, 4);
        blockEntity = ((VirusGeneratorBlockEntity) entity);
        this.level = inv.player.level;
        this.data = data;

        addPlayerInventory(inv);
        addPlayerHotbar(inv);

        this.blockEntity.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).ifPresent(handler -> {
            //设定坐标
            this.addSlot(new SlotItemHandler(handler, 0, 26, 11));
            this.addSlot(new SlotItemHandler(handler, 1, 26, 59));
            this.addSlot(new SlotItemHandler(handler, 2, 7, 35));

            this.addSlot(new ModResultSlot(handler, 3, 81, 36));
        });

        addDataSlots(data);
    }

    public boolean isCrafting() {
        return data.get(0) > 0;
    }

    public int getScaledProgress() {
        int progress = this.data.get(0);
        int maxProgress = this.data.get(1);  // Max Progress
        //int progressArrowSize = 26; // This is the height in pixels of your arrow
        int progressArrowSize = 13;
        return maxProgress != 0 && progress != 0 ? progress * progressArrowSize / maxProgress : 0;
    }

    // must assign a slot number to each of the slots used by the GUI.
    // For this container, we can see both the tile inventory's slots as well as the player inventory slots and the hotbar.
    // Each time we add a Slot to the container, it automatically increases the slotIndex, which means
    //  0 - 8 = hotbar slots (which will map to the InventoryPlayer slot numbers 0 - 8)
    //  9 - 35 = player inventory slots (which map to the InventoryPlayer slot numbers 9 - 35)
    //  36 - 44 = TileInventory slots, which map to our TileEntity slot numbers 0 - 8)
    private static final int HOTBAR_SLOT_COUNT = 9;
    private static final int PLAYER_INVENTORY_ROW_COUNT = 3;
    private static final int PLAYER_INVENTORY_COLUMN_COUNT = 9;
    private static final int PLAYER_INVENTORY_SLOT_COUNT = PLAYER_INVENTORY_COLUMN_COUNT * PLAYER_INVENTORY_ROW_COUNT;
    private static final int VANILLA_SLOT_COUNT = HOTBAR_SLOT_COUNT + PLAYER_INVENTORY_SLOT_COUNT;
    private static final int VANILLA_FIRST_SLOT_INDEX = 0;
    private static final int TE_INVENTORY_FIRST_SLOT_INDEX = VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT;

    // 这个和我们上面的槽位对应一致都是4
    private static final int TE_INVENTORY_SLOT_COUNT = 4;  // must be the number of slots you have!

    @Override
    public ItemStack quickMoveStack(PlayerEntity playerIn, int index) {
        Slot sourceSlot = slots.get(index);
        if (sourceSlot == null || !sourceSlot.hasItem()) return ItemStack.EMPTY;  //EMPTY_ITEM
        ItemStack sourceStack = sourceSlot.getItem();
        ItemStack copyOfSourceStack = sourceStack.copy();

        // Check if the slot clicked is one of the vanilla container slots
        if (index < VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT) {
            // This is a vanilla container slot so merge the stack into the tile inventory
            if (!moveItemStackTo(sourceStack, TE_INVENTORY_FIRST_SLOT_INDEX, TE_INVENTORY_FIRST_SLOT_INDEX
                    + TE_INVENTORY_SLOT_COUNT, false)) {
                return ItemStack.EMPTY;  // EMPTY_ITEM
            }
        } else if (index < TE_INVENTORY_FIRST_SLOT_INDEX + TE_INVENTORY_SLOT_COUNT) {
            // This is a TE slot so merge the stack into the players inventory
            if (!moveItemStackTo(sourceStack, VANILLA_FIRST_SLOT_INDEX, VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT, false)) {
                return ItemStack.EMPTY;
            }
        } else {
            //System.out.println("Invalid slotIndex:" + index);
            return ItemStack.EMPTY;
        }
        // If stack size == 0 (the entire stack was moved) set slot contents to null
        if (sourceStack.getCount() == 0) {
            sourceSlot.set(ItemStack.EMPTY);
        } else {
            sourceSlot.setChanged();
        }
        sourceSlot.onTake(playerIn, sourceStack);
        return copyOfSourceStack;
    }

    @Override
    public boolean stillValid(PlayerEntity pPlayer) {
        return stillValid(IWorldPosCallable.create(level, blockEntity.getBlockPos()),
                pPlayer, ModBlocks.VIRUS_GENERATOR_BLOCK.get());
    }

    //对玩家的物品槽进行渲染,不需要改动
    private void addPlayerInventory(PlayerInventory playerInventory) {
        for (int i = 0; i < 3; ++i) {
            for (int l = 0; l < 9; ++l) {
                this.addSlot(new Slot(playerInventory, l + i * 9 + 9, 8 + l * 18, 84 + i * 18));
            }
        }
    }
    //对玩家的物品槽进行渲染,不需要改动
    private void addPlayerHotbar(PlayerInventory playerInventory) {
        for (int i = 0; i < 9; ++i) {
            this.addSlot(new Slot(playerInventory, i, 8 + i * 18, 142));
        }
    }
}
```

在screen包中新建`ModResultSlot`类,将我们的产物放置进行预设。
`ModResultSlot.java`
```
package com.joy187.re8joymod.common.screen;


import net.minecraft.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.SlotItemHandler;

public class ModResultSlot extends SlotItemHandler {
    public ModResultSlot(IItemHandler itemHandler, int index, int x, int y) {
        super(itemHandler, index, x, y);
    }

    @Override
    public boolean mayPlace(ItemStack stack) {
        return false;
    }
}
```

5.在init包中新建`MenuInit`类,将我们第四步中的菜单进行注册:
`MenuInit.java`
```
package com.joy187.re8joymod.common.init;

import com.joy187.re8joymod.Utils;
import com.joy187.re8joymod.common.screen.VirusGeneratorMenu;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.ContainerType;
import net.minecraftforge.common.extensions.IForgeContainerType;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.RegistryObject;
import net.minecraftforge.fml.network.IContainerFactory;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;


public class MenuInit {
    public static final DeferredRegister<ContainerType<?>> MENUS =
            DeferredRegister.create(ForgeRegistries.CONTAINERS, Utils.MOD_ID);

    //将我们的屏幕信息进行注册
    public static final RegistryObject<ContainerType<VirusGeneratorMenu>> VIRUS_GENERATOR_MENU =
            registerMenuType(VirusGeneratorMenu::new, "virus_generator_menu");


    private static <T extends Container>RegistryObject<ContainerType<T>> registerMenuType(IContainerFactory<T> factory,
                                                                                          String name) {
        return MENUS.register(name, () -> IForgeContainerType.create(factory));
    }

    public static void register(IEventBus eventBus) {
        MENUS.register(eventBus);
    }
}
```
#### 在我们的项目主类中的Main函数中将`MenuInit`类进行注册:
```
    public Main(){
        IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus();

        bus.addListener(this::setup);
        bus.addListener(this::doClientStuff);

        SoundInit.SOUND_TYPES.register(bus);
        EntityInit.ENTITY_TYPES.register(bus);
        ModItems.ITEMS.register(bus);
        ModBlocks.BLOCKS.register(bus);

        BlockEntityInit.register(bus);
        RecipeInit.register(bus);
        //添加这个
        MenuInit.register(bus);
    }
```
#### 在项目主类的doClientStuff函数中将我们的gui进行注册,同时和屏幕信息绑定
```
    private void doClientStuff(final FMLClientSetupEvent event) {
        event.enqueueWork(() -> {

            RenderTypeLookup.setRenderLayer(ModBlocks.HERB_BLOCK.get(), RenderType.cutout());
            RenderTypeLookup.setRenderLayer(ModBlocks.PINK_ROSE.get(), RenderType.cutout());
            RenderTypeLookup.setRenderLayer(ModBlocks.EBONY_LEAVES.get(), RenderType.cutout());
            RenderTypeLookup.setRenderLayer(ModBlocks.EBONY_SAPLING.get(), RenderType.cutout());

            //添加这个
            ScreenManager.register(MenuInit.VIRUS_GENERATOR_MENU.get(), VirusGeneratorScreen::new);
        });
    }
```

6.代码部分结束,来到资源包制作。在`src\main\resources\assets\你的modid\blockstates`中新建我们的方块的状态文件:
`virus_generator.json`
```
{
  "variants": {
    "facing=east,lit=false": {
      "model": "re8joymod:block/virus_generator",
      "y": 90
    },
    "facing=east,lit=true": {
      "model": "re8joymod:block/virus_generator_on",
      "y": 90
    },
    "facing=north,lit=false": {
      "model": "re8joymod:block/virus_generator"
    },
    "facing=north,lit=true": {
      "model": "re8joymod:block/virus_generator_on"
    },
    "facing=south,lit=false": {
      "model": "re8joymod:block/virus_generator",
      "y": 180
    },
    "facing=south,lit=true": {
      "model": "re8joymod:block/virus_generator_on",
      "y": 180
    },
    "facing=west,lit=false": {
      "model": "re8joymod:block/virus_generator",
      "y": 270
    },
    "facing=west,lit=true": {
      "model": "re8joymod:block/virus_generator_on",
      "y": 270
    }
  }
}
```

在`src\main\resources\assets\你的modid\models\block`中新建两个我们的方块模型文件:
##### 方块平时的模型
`virus_generator.json`
```
{
  "parent": "block/orientable",
  "textures": {
    "top": "re8joymod:blocks/virus_generator_side",
    "front": "re8joymod:blocks/virus_generator",
    "side": "re8joymod:blocks/virus_generator_side"
  }
}
```

##### 方块工作时的模型
`virus_generator_on.json`
```
{
  "parent": "block/orientable",
  "textures": {
    "top": "re8joymod:blocks/virus_generator_side",
    "front": "re8joymod:blocks/virus_generator_on",
    "side": "re8joymod:blocks/virus_generator_side"
  }
}
```

在`models\item`中添加我们手拿方块时的模型文件
`virus_generator.json`
```
{
  "parent": "re8joymod:block/virus_generator"
}
```

在`textures\block`中添加我们方块的侧面、正面不工作、正面工作时的贴图:


![cr4.png]()


在textures包中新建gui包 -> gui包中把我们的gui(大小为256×256像素点)放进去:


![cr5.png]()


在lang包中的`en_us.json`文件中加上我们方块实体的名称和打开机器后上面显示的名称:
```
  "block.re8joymod.virus_generator":"Virus Analyser",
  "container.virus_generator":"Virus Analyser",
```
8.在`src\main\resources\data\你的modid\recipes`中新建几个属于我们的方块实体的配方:
记得在第三步中我们把配方类型设置为`virus_generator`,所以配方的type就写为`virus_generator`:
`evirus.json`
```
{
        "type":"re8joymod:virus_generator",
        "ingredients":[
                {
                        "item":"re8joymod:humus"
                },
                {
                        "item":"re8joymod:herbglass"
                }
        ],
        "output":{
                "item":"re8joymod:evirus"
        }
}
```
9.保存所有文件 -> 进入游戏调试:
#### 首先拿出我们的机器并放置下来,外观显示正常


![mac]()


将燃料和产物放入,成功产出了产物!


![cr8.png]()




评分

参与人数 1RF +4 Vis +2 Mana +1 收起 理由
QQ酱29797 + 4 + 2 + 1 优秀帖

查看全部评分

发表于 2022-9-3 21:34:23 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

回复 | 举报

该帖共收到 1 条回复!
我才是joy
发表于 2022-9-4 20:00:26 | 只看该作者

回复 | 举报

百科目前不允许匿名发帖哦~ 请先 [ 登陆 ][ 注册 ] 吧~

本版积分规则

发新帖
  • 回复
  • 点评
  • 评分

[ MC百科(mcmod.cn) 除另有声明,所有开放公共编辑的内容均使用 BY-NC-SA 3.0 协议 ]

Minecraft百科CC协议
快速回复 返回顶部 返回列表