我的世界java版复制方块模组
Minecraft1.12.2 方块实体教程
MC中有许多很有趣的方块实体如旗帜、钟、附魔台…我们今天在1.18的版本下实现一个类似于熔炉的方块实体。
1.在blocks包中新建一个我们的方块实体包virusgenerator,包中新建一个我们的方块类VirusGeneratorBlock:
VirusGeneratorBlock.java
package com.joy187.re8joymod.blocks.virusgenerator; import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.DirectionProperty; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraftforge.network.NetworkHooks; import org.jetbrains.annotations.Nullable; import com.joy187.re8joymod.init.BlockEntityInit; public class VirusGeneratorBlock extends BaseEntityBlock { public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; public static final BooleanProperty LIT = BlockStateProperties.LIT; public VirusGeneratorBlock(Properties properties) { super(properties); this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(LIT, Boolean.valueOf(false))); } private static final VoxelShape SHAPE = Block.box(0, 0, 0, 16, 15, 16); @Override public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) { return SHAPE; } /* FACING */ @Override public BlockState getStateForPlacement(BlockPlaceContext 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(StateDefinition.Builder<Block, BlockState> pBuilder) { pBuilder.add(FACING,LIT); } @Override public RenderShape getRenderShape(BlockState pState) { return RenderShape.MODEL; } @Override public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState pNewState, boolean pIsMoving) { if (pState.getBlock() != pNewState.getBlock()) { BlockEntity blockEntity = pLevel.getBlockEntity(pPos); if (blockEntity instanceof VirusGeneratorBlockEntity) { ((VirusGeneratorBlockEntity) blockEntity).drops(); } } super.onRemove(pState, pLevel, pPos, pNewState, pIsMoving); } @Override public InteractionResult use(BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHit) { if (!pLevel.isClientSide()) { BlockEntity entity = pLevel.getBlockEntity(pPos); if(entity instanceof VirusGeneratorBlockEntity) { NetworkHooks.openGui(((ServerPlayer)pPlayer), (VirusGeneratorBlockEntity)entity, pPos); } else { throw new IllegalStateException("Our Container provider is missing!"); } } return InteractionResult.sidedSuccess(pLevel.isClientSide()); } @Nullable @Override public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) { return new VirusGeneratorBlockEntity(pPos, pState); } @Nullable @Override public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level pLevel, BlockState pState, BlockEntityType<T> pBlockEntityType) { return createTickerHelper(pBlockEntityType, BlockEntityInit.VIRUS_GENERATOR_BLOCK_ENTITY.get(), VirusGeneratorBlockEntity::tick); } }在BlockInit中注册我们的方块:
public static final RegistryObject<Block> VIRUS_GENERATOR_BLOCK = registerBlock("virus_generator", () -> new VirusGeneratorBlock(BlockBehaviour.Properties.copy(Blocks.IRON_BLOCK).noOcclusion()), Main.TUTORIAL_TAB);2.在virusgenerator包中新建一个我们的实体类VirusGeneratorBlockEntity:
VirusGeneratorBlockEntity.java
package com.joy187.re8joymod.blocks.virusgenerator; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.world.Containers; import net.minecraft.world.MenuProvider; import net.minecraft.world.SimpleContainer; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.ContainerData; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.alchemy.PotionUtils; import net.minecraft.world.item.alchemy.Potions; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.AbstractFurnaceBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; 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 org.jetbrains.annotations.Nullable; import com.joy187.re8joymod.init.BlockEntityInit; import com.joy187.re8joymod.init.ItemInit; import com.joy187.re8joymod.screen.VirusGeneratorMenu; import javax.annotation.Nonnull; import java.util.Optional; import java.util.Random; public class VirusGeneratorBlockEntity extends BlockEntity implements MenuProvider { private BlockState blockState; private final ItemStackHandler itemHandler = new ItemStackHandler(4) { @Override protected void onContentsChanged(int slot) { setChanged(); } }; private LazyOptional<IItemHandler> lazyItemHandler = LazyOptional.empty(); protected final ContainerData data; private int progress = 0; //你熔炼一个物品所需要的时间刻tick private int maxProgress = 72; public VirusGeneratorBlockEntity(BlockPos pWorldPosition, BlockState pBlockState) { super(BlockEntityInit.VIRUS_GENERATOR_BLOCK_ENTITY.get(), pWorldPosition, pBlockState); this.data = new ContainerData() { 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 Component getDisplayName() { return new TranslatableComponent("container.virus_generator"); } // protected Component getDefaultName() { // return new TranslatableComponent("container.virus_generator"); // } @Nullable @Override public AbstractContainerMenu createMenu(int pContainerId, Inventory pInventory, Player pPlayer) { 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(); } @Override protected void saveAdditional(@NotNull CompoundTag tag) { tag.put("inventory", itemHandler.serializeNBT()); tag.putInt("virus_generator.progress", progress); super.saveAdditional(tag); } @Override public void load(CompoundTag nbt) { super.load(nbt); itemHandler.deserializeNBT(nbt.getCompound("inventory")); progress = nbt.getInt("virus_generator.progress"); } 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); } public static void tick(Level pLevel, BlockPos pPos, BlockState pState, VirusGeneratorBlockEntity pBlockEntity) { if(hasRecipe(pBlockEntity)) { pState.setValue(VirusGeneratorBlock.LIT, Boolean.valueOf(true)); pBlockEntity.progress++; setChanged(pLevel, pPos, pState); if(pBlockEntity.progress > pBlockEntity.maxProgress) { craftItem(pBlockEntity); } } else { pState.setValue(VirusGeneratorBlock.LIT, Boolean.valueOf(false)); pBlockEntity.resetProgress(); setChanged(pLevel, pPos, pState); } // if(hasRecipe(pBlockEntity) && notReachLimit(pBlockEntity)) { // craftItem(pBlockEntity); // } } private static boolean hasRecipe(VirusGeneratorBlockEntity entity) { Level level = entity.level; SimpleContainer inventory = new SimpleContainer(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(VirusGeneratorRecipe.Type.INSTANCE, inventory, level); return match.isPresent() && canInsertAmountIntoOutputSlot(inventory) && canInsertItemIntoOutputSlot(inventory, match.get().getResultItem()) && hasFuelSlot(entity); // && has0Slot(entity) && has1Slot(entity); // boolean canUse = has0Slot(entity) && hasFuelSlot(entity) && has1Slot(entity); // if(canUse) entity.blockState = entity.blockState.setValue(VirusGeneratorBlock.LIT, Boolean.valueOf(true)); // else entity.blockState = entity.blockState.setValue(VirusGeneratorBlock.LIT, Boolean.valueOf(false)); // return canUse; } // private static boolean has0Slot(VirusGeneratorBlockEntity entity) { // return entity.itemHandler.getStackInSlot(0).getItem() == Items.ROTTEN_FLESH; // } // private static boolean has1Slot(VirusGeneratorBlockEntity entity) { // return entity.itemHandler.getStackInSlot(1).getItem() == ItemInit.LYEYE.get(); // } //你的燃料槽 private static boolean hasFuelSlot(VirusGeneratorBlockEntity entity) { return entity.itemHandler.getStackInSlot(2).getItem() == ItemInit.BLACKSHEEP.get() || entity.itemHandler.getStackInSlot(2).getItem() == Items.FERMENTED_SPIDER_EYE; } //检查物品槽是否放了原料 private static void craftItem(VirusGeneratorBlockEntity entity) { Level level = entity.level; SimpleContainer inventory = new SimpleContainer(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(VirusGeneratorRecipe.Type.INSTANCE, inventory, level); 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.itemHandler.setStackInSlot(3, new ItemStack(ItemInit.HUMUS.get(), // entity.itemHandler.getStackInSlot(3).getCount() + 1)); entity.resetProgress(); } } private void resetProgress() { this.progress = 0; } //我们设定了3号槽是产物槽 private static boolean canInsertItemIntoOutputSlot(SimpleContainer inventory, ItemStack output) { return inventory.getItem(3).getItem() == output.getItem() || inventory.getItem(3).isEmpty(); } private static boolean canInsertAmountIntoOutputSlot(SimpleContainer inventory) { return inventory.getItem(3).getMaxStackSize() > inventory.getItem(3).getCount(); } private static boolean notReachLimit(VirusGeneratorBlockEntity entity) { return entity.itemHandler.getStackInSlot(3).getCount()<64; } // @Override // public BlockState getBlockState() { // return this.blockState; // } }在init包中新建一个BlockEntityInit类,注册我们模组中所有的方块实体:
BlockEntityInit.java
package com.joy187.re8joymod.init; import com.joy187.re8joymod.Main; import com.joy187.re8joymod.blocks.virusgenerator.VirusGeneratorBlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegistryObject; public class BlockEntityInit { public static final DeferredRegister<BlockEntityType<?>> BLOCK_ENTITIES = DeferredRegister.create(ForgeRegistries.BLOCK_ENTITIES, Main.MOD_ID); public static final RegistryObject<BlockEntityType<VirusGeneratorBlockEntity>> VIRUS_GENERATOR_BLOCK_ENTITY = BLOCK_ENTITIES.register("virus_generator_block_entity", () -> BlockEntityType.Builder.of(VirusGeneratorBlockEntity::new, BlockInit.VIRUS_GENERATOR_BLOCK.get()).build(null)); // public static final RegistryObject<BlockEntityType<GeneratorBlockEntity>> GENERATOR_BLOCK_ENTITY = // BLOCK_ENTITIES.register("generator_block_entity", () -> // BlockEntityType.Builder.of(GeneratorBlockEntity::new, // BlockInit.GENERATOR_BLOCK.get()).build(null)); public static void register(IEventBus eventBus) { BLOCK_ENTITIES.register(eventBus); } }在我们的项目主类中的Main函数中将BlockEntityInit类进行注册:
public Main() { IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus(); ItemInit.ITEMS.register(bus); BlockInit.BLOCKS.register(bus); EntityInit.ENTITY_TYPES.register(bus); EffectInit.EFFECTS.register(bus); PotionInit.POTIONS.register(bus); SoundInit.SOUNDS.register(bus); //添加这个 BlockEntityInit.register(bus); MinecraftForge.EVENT_BUS.register(this); }3.在virusgenerator包中新建一个我们的配方类VirusGeneratorRecipe:
VirusGeneratorRecipe.java
package com.joy187.re8joymod.blocks.virusgenerator; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.joy187.re8joymod.Main; import com.joy187.re8joymod.init.BlockInit; import com.joy187.re8joymod.init.ItemInit; import net.minecraft.core.NonNullList; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.GsonHelper; import net.minecraft.world.SimpleContainer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.crafting.*; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; import com.google.common.collect.HashBasedTable; import com.google.common.collect.Maps; import com.google.common.collect.Table; import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nullable; public class VirusGeneratorRecipe implements Recipe<SimpleContainer> { 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 boolean matches(SimpleContainer pContainer, Level pLevel) { return recipeItems.get(0).test(pContainer.getItem(1)) && recipeItems.get(1).test(pContainer.getItem(0)); } @Override public NonNullList<Ingredient> getIngredients() { return recipeItems; } @Override public ItemStack assemble(SimpleContainer pContainer) { 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 RecipeSerializer<?> getSerializer() { return Serializer.INSTANCE; } @Override public RecipeType<?> getType() { return Type.INSTANCE; } public static class Type implements RecipeType<VirusGeneratorRecipe> { private Type() { } public static final Type INSTANCE = new Type(); //这个就是我们之后的配方类的字段 public static final String ID = "virus_generator"; } public static class Serializer implements RecipeSerializer<VirusGeneratorRecipe> { public static final Serializer INSTANCE = new Serializer(); public static final ResourceLocation ID = new ResourceLocation(Main.MOD_ID,"virus_generator"); @Override public VirusGeneratorRecipe fromJson(ResourceLocation id, JsonObject json) { ItemStack output = ShapedRecipe.itemStackFromJson(GsonHelper.getAsJsonObject(json, "output")); JsonArray ingredients = GsonHelper.getAsJsonArray(json, "ingredients"); //这里说明一共有几个原料,我们需要2个,就写2,需要3个,就改成3 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, FriendlyByteBuf 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(FriendlyByteBuf buf, VirusGeneratorRecipe recipe) { buf.writeInt(recipe.getIngredients().size()); for (Ingredient ing : recipe.getIngredients()) { ing.toNetwork(buf); } buf.writeItemStack(recipe.getResultItem(), false); } @Override public RecipeSerializer<?> setRegistryName(ResourceLocation name) { return INSTANCE; } @Nullable @Override public ResourceLocation getRegistryName() { return ID; } @Override public Class<RecipeSerializer<?>> getRegistryType() { return Serializer.castClass(RecipeSerializer.class); } @SuppressWarnings("unchecked") // Need this wrapper, because generics private static <G> Class<G> castClass(Class<?> cls) { return (Class<G>)cls; } } }4.方块和实体的代码部分结束,进入到GUI的制作环节。在Java包中新建一个screen包 -> screen包中新建VirusGeneratorScreen类指明我们GUI贴图存放的位置:
VirusGeneratorScreen.java
package com.joy187.re8joymod.screen; import com.joy187.re8joymod.Main; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Inventory; public class VirusGeneratorScreen extends AbstractContainerScreen<VirusGeneratorMenu> { //指明我们材质的位置 private static final ResourceLocation TEXTURE = new ResourceLocation(Main.MOD_ID, "textures/gui/virus_generator.png"); public VirusGeneratorScreen(VirusGeneratorMenu pMenu, Inventory pPlayerInventory, Component pTitle) { super(pMenu, pPlayerInventory, pTitle); } @Override protected void renderBg(PoseStack pPoseStack, float pPartialTick, int pMouseX, int pMouseY) { RenderSystem.setShader(GameRenderer::getPositionTexShader); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); RenderSystem.setShaderTexture(0, TEXTURE); int x = (width - imageWidth) / 2; int y = (height - imageHeight) / 2; this.blit(pPoseStack, x, y, 0, 0, imageWidth, imageHeight); if(menu.isCrafting()) { //blit(pPoseStack, x + 102, y + 41, 176, 0, 8, menu.getScaledProgress()); blit(pPoseStack, x + 8, y + 54+12-13, 176, 12-13, 14, menu.getScaledProgress()); } } //this.drawTexturedModalRect(this.guiLeft + 8, this.guiTop + 54 + 12 - k, 176, 12 - k, 14, k + 1); @Override public void render(PoseStack pPoseStack, int mouseX, int mouseY, float delta) { renderBackground(pPoseStack); super.render(pPoseStack, mouseX, mouseY, delta); renderTooltip(pPoseStack, mouseX, mouseY); } }在我们项目主类的clientSetup()函数中将screen时间进行注册:
private void clientSetup(final FMLClientSetupEvent event) { //EntityRenderers.register(EntityInit.DUND1.get(), RenderDund::new); ItemBlockRenderTypes.setRenderLayer(BlockInit.EBONY_LEAVES.get(), RenderType.cutout()); ItemBlockRenderTypes.setRenderLayer(BlockInit.HERB_BLOCK.get(), RenderType.cutout()); ItemBlockRenderTypes.setRenderLayer(BlockInit.EBONY_SAPLING.get(), RenderType.cutout()); //添加这个,第一个参数机器方块的GUI,后面是这个机器方块的screen MenuScreens.register(MenuInit.VIRUS_GENERATOR_MENU.get(), VirusGeneratorScreen::new); }screen包中新建VirusGeneratorMenu类将所有的槽位的位置都指出来:
VirusGeneratorMenu.java
package com.joy187.re8joymod.screen; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.*; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.SlotItemHandler; import org.jetbrains.annotations.Nullable; import com.joy187.re8joymod.blocks.virusgenerator.VirusGeneratorBlock; import com.joy187.re8joymod.blocks.virusgenerator.VirusGeneratorBlockEntity; import com.joy187.re8joymod.init.BlockInit; import com.joy187.re8joymod.init.MenuInit; import com.joy187.re8joymod.screen.slot.ModResultSlot; public class VirusGeneratorMenu extends AbstractContainerMenu { private final VirusGeneratorBlockEntity blockEntity; private final Level level; private final ContainerData data; public VirusGeneratorMenu(int pContainerId, Inventory inv, FriendlyByteBuf extraData) { this(pContainerId, inv, inv.player.level.getBlockEntity(extraData.readBlockPos()), new SimpleContainerData(2)); } public VirusGeneratorMenu(int pContainerId, Inventory inv, BlockEntity entity, ContainerData data) { super(MenuInit.VIRUS_GENERATOR_MENU.get(), pContainerId); 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 //我们的进度条高13像素 int progressArrowSize = 13; return maxProgress != 0 && progress != 0 ? progress * progressArrowSize / maxProgress : 0; } // CREDIT GOES TO: diesieben07 | https:///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; // THIS YOU HAVE TO DEFINE! private static final int TE_INVENTORY_SLOT_COUNT = 4; // must be the number of slots you have! @Override public ItemStack quickMoveStack(Player 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(Player pPlayer) { return stillValid(ContainerLevelAccess.create(level, blockEntity.getBlockPos()), pPlayer, BlockInit.VIRUS_GENERATOR_BLOCK.get()); } //3行物品栏 private void addPlayerInventory(Inventory 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(Inventory playerInventory) { for (int i = 0; i < 9; ++i) { this.addSlot(new Slot(playerInventory, i, 8 + i * 18, 142)); } } }我们的槽位对应关系,左上角为坐标原点:
我们13像素的进度条就是用右上角的图案覆盖左下角的图案。
在screen包中新建ModResultSlot类,将我们的产物放置进行预设。
ModResultSlot.java
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.init; import com.joy187.re8joymod.Main; import com.joy187.re8joymod.screen.VirusGeneratorMenu; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.MenuType; import net.minecraftforge.common.extensions.IForgeMenuType; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.network.IContainerFactory; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegistryObject; public class MenuInit { public static final DeferredRegister<MenuType<?>> MENUS = DeferredRegister.create(ForgeRegistries.CONTAINERS, Main.MOD_ID); //注册我们的方块菜单界面 public static final RegistryObject<MenuType<VirusGeneratorMenu>> VIRUS_GENERATOR_MENU = registerMenuType(VirusGeneratorMenu::new, "virus_generator_menu"); private static <T extends AbstractContainerMenu>RegistryObject<MenuType<T>> registerMenuType(IContainerFactory<T> factory, String name) { return MENUS.register(name, () -> IForgeMenuType.create(factory)); } public static void register(IEventBus eventBus) { MENUS.register(eventBus); } }在我们的项目主类中的Main函数中将MenuInit类进行注册:
public Main() { IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus(); ItemInit.ITEMS.register(bus); BlockInit.BLOCKS.register(bus); EntityInit.ENTITY_TYPES.register(bus); EffectInit.EFFECTS.register(bus); PotionInit.POTIONS.register(bus); SoundInit.SOUNDS.register(bus); BlockEntityInit.register(bus); //添加这个 MenuInit.register(bus); MinecraftForge.EVENT_BUS.register(this); }6.在init包中新建RecipeInit类,将我们第三步中的配方类进行注册:
RecipeInit.java
package com.joy187.re8joymod.init; import com.joy187.re8joymod.Main; import com.joy187.re8joymod.blocks.virusgenerator.VirusGeneratorRecipe; import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegistryObject; public class RecipeInit { public static final DeferredRegister<RecipeSerializer<?>> SERIALIZERS = DeferredRegister.create(ForgeRegistries.RECIPE_SERIALIZERS, Main.MOD_ID); public static final RegistryObject<RecipeSerializer<VirusGeneratorRecipe>> VIRUS_GENERATOR_SERIALIZER = SERIALIZERS.register("virus_generator", () -> VirusGeneratorRecipe.Serializer.INSTANCE); public static void register(IEventBus eventBus) { SERIALIZERS.register(eventBus); } }在我们的项目主类中的Main函数中将RecipeInit类进行注册:
public Main() { IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus(); ItemInit.ITEMS.register(bus); BlockInit.BLOCKS.register(bus); EntityInit.ENTITY_TYPES.register(bus); EffectInit.EFFECTS.register(bus); PotionInit.POTIONS.register(bus); SoundInit.SOUNDS.register(bus); BlockEntityInit.register(bus); MenuInit.register(bus); //添加这个 RecipeInit.register(bus); MinecraftForge.EVENT_BUS.register(this); }在ClientModEventSubscriber类中添加我们的配方类型的类进行注册
ClientModEventSubscriber.java
@SubscribeEvent public static void registerRecipeTypes(final RegistryEvent.Register<RecipeSerializer<?>> event) { Registry.register(Registry.RECIPE_TYPE, VirusGeneratorRecipe.Type.ID, VirusGeneratorRecipe.Type.INSTANCE); }7.代码部分结束,来到资源包制做。在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:block/virus_generator_side", "front": "re8joymod:block/virus_generator", "side": "re8joymod:block/virus_generator_side" } }方块工作时的模型
virus_generator_on.json
{ "parent": "block/orientable", "textures": { "top": "re8joymod:block/virus_generator_side", "front": "re8joymod:block/virus_generator_on", "side": "re8joymod:block/virus_generator_side" } }在models\item中添加我们手拿方块时的模型文件
virus_generator.json
{ "parent": "re8joymod:block/virus_generator" }在textures\block中添加我们方块的侧面、正面不工作、正面工作时的贴图:
在textures包中新建gui包 -> gui包中把我们的gui(大小为256×256像素点)放进去:
在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.保存所有文件 -> 进入游戏调试:
首先拿出我们的机器并放置下来,外观显示正常
将燃料和产物放入,成功产出了产物!