Minecraft1.12.2 方块实体教程
Minecraft1.18.2 方块实体教程
MC中有许多很有趣的方块实体如告示牌、酿造台、附魔台…我们今天在1.16的版本下实现一个类似于熔炉的方块实体。
1.在blocks包中新建一个我们的方块实体包virusgenerator,包中新建一个我们的方块类VirusGeneratorBlock
:
VirusGeneratorBlock.java
package com.mon.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.workHooks;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)));}@Overridepublic VoxelShape getShape(BlockState pState, IBlockReader pLevel, BlockPos pPos, ISelectionContext pContext) {return SHAPE;}/* FACING */@Overridepublic BlockState getStateForPlacement(BlockItemUseContext pContext) {return this.defaultBlockState().setValue(FACING, pContext.getHorizontalDirection().getOpposite());}@Overridepublic BlockState rotate(BlockState pState, Rotation pRotation) {return pState.setValue(FACING, pRotation.rotate(pState.getValue(FACING)));}@Overridepublic BlockState mirror(BlockState pState, Mirror pMirror) {return pState.rotate(pMirror.getRotation(pState.getValue(FACING)));}@Overrideprotected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> pBuilder) {pBuilder.add(FACING,LIT);}@Overridepublic BlockRenderType getRenderShape(BlockState pState) {return BlockRenderType.MODEL;}@Overridepublic 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);}@Overridepublic 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@Overridepublic 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.mon.blocks.virusgenerator;import com.mon.init.BlockEntityInit;import com.mon.init.ModItems;import com.mon.recipe.VirusGeneratorRecipe;import com.mon.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.poundNBT;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 mon.capabilities.Capability;import mon.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) {@Overrideprotected 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;}};}@Overridepublic int getContainerSize() {return this.items.size();}//你机器的名称@Overridepublic ITextComponent getDisplayName() {return new TranslationTextComponent("container.virus_generator");}@Overrideprotected ITextComponent getDefaultName() {return new TranslationTextComponent("container.virus_generator");}@Overrideprotected Container createMenu(int pContainerId, PlayerInventory pInventory) {return new VirusGeneratorMenu(pContainerId, pInventory, this, this.data);}@Nonnull@Overridepublic <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);}@Overridepublic void onLoad() {super.onLoad();lazyItemHandler = LazyOptional.of(() -> itemHandler);}@Overridepublic void invalidateCaps() {super.invalidateCaps();lazyItemHandler.invalidate();}//将你机器的当前反应进度存储到nbt标签中,virus_generator.progress里面存放progress数据@Overridepublic CompoundNBT save(@NotNull CompoundNBT tag) {tag.put("inventory", itemHandler.serializeNBT());tag.putInt("virus_generator.progress", progress);super.save(tag);return tag;}@Overridepublic 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);// }//在每一个时间刻对我们的机器的状态进行监视@Overridepublic 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();}//产出产物,同时所有原料数量-1private 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;}@Overridepublic boolean isEmpty() {for(ItemStack itemstack : this.items) {if (!itemstack.isEmpty()) {return false;}}return true;}@Overridepublic ItemStack getItem(int p_70301_1_) {return this.items.get(p_70301_1_);}@Overridepublic ItemStack removeItem(int p_70298_1_, int p_70298_2_) {return ItemStackHelper.removeItem(this.items, p_70298_1_, p_70298_2_);}@Overridepublic ItemStack removeItemNoUpdate(int p_70304_1_) {return ItemStackHelper.takeItem(this.items, p_70304_1_);}@Overridepublic 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();}}@Overridepublic 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;} }@Overridepublic void clearContent() {this.items.clear();}}
在init包中新建一个BlockEntityInit
类,注册我们模组中所有的方块实体:
BlockEntityInit.java
package com.mon.init;import com.joy187.re8joymod.Utils;import com.mon.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.mon.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 work.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;}@Overridepublic NonNullList<Ingredient> getIngredients() {return recipeItems;}// @Override// public ItemStack assemble(ItemStackHandler pContainer) {// return output;// }//判断我们两个原料是否和配方中对应的上@Overridepublic boolean matches(Inventory pContainer, World p_77569_2_) {return recipeItems.get(0).test(pContainer.getItem(1))&& recipeItems.get(1).test(pContainer.getItem(0));}@Overridepublic ItemStack assemble(Inventory p_77572_1_) {return output;}@Overridepublic boolean canCraftInDimensions(int pWidth, int pHeight) {return true;}@Overridepublic ItemStack getResultItem() {return output.copy();}@Overridepublic ResourceLocation getId() {return id;}@Overridepublic IRecipeSerializer<?> getSerializer() {return Serializer.INSTANCE;}@Overridepublic 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_generatorpublic static final ResourceLocation ID = new ResourceLocation(Utils.MOD_ID,"virus_generator");@Overridepublic VirusGeneratorRecipe fromJson(ResourceLocation id, JsonObject json) {ItemStack output = ShapedRecipe.itemFromJson(JSONUtils.getAsJsonObject(json, "output"));JsonArray ingredients = JSONUtils.getAsJsonArray(json, "ingredients");//因为我们有两个原料槽,所以这里是2NonNullList<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);}@Overridepublic 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);}@Overridepublic void toNetwork(PacketBuffer buf, VirusGeneratorRecipe recipe) {buf.writeInt(recipe.getIngredients().size());for (Ingredient ing : recipe.getIngredients()) {ing.toNetwork(buf);}buf.writeItemStack(recipe.getResultItem(), false);}@Overridepublic IRecipeSerializer<?> setRegistryName(ResourceLocation name) {return INSTANCE;}@Nullable@Overridepublic ResourceLocation getRegistryName() {return ID;}@Overridepublic Class<IRecipeSerializer<?>> getRegistryType() {return Serializer.castClass(IRecipeSerializer.class);}@SuppressWarnings("unchecked") // Need this wrapper, because genericsprivate static <G> Class<G> castClass(Class<?> cls) {return (Class<G>)cls;}}}
在init包中新建RecipeInit
类,将我们的配方类进行注册:
RecipeInit.java
package com.mon.init;import com.joy187.re8joymod.Utils;import com.mon.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.mon.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);}//渲染我们的背景图片@Overrideprotected 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());}}@Overridepublic 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.mon.screen;import com.mon.blocks.virusgenerator.VirusGeneratorBlockEntity;import com.mon.init.MenuInit;import com.mon.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 work.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 arrowint progressArrowSize = 13;return maxProgress != 0 && progress != 0 ? progress * progressArrowSize / maxProgress : 0;}// CREDIT GOES TO: diesieben07 | /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;// 这个和我们上面的槽位对应一致都是4private static final int TE_INVENTORY_SLOT_COUNT = 4; // must be the number of slots you have!@Overridepublic ItemStack quickMoveStack(PlayerEntity playerIn, int index) {Slot sourceSlot = slots.get(index);if (sourceSlot == null || !sourceSlot.hasItem()) return ItemStack.EMPTY; //EMPTY_ITEMItemStack sourceStack = sourceSlot.getItem();ItemStack copyOfSourceStack = sourceStack.copy();// Check if the slot clicked is one of the vanilla container slotsif (index < VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT) {// This is a vanilla container slot so merge the stack into the tile inventoryif (!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 inventoryif (!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 nullif (sourceStack.getCount() == 0) {sourceSlot.set(ItemStack.EMPTY);} else {sourceSlot.setChanged();}sourceSlot.onTake(playerIn, sourceStack);return copyOfSourceStack;}@Overridepublic 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.mon.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);}@Overridepublic boolean mayPlace(ItemStack stack) {return false;}}
5.在init包中新建MenuInit
类,将我们第四步中的菜单进行注册:
MenuInit.java
package com.mon.init;import com.joy187.re8joymod.Utils;import com.mon.screen.VirusGeneratorMenu;import net.minecraft.inventory.container.Container;import net.minecraft.inventory.container.ContainerType;import mon.extensions.IForgeContainerType;import net.minecraftforge.eventbus.api.IEventBus;import net.minecraftforge.fml.RegistryObject;import net.work.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
中添加我们方块的侧面、正面不工作、正面工作时的贴图:
在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"}}