本帖最后由 QQ酱71683 于 2022-9-3 21:44 编辑
Minecraft1.12.2 方块实体教程
Minecraft1.18.2 方块实体教程
MC中有许多很有趣的方块实体如告示牌、酿造台、附魔台...我们今天在1.16的版本下实现一个类似于熔炉的方块实体。
1.在blocks包中新建一个我们的方块实体包virusgenerator,包中新建一个我们的方块类`VirusGeneratorBlock`:
VirusGeneratorBlock.java
```
package com.joy187.re8joymod.common.blocks.virusgenerator;
import net.minecraft.block.*;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.DirectionProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.*;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraftforge.fml.network.NetworkHooks;
import javax.annotation.Nullable;
public class VirusGeneratorBlock extends ContainerBlock {
//方块实体的摆放朝向的参数
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
public static final BooleanProperty LIT = BlockStateProperties.LIT;
private static final VoxelShape SHAPE = Block.box(0, 0, 0, 16, 15, 16);
public VirusGeneratorBlock(AbstractBlock.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(LIT, Boolean.valueOf(false)));
}
@Override
public VoxelShape getShape(BlockState pState, IBlockReader pLevel, BlockPos pPos, ISelectionContext pContext) {
return SHAPE;
}
/* FACING */
@Override
public BlockState getStateForPlacement(BlockItemUseContext pContext) {
return this.defaultBlockState().setValue(FACING, pContext.getHorizontalDirection().getOpposite());
}
@Override
public BlockState rotate(BlockState pState, Rotation pRotation) {
return pState.setValue(FACING, pRotation.rotate(pState.getValue(FACING)));
}
@Override
public BlockState mirror(BlockState pState, Mirror pMirror) {
return pState.rotate(pMirror.getRotation(pState.getValue(FACING)));
}
@Override
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> pBuilder) {
pBuilder.add(FACING,LIT);
}
@Override
public BlockRenderType getRenderShape(BlockState pState) {
return BlockRenderType.MODEL;
}
@Override
public void onRemove(BlockState pState, World pLevel, BlockPos pPos, BlockState pNewState, boolean pIsMoving) {
if (pState.getBlock() != pNewState.getBlock()) {
TileEntity blockEntity = pLevel.getBlockEntity(pPos);
if (blockEntity instanceof VirusGeneratorBlockEntity) {
((VirusGeneratorBlockEntity) blockEntity).dropContents(); //.drops();
//InventoryHelper.dropContents(pLevel, pPos, (VirusGeneratorBlockEntity)blockEntity);
}
}
super.onRemove(pState, pLevel, pPos, pNewState, pIsMoving);
}
@Override
public ActionResultType use(BlockState pState, World pLevel, BlockPos pPos,
PlayerEntity pPlayer, Hand pHand, BlockRayTraceResult pHit) {
if (!pLevel.isClientSide()) {
TileEntity entity = pLevel.getBlockEntity(pPos);
if(entity instanceof VirusGeneratorBlockEntity) {
//执行打开gui的操作
NetworkHooks.openGui(((ServerPlayerEntity)pPlayer), (VirusGeneratorBlockEntity) entity, pPos);
} else {
throw new IllegalStateException("Our Container provider is missing!");
}
}
return ActionResultType.sidedSuccess(pLevel.isClientSide());
}
@Nullable
@Override
public TileEntity newBlockEntity(IBlockReader p_196283_1_) {
return new VirusGeneratorBlockEntity();
}
}
```
在BlockInit中注册我们的方块:
```
public static RegistryObject<Block> VIRUS_GENERATOR_BLOCK = BLOCKS.register("virus_generator",
()-> new VirusGeneratorBlock(AbstractBlock.Properties.copy(Blocks.IRON_BLOCK).harvestTool(ToolType.PICKAXE).harvestLevel(1).requiresCorrectToolForDrops()));
```
2.在virusgenerator包中新建一个我们的实体类`VirusGeneratorBlockEntity`:
VirusGeneratorBlockEntity.java
```
package com.joy187.re8joymod.common.blocks.virusgenerator;
import com.joy187.re8joymod.common.init.BlockEntityInit;
import com.joy187.re8joymod.common.init.ModItems;
import com.joy187.re8joymod.common.recipe.VirusGeneratorRecipe;
import com.joy187.re8joymod.common.screen.VirusGeneratorMenu;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.Inventory;
import net.minecraft.inventory.InventoryHelper;
import net.minecraft.inventory.ItemStackHelper;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.INamedContainerProvider;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.LockableTileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.IIntArray;
import net.minecraft.util.NonNullList;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemStackHandler;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
import java.util.Optional;
public class VirusGeneratorBlockEntity extends LockableTileEntity implements INamedContainerProvider, ITickableTileEntity {
protected NonNullList<ItemStack> items = NonNullList.withSize(4, ItemStack.EMPTY);
protected IRecipeType<? extends VirusGeneratorRecipe> recipeType;
private final ItemStackHandler itemHandler = new ItemStackHandler(4) {
@Override
protected void onContentsChanged(int slot) {
setChanged();
}
};
private LazyOptional<IItemHandler> lazyItemHandler = LazyOptional.empty();
//我们的方块有当前反应进度和总反应进度两个数据
protected IIntArray data = new IIntArray() {
public int get(int index) {
switch (index) {
case 0: return VirusGeneratorBlockEntity.this.progress;
case 1: return VirusGeneratorBlockEntity.this.maxProgress;
default: return 0;
}
}
public void set(int index, int value) {
switch(index) {
case 0: VirusGeneratorBlockEntity.this.progress = value; break;
case 1: VirusGeneratorBlockEntity.this.maxProgress = value; break;
}
}
public int getCount() {
return 2;
}
};
private int progress = 0;
private int maxProgress = 72;
public VirusGeneratorBlockEntity() {
super(BlockEntityInit.VIRUS_GENERATOR_BLOCK_ENTITY.get());
this.data=new IIntArray() {
public int get(int index) {
switch (index) {
case 0: return VirusGeneratorBlockEntity.this.progress;
case 1: return VirusGeneratorBlockEntity.this.maxProgress;
default: return 0;
}
}
public void set(int index, int value) {
switch(index) {
case 0: VirusGeneratorBlockEntity.this.progress = value; break;
case 1: VirusGeneratorBlockEntity.this.maxProgress = value; break;
}
}
public int getCount() {
return 2;
}
};
}
@Override
public int getContainerSize() {
return this.items.size();
}
//你机器的名称
@Override
public ITextComponent getDisplayName() {
return new TranslationTextComponent("container.virus_generator");
}
@Override
protected ITextComponent getDefaultName() {
return new TranslationTextComponent("container.virus_generator");
}
@Override
protected Container createMenu(int pContainerId, PlayerInventory pInventory) {
return new VirusGeneratorMenu(pContainerId, pInventory, this, this.data);
}
@Nonnull
@Override
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @javax.annotation.Nullable Direction side) {
if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) {
return lazyItemHandler.cast();
}
return super.getCapability(cap, side);
}
@Override
public void onLoad() {
super.onLoad();
lazyItemHandler = LazyOptional.of(() -> itemHandler);
}
@Override
public void invalidateCaps() {
super.invalidateCaps();
lazyItemHandler.invalidate();
}
//将你机器的当前反应进度存储到nbt标签中,virus_generator.progress里面存放progress数据
@Override
public CompoundNBT save(@NotNull CompoundNBT tag) {
tag.put("inventory", itemHandler.serializeNBT());
tag.putInt("virus_generator.progress", progress);
super.save(tag);
return tag;
}
@Override
public void load(BlockState p_230337_1_,CompoundNBT nbt) {
super.load(p_230337_1_,nbt);
this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY);
ItemStackHelper.loadAllItems(nbt, this.items);
itemHandler.deserializeNBT(nbt.getCompound("inventory"));
progress = nbt.getInt("virus_generator.progress");
}
public void dropContents() {
Inventory inventory = new Inventory(itemHandler.getSlots());
for (int i = 0; i < itemHandler.getSlots(); i++) {
inventory.setItem(i, itemHandler.getStackInSlot(i));
}
InventoryHelper.dropContents(this.level, this.getBlockPos(), inventory);
}
// public void drops() {
// SimpleContainer inventory = new SimpleContainer(itemHandler.getSlots());
// for (int i = 0; i < itemHandler.getSlots(); i++) {
// inventory.setItem(i, itemHandler.getStackInSlot(i));
// }
//
// Containers.dropContents(this.level, this.worldPosition, inventory);
// }
//在每一个时间刻对我们的机器的状态进行监视
@Override
public void tick() {
if(hasRecipe(this)) {
this.getBlockState().setValue(VirusGeneratorBlock.LIT, Boolean.valueOf(true));
this.progress++;
//setChanged(pLevel, pPos, pState);
setChanged();
//当前进度已经超过了最大进度,说明要产出产物了
if(this.progress > this.maxProgress) {
//调用生成产物函数
craftItem(this);
}
} else {
this.getBlockState().setValue(VirusGeneratorBlock.LIT, Boolean.valueOf(false));
this.resetProgress();
//setChanged(pLevel, pPos, pState);
setChanged();
}
}
//判断我们放的原料是否是一个正确的配方
private boolean hasRecipe(VirusGeneratorBlockEntity entity) {
World level = entity.level;
Inventory inventory = new Inventory(entity.itemHandler.getSlots());
for (int i = 0; i < entity.itemHandler.getSlots(); i++) {
inventory.setItem(i, entity.itemHandler.getStackInSlot(i));
}
Optional<VirusGeneratorRecipe> match = level.getRecipeManager()
.getRecipeFor((IRecipeType)VirusGeneratorRecipe.Type.INSTANCE, inventory, level);
return match.isPresent() && canInsertAmountIntoOutputSlot(inventory)
&& canInsertItemIntoOutputSlot(inventory, match.get().getResultItem())
&& hasFuelSlot(entity); // && has0Slot(entity) && has1Slot(entity);
}
//判断我们的燃料是否放入
private static boolean hasFuelSlot(VirusGeneratorBlockEntity entity) {
return entity.itemHandler.getStackInSlot(2).getItem() == ModItems.BLACKSHEEP.get();
}
//产出产物,同时所有原料数量-1
private static void craftItem(VirusGeneratorBlockEntity entity) {
World level = entity.level;
Inventory inventory = new Inventory(entity.itemHandler.getSlots());
for (int i = 0; i < entity.itemHandler.getSlots(); i++) {
inventory.setItem(i, entity.itemHandler.getStackInSlot(i));
}
//IRecipe<?> match = level.getRecipeManager().getRecipeFor(RecipeInit.VIRUS_GENERATOR_SERIALIZER., inventory, level).orElse(null);
// Optional<VirusGeneratorRecipe> match = level.getRecipeManager()
// .getRecipeFor((IRecipeType)RecipeInit.VIRUS_GENERATOR_SERIALIZER.get(), inventory, level);
Optional<VirusGeneratorRecipe> match = level.getRecipeManager()
.getRecipeFor((IRecipeType)VirusGeneratorRecipe.Type.INSTANCE, inventory, level);
//return this.level.getRecipeManager().getRecipeFor((IRecipeType)this.recipeType, new Inventory(p_217057_1_), this.level).isPresent();
if(match.isPresent()) {
//if(has0Slot(entity) && has1Slot(entity) && hasFuelSlot(entity)) {
entity.itemHandler.extractItem(0,1, false);
entity.itemHandler.extractItem(1,1, false);
entity.itemHandler.extractItem(2,1, false);
entity.itemHandler.setStackInSlot(3, new ItemStack(match.get().getResultItem().getItem(),
entity.itemHandler.getStackInSlot(3).getCount() + 1));
//产出后就重置我们的进度,开始生产下一个产物
entity.resetProgress();
}
}
private void resetProgress() {
this.progress = 0;
}
private boolean canInsertItemIntoOutputSlot(Inventory inventory, ItemStack output) {
return this.getItem(3).getItem() == output.getItem() || this.getItem(3).isEmpty();
}
private boolean canInsertAmountIntoOutputSlot(Inventory inventory) {
return this.getItem(3).getMaxStackSize() > this.getItem(3).getCount();
}
private static boolean notReachLimit(VirusGeneratorBlockEntity entity) {
return entity.itemHandler.getStackInSlot(3).getCount()<64;
}
@Override
public boolean isEmpty() {
for(ItemStack itemstack : this.items) {
if (!itemstack.isEmpty()) {
return false;
}
}
return true;
}
@Override
public ItemStack getItem(int p_70301_1_) {
return this.items.get(p_70301_1_);
}
@Override
public ItemStack removeItem(int p_70298_1_, int p_70298_2_) {
return ItemStackHelper.removeItem(this.items, p_70298_1_, p_70298_2_);
}
@Override
public ItemStack removeItemNoUpdate(int p_70304_1_) {
return ItemStackHelper.takeItem(this.items, p_70304_1_);
}
@Override
public void setItem(int p_70299_1_, ItemStack p_70299_2_) {
ItemStack itemstack = this.items.get(p_70299_1_);
boolean flag = !p_70299_2_.isEmpty() && p_70299_2_.sameItem(itemstack) && ItemStack.tagMatches(p_70299_2_, itemstack);
this.items.set(p_70299_1_, p_70299_2_);
if (p_70299_2_.getCount() > this.getMaxStackSize()) {
p_70299_2_.setCount(this.getMaxStackSize());
}
if (p_70299_1_ == 0 && !flag) {
this.progress = 0;
this.setChanged();
}
}
@Override
public boolean stillValid(PlayerEntity p_70300_1_) {
if (this.level.getBlockEntity(this.worldPosition) != this) {
return false;
} else {
return p_70300_1_.distanceToSqr((double)this.worldPosition.getX() + 0.5D, (double)this.worldPosition.getY() + 0.5D, (double)this.worldPosition.getZ() + 0.5D) <= 64.0D;
} }
@Override
public void clearContent() {
this.items.clear();
}
}
```
在init包中新建一个`BlockEntityInit`类,注册我们模组中所有的方块实体:
`BlockEntityInit.java`
```
package com.joy187.re8joymod.common.init;
import com.joy187.re8joymod.Utils;
import com.joy187.re8joymod.common.blocks.virusgenerator.VirusGeneratorBlockEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.RegistryObject;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
public class BlockEntityInit {
public static final DeferredRegister<TileEntityType<?>> BLOCK_ENTITIES =
DeferredRegister.create(ForgeRegistries.TILE_ENTITIES, Utils.MOD_ID);
//注册我们的方块实体
public static RegistryObject<TileEntityType<VirusGeneratorBlockEntity>> VIRUS_GENERATOR_BLOCK_ENTITY =
BLOCK_ENTITIES.register("virus_generator_block_entity", () -> TileEntityType.Builder.of(
VirusGeneratorBlockEntity::new, ModBlocks.VIRUS_GENERATOR_BLOCK.get()).build(null));
public static void register(IEventBus eventBus) {
BLOCK_ENTITIES.register(eventBus);
}
}
```
#### 在我们的项目主类中的Main函数中将`BlockEntityInit`类进行注册:
```
public Main(){
IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus();
bus.addListener(this::setup);
bus.addListener(this::doClientStuff);
SoundInit.SOUND_TYPES.register(bus);
EntityInit.ENTITY_TYPES.register(bus);
ModItems.ITEMS.register(bus);
ModBlocks.BLOCKS.register(bus);
//添加这个
BlockEntityInit.register(bus);
}
```
### 3.在Java包中新建一个我们的配方包`recipe` -> `recipe`包中新建`VirusGeneratorRecipe`类:
`VirusGeneratorRecipe.java`
```
package com.joy187.re8joymod.common.recipe;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.joy187.re8joymod.Utils;
import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.*;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.Nullable;
public class VirusGeneratorRecipe implements IRecipe<Inventory> {
private final ResourceLocation id;
private final ItemStack output;
private final NonNullList<Ingredient> recipeItems;
public VirusGeneratorRecipe(ResourceLocation id, ItemStack output,
NonNullList<Ingredient> recipeItems) {
this.id = id;
this.output = output;
this.recipeItems = recipeItems;
}
@Override
public NonNullList<Ingredient> getIngredients() {
return recipeItems;
}
// @Override
// public ItemStack assemble(ItemStackHandler pContainer) {
// return output;
// }
//判断我们两个原料是否和配方中对应的上
@Override
public boolean matches(Inventory pContainer, World p_77569_2_) {
return recipeItems.get(0).test(pContainer.getItem(1))
&& recipeItems.get(1).test(pContainer.getItem(0));
}
@Override
public ItemStack assemble(Inventory p_77572_1_) {
return output;
}
@Override
public boolean canCraftInDimensions(int pWidth, int pHeight) {
return true;
}
@Override
public ItemStack getResultItem() {
return output.copy();
}
@Override
public ResourceLocation getId() {
return id;
}
@Override
public IRecipeSerializer<?> getSerializer() {
return Serializer.INSTANCE;
}
@Override
public IRecipeType<?> getType() {
return Type.INSTANCE;
}
public static class Type implements IRecipeType<VirusGeneratorRecipe> {
private Type() { }
public static final Type INSTANCE = new Type();
public static final String ID = "virus_generator";
}
//我们配方是将.json文件进行转换后在游戏进行判断
public static class Serializer implements IRecipeSerializer<VirusGeneratorRecipe> {
public static final Serializer INSTANCE = new Serializer();
//这个名称很重要,因为我们把这个配方类型定义为了virus_generator
public static final ResourceLocation ID = new ResourceLocation(Utils.MOD_ID,"virus_generator");
@Override
public VirusGeneratorRecipe fromJson(ResourceLocation id, JsonObject json) {
ItemStack output = ShapedRecipe.itemFromJson(JSONUtils.getAsJsonObject(json, "output"));
JsonArray ingredients = JSONUtils.getAsJsonArray(json, "ingredients");
//因为我们有两个原料槽,所以这里是2
NonNullList<Ingredient> inputs = NonNullList.withSize(2, Ingredient.EMPTY);
for (int i = 0; i < inputs.size(); i++) {
inputs.set(i, Ingredient.fromJson(ingredients.get(i)));
}
return new VirusGeneratorRecipe(id, output, inputs);
}
@Override
public VirusGeneratorRecipe fromNetwork(ResourceLocation id, PacketBuffer buf) {
NonNullList<Ingredient> inputs = NonNullList.withSize(buf.readInt(), Ingredient.EMPTY);
for (int i = 0; i < inputs.size(); i++) {
inputs.set(i, Ingredient.fromNetwork(buf));
}
ItemStack output = buf.readItem();
return new VirusGeneratorRecipe(id, output, inputs);
}
@Override
public void toNetwork(PacketBuffer buf, VirusGeneratorRecipe recipe) {
buf.writeInt(recipe.getIngredients().size());
for (Ingredient ing : recipe.getIngredients()) {
ing.toNetwork(buf);
}
buf.writeItemStack(recipe.getResultItem(), false);
}
@Override
public IRecipeSerializer<?> setRegistryName(ResourceLocation name) {
return INSTANCE;
}
@Nullable
@Override
public ResourceLocation getRegistryName() {
return ID;
}
@Override
public Class<IRecipeSerializer<?>> getRegistryType() {
return Serializer.castClass(IRecipeSerializer.class);
}
@SuppressWarnings("unchecked") // Need this wrapper, because generics
private static <G> Class<G> castClass(Class<?> cls) {
return (Class<G>)cls;
}
}
}
```
在init包中新建`RecipeInit`类,将我们的配方类进行注册:
`RecipeInit.java`
```
package com.joy187.re8joymod.common.init;
import com.joy187.re8joymod.Utils;
import com.joy187.re8joymod.common.recipe.VirusGeneratorRecipe;
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.RegistryObject;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
public class RecipeInit
{
public static final DeferredRegister<IRecipeSerializer<?>> SERIALIZERS =
DeferredRegister.create(ForgeRegistries.RECIPE_SERIALIZERS, Utils.MOD_ID);
//注册我们的配方类
public static final RegistryObject<IRecipeSerializer<?>> VIRUS_GENERATOR_SERIALIZER =
SERIALIZERS.register("virus_generator", () -> VirusGeneratorRecipe.Serializer.INSTANCE);
public static void register(IEventBus eventBus) {
SERIALIZERS.register(eventBus);
}
}
```
#### 在我们的项目主类中的Main函数中将`BlockEntityInit`类进行注册:
```
public Main(){
IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus();
bus.addListener(this::setup);
bus.addListener(this::doClientStuff);
SoundInit.SOUND_TYPES.register(bus);
EntityInit.ENTITY_TYPES.register(bus);
ModItems.ITEMS.register(bus);
ModBlocks.BLOCKS.register(bus);
BlockEntityInit.register(bus);
//添加这个
RecipeInit.register(bus);
}
```
4.方块和实体的代码部分结束,进入到GUI的制作环节。在Java包中新建一个screen包 -> screen包中新建`VirusGeneratorScreen`类指明我们GUI贴图存放的位置:
`VirusGeneratorScreen.java`
```
package com.joy187.re8joymod.common.screen;
import com.joy187.re8joymod.Utils;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
public class VirusGeneratorScreen extends ContainerScreen<VirusGeneratorMenu> {
//我们的gui图片
private static final ResourceLocation TEXTURE =
new ResourceLocation(Utils.MOD_ID, "textures/gui/virus_generator.png");
public VirusGeneratorScreen(VirusGeneratorMenu pMenu, PlayerInventory pPlayerInventory, ITextComponent pTitle) {
super(pMenu, pPlayerInventory, pTitle);
}
//渲染我们的背景图片
@Override
protected void renderBg(MatrixStack pPoseStack, float pPartialTick, int pMouseX, int pMouseY) {
//RenderSystem.(GameRenderer::getPositionTexShader);
RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
this.minecraft.getTextureManager().bind(TEXTURE);
//RenderSystem.(0, TEXTURE);
int x = (width - imageWidth) / 2;
int y = (height - imageHeight) / 2;
this.blit(pPoseStack, x, y, 0, 0, imageWidth, imageHeight);
if(this.menu.isCrafting()) {
//这里渲染的是我们的进度条,就像熔炉一样,坐标对应关系请参考最上方1.12.2的教程
blit(pPoseStack, x + 8, y + 54+12-13, 176, 12-13, 14, this.menu.getScaledProgress());
}
}
@Override
public void render(MatrixStack pPoseStack, int mouseX, int mouseY, float delta) {
renderBackground(pPoseStack);
// this.getMinecraft().getTextureManager().bind(TEXTURE);
// this.getMinecraft().getTextureManager().getTexture(TEXTURE);
super.render(pPoseStack, mouseX, mouseY, delta);
//renderTooltip(pPoseStack, mouseX, mouseY);
}
}
```
screen包中新建`VirusGeneratorMenu`类将所有的槽位的位置都指出来:
`VirusGeneratorMenu.java`
```
package com.joy187.re8joymod.common.screen;
import com.joy187.re8joymod.common.blocks.virusgenerator.VirusGeneratorBlockEntity;
import com.joy187.re8joymod.common.init.MenuInit;
import com.joy187.re8joymod.common.init.ModBlocks;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketBuffer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IIntArray;
import net.minecraft.util.IWorldPosCallable;
import net.minecraft.util.IntArray;
import net.minecraft.world.World;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.SlotItemHandler;
public class VirusGeneratorMenu extends Container {
private final VirusGeneratorBlockEntity blockEntity;
private final World level;
private final IIntArray data;
public VirusGeneratorMenu(int pContainerId, PlayerInventory inv, PacketBuffer extraData) {
this(pContainerId, inv, inv.player.level.getBlockEntity(extraData.readBlockPos()), new IntArray(2));
}
public VirusGeneratorMenu(int pContainerId, PlayerInventory inv, TileEntity entity, IIntArray data) {
super(MenuInit.VIRUS_GENERATOR_MENU.get(), pContainerId);
//我们一共有4个槽,一个燃料槽,两个原料槽,一个产物槽
checkContainerSize(inv, 4);
blockEntity = ((VirusGeneratorBlockEntity) entity);
this.level = inv.player.level;
this.data = data;
addPlayerInventory(inv);
addPlayerHotbar(inv);
this.blockEntity.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).ifPresent(handler -> {
//设定坐标
this.addSlot(new SlotItemHandler(handler, 0, 26, 11));
this.addSlot(new SlotItemHandler(handler, 1, 26, 59));
this.addSlot(new SlotItemHandler(handler, 2, 7, 35));
this.addSlot(new ModResultSlot(handler, 3, 81, 36));
});
addDataSlots(data);
}
public boolean isCrafting() {
return data.get(0) > 0;
}
public int getScaledProgress() {
int progress = this.data.get(0);
int maxProgress = this.data.get(1); // Max Progress
//int progressArrowSize = 26; // This is the height in pixels of your arrow
int progressArrowSize = 13;
return maxProgress != 0 && progress != 0 ? progress * progressArrowSize / maxProgress : 0;
}
// must assign a slot number to each of the slots used by the GUI.
// For this container, we can see both the tile inventory's slots as well as the player inventory slots and the hotbar.
// Each time we add a Slot to the container, it automatically increases the slotIndex, which means
// 0 - 8 = hotbar slots (which will map to the InventoryPlayer slot numbers 0 - 8)
// 9 - 35 = player inventory slots (which map to the InventoryPlayer slot numbers 9 - 35)
// 36 - 44 = TileInventory slots, which map to our TileEntity slot numbers 0 - 8)
private static final int HOTBAR_SLOT_COUNT = 9;
private static final int PLAYER_INVENTORY_ROW_COUNT = 3;
private static final int PLAYER_INVENTORY_COLUMN_COUNT = 9;
private static final int PLAYER_INVENTORY_SLOT_COUNT = PLAYER_INVENTORY_COLUMN_COUNT * PLAYER_INVENTORY_ROW_COUNT;
private static final int VANILLA_SLOT_COUNT = HOTBAR_SLOT_COUNT + PLAYER_INVENTORY_SLOT_COUNT;
private static final int VANILLA_FIRST_SLOT_INDEX = 0;
private static final int TE_INVENTORY_FIRST_SLOT_INDEX = VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT;
// 这个和我们上面的槽位对应一致都是4
private static final int TE_INVENTORY_SLOT_COUNT = 4; // must be the number of slots you have!
@Override
public ItemStack quickMoveStack(PlayerEntity playerIn, int index) {
Slot sourceSlot = slots.get(index);
if (sourceSlot == null || !sourceSlot.hasItem()) return ItemStack.EMPTY; //EMPTY_ITEM
ItemStack sourceStack = sourceSlot.getItem();
ItemStack copyOfSourceStack = sourceStack.copy();
// Check if the slot clicked is one of the vanilla container slots
if (index < VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT) {
// This is a vanilla container slot so merge the stack into the tile inventory
if (!moveItemStackTo(sourceStack, TE_INVENTORY_FIRST_SLOT_INDEX, TE_INVENTORY_FIRST_SLOT_INDEX
+ TE_INVENTORY_SLOT_COUNT, false)) {
return ItemStack.EMPTY; // EMPTY_ITEM
}
} else if (index < TE_INVENTORY_FIRST_SLOT_INDEX + TE_INVENTORY_SLOT_COUNT) {
// This is a TE slot so merge the stack into the players inventory
if (!moveItemStackTo(sourceStack, VANILLA_FIRST_SLOT_INDEX, VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT, false)) {
return ItemStack.EMPTY;
}
} else {
//System.out.println("Invalid slotIndex:" + index);
return ItemStack.EMPTY;
}
// If stack size == 0 (the entire stack was moved) set slot contents to null
if (sourceStack.getCount() == 0) {
sourceSlot.set(ItemStack.EMPTY);
} else {
sourceSlot.setChanged();
}
sourceSlot.onTake(playerIn, sourceStack);
return copyOfSourceStack;
}
@Override
public boolean stillValid(PlayerEntity pPlayer) {
return stillValid(IWorldPosCallable.create(level, blockEntity.getBlockPos()),
pPlayer, ModBlocks.VIRUS_GENERATOR_BLOCK.get());
}
//对玩家的物品槽进行渲染,不需要改动
private void addPlayerInventory(PlayerInventory playerInventory) {
for (int i = 0; i < 3; ++i) {
for (int l = 0; l < 9; ++l) {
this.addSlot(new Slot(playerInventory, l + i * 9 + 9, 8 + l * 18, 84 + i * 18));
}
}
}
//对玩家的物品槽进行渲染,不需要改动
private void addPlayerHotbar(PlayerInventory playerInventory) {
for (int i = 0; i < 9; ++i) {
this.addSlot(new Slot(playerInventory, i, 8 + i * 18, 142));
}
}
}
```
在screen包中新建`ModResultSlot`类,将我们的产物放置进行预设。
`ModResultSlot.java`
```
package com.joy187.re8joymod.common.screen;
import net.minecraft.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.SlotItemHandler;
public class ModResultSlot extends SlotItemHandler {
public ModResultSlot(IItemHandler itemHandler, int index, int x, int y) {
super(itemHandler, index, x, y);
}
@Override
public boolean mayPlace(ItemStack stack) {
return false;
}
}
```
5.在init包中新建`MenuInit`类,将我们第四步中的菜单进行注册:
`MenuInit.java`
```
package com.joy187.re8joymod.common.init;
import com.joy187.re8joymod.Utils;
import com.joy187.re8joymod.common.screen.VirusGeneratorMenu;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.ContainerType;
import net.minecraftforge.common.extensions.IForgeContainerType;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.RegistryObject;
import net.minecraftforge.fml.network.IContainerFactory;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
public class MenuInit {
public static final DeferredRegister<ContainerType<?>> MENUS =
DeferredRegister.create(ForgeRegistries.CONTAINERS, Utils.MOD_ID);
//将我们的屏幕信息进行注册
public static final RegistryObject<ContainerType<VirusGeneratorMenu>> VIRUS_GENERATOR_MENU =
registerMenuType(VirusGeneratorMenu::new, "virus_generator_menu");
private static <T extends Container>RegistryObject<ContainerType<T>> registerMenuType(IContainerFactory<T> factory,
String name) {
return MENUS.register(name, () -> IForgeContainerType.create(factory));
}
public static void register(IEventBus eventBus) {
MENUS.register(eventBus);
}
}
```
#### 在我们的项目主类中的Main函数中将`MenuInit`类进行注册:
```
public Main(){
IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus();
bus.addListener(this::setup);
bus.addListener(this::doClientStuff);
SoundInit.SOUND_TYPES.register(bus);
EntityInit.ENTITY_TYPES.register(bus);
ModItems.ITEMS.register(bus);
ModBlocks.BLOCKS.register(bus);
BlockEntityInit.register(bus);
RecipeInit.register(bus);
//添加这个
MenuInit.register(bus);
}
```
#### 在项目主类的doClientStuff函数中将我们的gui进行注册,同时和屏幕信息绑定
```
private void doClientStuff(final FMLClientSetupEvent event) {
event.enqueueWork(() -> {
RenderTypeLookup.setRenderLayer(ModBlocks.HERB_BLOCK.get(), RenderType.cutout());
RenderTypeLookup.setRenderLayer(ModBlocks.PINK_ROSE.get(), RenderType.cutout());
RenderTypeLookup.setRenderLayer(ModBlocks.EBONY_LEAVES.get(), RenderType.cutout());
RenderTypeLookup.setRenderLayer(ModBlocks.EBONY_SAPLING.get(), RenderType.cutout());
//添加这个
ScreenManager.register(MenuInit.VIRUS_GENERATOR_MENU.get(), VirusGeneratorScreen::new);
});
}
```
6.代码部分结束,来到资源包制作。在`src\main\resources\assets\你的modid\blockstates`中新建我们的方块的状态文件:
`virus_generator.json`
```
{
"variants": {
"facing=east,lit=false": {
"model": "re8joymod:block/virus_generator",
"y": 90
},
"facing=east,lit=true": {
"model": "re8joymod:block/virus_generator_on",
"y": 90
},
"facing=north,lit=false": {
"model": "re8joymod:block/virus_generator"
},
"facing=north,lit=true": {
"model": "re8joymod:block/virus_generator_on"
},
"facing=south,lit=false": {
"model": "re8joymod:block/virus_generator",
"y": 180
},
"facing=south,lit=true": {
"model": "re8joymod:block/virus_generator_on",
"y": 180
},
"facing=west,lit=false": {
"model": "re8joymod:block/virus_generator",
"y": 270
},
"facing=west,lit=true": {
"model": "re8joymod:block/virus_generator_on",
"y": 270
}
}
}
```
在`src\main\resources\assets\你的modid\models\block`中新建两个我们的方块模型文件:
##### 方块平时的模型
`virus_generator.json`
```
{
"parent": "block/orientable",
"textures": {
"top": "re8joymod:blocks/virus_generator_side",
"front": "re8joymod:blocks/virus_generator",
"side": "re8joymod:blocks/virus_generator_side"
}
}
```
##### 方块工作时的模型
`virus_generator_on.json`
```
{
"parent": "block/orientable",
"textures": {
"top": "re8joymod:blocks/virus_generator_side",
"front": "re8joymod:blocks/virus_generator_on",
"side": "re8joymod:blocks/virus_generator_side"
}
}
```
在`models\item`中添加我们手拿方块时的模型文件
`virus_generator.json`
```
{
"parent": "re8joymod:block/virus_generator"
}
```
在`textures\block`中添加我们方块的侧面、正面不工作、正面工作时的贴图:
![cr4.png]()
在textures包中新建gui包 -> gui包中把我们的gui(大小为256×256像素点)放进去:
![cr5.png]()
在lang包中的`en_us.json`文件中加上我们方块实体的名称和打开机器后上面显示的名称:
```
"block.re8joymod.virus_generator":"Virus Analyser",
"container.virus_generator":"Virus Analyser",
```
8.在`src\main\resources\data\你的modid\recipes`中新建几个属于我们的方块实体的配方:
记得在第三步中我们把配方类型设置为`virus_generator`,所以配方的type就写为`virus_generator`:
`evirus.json`
```
{
"type":"re8joymod:virus_generator",
"ingredients":[
{
"item":"re8joymod:humus"
},
{
"item":"re8joymod:herbglass"
}
],
"output":{
"item":"re8joymod:evirus"
}
}
```
9.保存所有文件 -> 进入游戏调试:
#### 首先拿出我们的机器并放置下来,外观显示正常
![mac]()
将燃料和产物放入,成功产出了产物!
![cr8.png]()
|