- Версия(и) Minecraft
- 1.12.х
Это более продвинутый учебник, в котором представлены различные новые концепции. Сначала вы увидите, как создать
Для начала убедитесь что вы добавили в ваш
Затем добавляем блок:
Затем работаем над
Наконец у нас есть работающий TESR. Обратите внимание, что статическая модель здесь не отображается. Это обрабатывается автоматически. Нам нужно только сделать анимирующие рукояти и предмет:
И, наконец, нам нужен .json файл состояния блока:
TileEntity
, который должен синхронизировать данные с сервера на клиент. Вы также увидите, как мы можем использовать статическую .obj модель, так и анимированную динамическую .obj модель. И, наконец, вы увидите, как рендерить любой предмет прямо в мире. Для этого нам необходимо реализовать TESR ("TileEntitySpecialRenderer
"). Конечные результат будет выглядят вот как-то так. Вы сможете так же вставлять и удалять предметы в блоке, щелкнув правой кнопкой мыши по этому самому блоку:Для начала убедитесь что вы добавили в ваш
ClientProxy.preInit ()
следующий код, который гарантировал бы работу ․օbj загрузчика:
ClientProxy.java:
public static class ClientProxy extends CommonProxy {
@Override
public void preInit(FMLPreInitializationEvent e) {
super.preInit(e);
OBJLoader.INSTANCE.addDomain(MODID);
...
}
}
Затем добавляем блок:
PedestalBlock.java:
public class PedestalBlock extends Block implements ITileEntityProvider {
//Для рендера
public static final IProperty<Boolean> IS_HANDLES = PropertyBool.create("is_handles");
public PedestalBlock() {
super(Material.ROCK);
setUnlocalizedName(ModTut.MODID + ".pedestalblock");
setRegistryName("pedestalblock");
setDefaultState(blockState.getBaseState().withProperty(IS_HANDLES, false));
}
@Override
public IBlockState getActualState(IBlockState state, IBlockAccess worldIn, BlockPos pos) {
return state.withProperty(IS_HANDLES, false);
}
@Override
protected BlockStateContainer createBlockState() {
return new BlockStateContainer(this, IS_HANDLES);
}
@Override
public IBlockState getStateFromMeta(int meta) {
return getDefaultState();
}
@Override
public int getMetaFromState(IBlockState state) {
return 0;
}
@SideOnly(Side.CLIENT)
public void initModel() {
ModelLoader.setCustomModelResourceLocation(Item.getItemFromBlock(this), 0, new ModelResourceLocation(getRegistryName(), "inventory"));
//Связать TESR с TileEntity
ClientRegistry.bindTileEntitySpecialRenderer(PedestalTileEntity.class, new PedestalTESR());
}
@Override
public TileEntity createNewTileEntity(World worldIn, int meta) {
return new PedestalTileEntity();
}
@Override
@SideOnly(Side.CLIENT)
public boolean shouldSideBeRendered(IBlockState blockState, IBlockAccess worldIn, BlockPos pos, EnumFacing side) {
return false;
}
@Override
public boolean isBlockNormalCube(IBlockState blockState) {
return false;
}
@Override
public boolean isOpaqueCube(IBlockState blockState) {
return false;
}
private PedestalTileEntity getTE(World world, BlockPos pos) {
return (PedestalTileEntity) world.getTileEntity(pos);
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player,
EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ) {
if (!world.isRemote) {
PedestalTileEntity te = getTE(world, pos);
if (te.getStack().isEmpty()) {
if (!player.getHeldItem(hand).isEmpty()) {
//Ели на пьедестале нет предмета, а игрок держит предмет. Мы перемещаем этот предмет на пьедестал
te.setStack(player.getHeldItem(hand));
player.inventory.setInventorySlotContents(player.inventory.currentItem, ItemStack.EMPTY);
//Убедитесь, что клиентская сторона знает об изменениях в инвентаре игрока
player.openContainer.detectAndSendChanges();
}
} else {
//В пьедестале есть стек. В этом случае мы удаляем его и пытаемся положить его в инвентарь игрока, если есть место
ItemStack stack = te.getStack();
te.setStack(ItemStack.EMPTY);
if (!player.inventory.addItemStackToInventory(stack)) {
//Если в инвентаре нет места, выкидываем предмет в мир
EntityItem entityItem = new EntityItem(world, pos.getX(), pos.getY()+1, pos.getZ(), stack);
world.spawnEntity(entityItem);
} else {
player.openContainer.detectAndSendChanges();
}
}
}
//Возвращаем true на клиенте, чтобы убедиться, что игра знает, что мы обработали этот блок, и не надо его пытаться разместить на клиенте.
return true;
}
}
Затем работаем над
TileEntity
. Обратите внимание на дополнительный код, который мы используем для синхронизации вашего TileEntity
с клиентом при необходимости:
PedestalTileEntity.java:
public class PedestalTileEntity extends TileEntity {
private ItemStack stack = ItemStack.EMPTY;
public ItemStack getStack() {
return stack;
}
public void setStack(ItemStack stack) {
this.stack = stack;
markDirty();
if (world != null) {
IBlockState state = worldObj.getBlockState(getPos());
world.notifyBlockUpdate(getPos(), state, state, 3);
}
}
@Override
public NBTTagCompound getUpdateTag() {
//getUpdateTag () вызывается всякий раз, когда данные чанка отправляются на сторону клиента.
//В противоположность этому имееться getUpdatePacket () который вызывается, когда сама TileEntity хочет синхронизироваться с клиентом.
//Во многих случаях можно отправить ту же информацию через getUpdateTag (), или getUpdatePacket ().
return writeToNBT(new NBTTagCompound());
}
@Override
public SPacketUpdateTileEntity getUpdatePacket() {
// Подготовить пакет для синхронизации TileEntity с клиентом.
//Поскольку нам нужно только синхронизировать стек, и это все, что у нас есть, мы просто напишем здесь весь NBT.
//Если у нас имееться сложный TileEntity, которому не нужно иметь всю информацию о клиенте, вы можете написать более оптимальный NBT здесь.
NBTTagCompound nbtTag = new NBTTagCompound();
this.writeToNBT(nbtTag);
return new SPacketUpdateTileEntity(getPos(), 1, nbtTag);
}
@Override
public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity packet) {
//Здесь мы получаем пакет с сервера и записываем его в вашу клиентскую часть TileEntity
this.readFromNBT(packet.getNbtCompound());
}
@Override
public void readFromNBT(NBTTagCompound compound) {
super.readFromNBT(compound);
if (compound.hasKey("item")) {
stack = new ItemStack(compound.getCompoundTag("item"));
} else {
stack = ItemStack.EMPTY;
}
}
@Override
public NBTTagCompound writeToNBT(NBTTagCompound compound) {
super.writeToNBT(compound);
if (!stack.isEmpty()) {
NBTTagCompound tagCompound = new NBTTagCompound();
stack.writeToNBT(tagCompound);
compound.setTag("item", tagCompound);
}
return compound;
}
}
Наконец у нас есть работающий TESR. Обратите внимание, что статическая модель здесь не отображается. Это обрабатывается автоматически. Нам нужно только сделать анимирующие рукояти и предмет:
PedestalTESR.java:
@SideOnly(Side.CLIENT)
public class PedestalTESR extends TileEntitySpecialRenderer<PedestalTileEntity> {
@Override
public void render(PedestalTileEntity te, double x, double y, double z, float partialTicks, int destroyStage, float alpha) {
GlStateManager.pushAttrib();
GlStateManager.pushMatrix();
//Перемести на расположение вашего TileEntity
GlStateManager.translate(x, y, z);
GlStateManager.disableRescaleNormal();
//Рендеринг вращающихся ручек
renderHandles(te);
//рендер предмета на пьедестале
renderItem(te);
GlStateManager.popMatrix();
GlStateManager.popAttrib();
}
private void renderHandles(PedestalTileEntity te) {
GlStateManager.pushMatrix();
GlStateManager.translate(.5, 0, .5);
long angle = (System.currentTimeMillis() / 10) % 360;
GlStateManager.rotate(angle, 0, 1, 0);
RenderHelper.disableStandardItemLighting();
this.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
if (Minecraft.isAmbientOcclusionEnabled()) {
GlStateManager.shadeModel(GL11.GL_SMOOTH);
} else {
GlStateManager.shadeModel(GL11.GL_FLAT);
}
World world = te.getWorld();
// Перевести обратно в локальный вид координат, так что бы мы могли провести здесь рендер
GlStateManager.translate(-te.getPos().getX(), -te.getPos().getY(), -te.getPos().getZ());
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder bufferBuilder = tessellator.getBuffer();
bufferBuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK);
IBlockState state = ModBlocks.pedestalBlock.getDefaultState().withProperty(PedestalBlock.IS_HANDLES, true);
BlockRendererDispatcher dispatcher = Minecraft.getMinecraft().getBlockRendererDispatcher();
IBakedModel model = dispatcher.getModelForState(state);
dispatcher.getBlockModelRenderer().renderModel(world, model, state, te.getPos(), bufferBuilder, true);
tessellator.draw();
RenderHelper.enableStandardItemLighting();
GlStateManager.popMatrix();
}
private void renderItem(PedestalTileEntity te) {
ItemStack stack = te.getStack();
if (!stack.isEmpty()) {
RenderHelper.enableStandardItemLighting();
GlStateManager.enableLighting();
GlStateManager.pushMatrix();
//Переместить в центр блока и на 0,9 пунктов выше
GlStateManager.translate(.5, .9, .5);
GlStateManager.scale(.4f, .4f, .4f);
Minecraft.getMinecraft().getRenderItem().renderItem(stack, ItemCameraTransforms.TransformType.NONE);
GlStateManager.popMatrix();
}
}
}
И, наконец, нам нужен .json файл состояния блока:
modelblock.json:
{
"forge_marker": 1,
"defaults": {
"custom": { "flip-v": true }
},
"variants": {
"is_handles=false": { "model": "modtut:pedestal.obj" },
"is_handles=true": { "model": "modtut:pedestalhandles.obj" },
"inventory": { "model": "modtut:pedestal.obj" }
}
}