Minecraft 1.16.5模组开发(五十一) 方块实体 (Tile Entity)_Jay_fearless的博客-程序员ITS203

技术标签: minecraft  我的世界  java  开发语言  

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;
    }

    // CREDIT GOES TO: diesieben07 | https://github.com/diesieben07/SevenCommons
    // 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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Jay_fearless/article/details/125549815

智能推荐

jenkins.war修改默认8080端口_i学长的猫的博客-程序员ITS203_jenkins war 改端口

(22条消息) jenkins.war修改默认8080端口_yarbrough-程序员ITS203_jenkins war 端口

C# 操作数据库增删改查方法详解_欢少的成长之路的博客-程序员ITS203

C# 操作数据库最基本的类文件1.引用Mysql.Data文件2.创建C#和Mysql之间的连接对象类:MysqlConnection3.创建C#和Mysql之间的对象操作类:MysqlCommand4.创建C#和Mysql之间的对象适配器类:MysqlDataAdapter5.创建数据库配置信息的字符串(个人建议用App.Config)6.创建C#和Mysql之间的数据集:DataS...

模拟数字电路->运算放大器_oasis1500的博客-程序员ITS203_运算放大器属于数字电路吗

运算放大器集成电路运算放大器理想运算放大器同相放大电路电压跟随器反相放大电路求差电路仪用放大器求和电路总结集成电路运算放大器集成运放将大量的半导体器件,三极管及其连线制作在同一小块的硅片上来实现一定的功能,运用十分广泛.集成运放器件本身具有非线性特性,在很多情况下,我们会使他工作在线性区,用来设计电路.P是同相输入端,N是反相输入端,一个输出口,O端温度漂移:当输入信号为0时,输出不一定为0.差分放大电路可以抑制温漂中间级的作用是提高电压增益输出级主要是功率放大,特点是低输出电阻提高带负载能

hadoop深入研究:(三)——hdfs数据流 随机选节点_linuxheik的博客-程序员ITS203_hadoop 三个以上的剩余副本随机选择节点存储

hadoop深入研究:(三)——hdfs数据流标签: HadoopHDFS2013-06-12 17:38 8443人阅读 评论(10) 收藏 举报 分类: hadoop(19) 版权声明:本文为博主原创文章,未经博主允许不得转载。目录(?)[+]以下几个小节并补独立,都是相辅相成的,要结合来看

java 启动方式 java -jar xx.jar_RunMonster的博客-程序员ITS203_java xxx.jar

1、窗口被锁定,可按CTRL + C打断程序运行,关闭窗口程序停止运行java -jar XXX.jar 2、窗口不被锁定,关闭窗口时,程序停止运行java -jar XXX.jar &amp;3、nohup 意思是不挂断运行,当账户退出或终端关闭时,程序仍然运行,日志内容默认存入nohup.out 文件内nohup java -jar XXX.jar &amp; ...

【react】error Received malformed response from registry for “react-native“. The registry may be down._七月星辰的博客-程序员ITS203

【react-native】error Received malformed response from registry for "react-native". The registry may be down.

随便推点

VS2010系统找不到指定的文件_shhdgl的博客-程序员ITS203

我在VS2010下调试一个VC6.0下写的程序。然后在启动运行后出现系统找不到指定的文件的错误。由于VS是从Debug下面找exe文件所以项目的输出路径bin出错,应该修改为Debug。找到“项目”——“属性”——“链接器”——“常规”——“输出文件”,发现果然输出路径是bin下面,于是将bin改为了Debug。

linux下weblogic10.3启动报连不上自己的数据库?貌似是这样,求大虾解救!!!..._jarsim的博客-程序员ITS203

..JAVA Memory arguments: -Xms512m -Xmx512m -XX:CompileThreshold=8000 -XX:PermSize=48m -XX:MaxPermSize=128m -XX:MaxPermSize=160m.WLS Start Mode=Development.CLASSPATH=:/home/scom/bea/patch_...

串行、并行、并发_爱编程的喵喵的博客-程序员ITS203_并发和并行、串行

单核CPU也能同时运行多个进程(线程),这是一种假的多线程,因为一个CPU会划分成时间切片,可快速在多线程间进行切换执行,由于切换时间极短,看起来像是多个进程(线程)同时在运行。并发:多个进程(线程)看起来同时在运行的现象(可能是并行,也可能是串行,如上述假的多线程)串行:运行完一个程序,才会执行下一个程序, 同一时刻不能同时运行。并行:多个程序(线程)在某个时刻同时运行,依赖于多核CPU。

人工智能资料库:第32辑(20170213)_chuange6363的博客-程序员ITS203

【博客】Natural Language Processing with Stanford CoreNLP 简介: Today, we’ll be following up on our recent post on the Google Cloud Natural Lang...

Gitee上传代码 提示remote Incorrect username or password ( access token )错误原因_静思心远的博客-程序员ITS203_remote: [session-1b4ade87] laijihuan520: incorrect

@[TOC](Gitee上传代码 提示remote Incorrect username or password ( access token )错误原因)问题Git进行项目代码提交的最后一步操作的时候,出现了一个问题:用户名或密码不正确(访问令牌)remote: [session-674ec3bd] [email protected]: Incorrect username or password (access token)出现这种现象是因为之push代码的时候windows弹出一个用户名密码输入框,用

夏天最快乐的10件小事_BEOL贝尔科技的博客-程序员ITS203

万物萌动,夏日已至。窗外蝉鸣悄然响起,蒸腾的暑气扑面而来。空气中氤氲着水果的清香,一切惬意而美好。每年夏天伊始,人们都会充满期待,仿佛只有在夏天,那些温暖而热烈的事情才会发生。想要过好这个夏天,你一定要收藏这10件让你幸福的小事。1、把夏天拍成照片时间会淡化回忆,但照片可以定格时光。这个夏天,不妨养成拍照的习惯,去捕捉生活中的小确幸,留住当下的快乐和心情。日后翻开时,不经意间就能嘴角上扬。拍照的意义,在于把瞬间变成永恒。那些记录下来的时刻,会永远在时间长河中熠熠生辉。爱拍照的人,往往更热爱生命。

推荐文章

热门文章

相关标签