Исправление ошибок и дополнения
Скрытый текст
I. Внутри mod_*.java вместо
достаточно прописать
ну и соответственно
1. Скачиваем 2. В mod_*.java прописываем строчку
Теперь при объявлении блока или предмета вместо id пишем соответственно *ID и так для каждого предмета/блока
(значения будут сохраняться в файле "/config/mod_*.cfg")
V.все имена методов modloader'а надо писать с маленькой буквы (ModLoader.addName(...), ModLoader.addRecipe(...), ModLoader.registerBlock(...) и прочие)
VI. Вместо world.multiplayerWorld идет world.isRemote
public mod_*()нужно писать
public void load()Вместо
public String Version()пишем
public String getVersion()II. Внутри файла блока вместо
public int idDropped(int i, int j)нужно писать
public int idDropped(int i, Random random, int j)III.
Необязательно создавать новый файл блока или предмета если в них не будет использоваться доп. код (просто декоративные блоки и предметы, используемые разве что в крафтах)Глава 2. Создание блока.
Глава 3. Создание предмета.
достаточно прописать
Public Static Block *=new Block(id, index, material)[paste][/paste]setHardness(0[paste][/paste]5F)[paste][/paste]setResistance(1[paste][/paste]0F)[paste][/paste]setBlockName("*")[paste][/paste][paste][/paste][paste][/paste][paste][/paste][paste][/paste];(при дропе будет возвращаться этот же блок)
ну и соответственно
Public Static Item *=new Item(id)[paste][/paste]setItemName("*")[paste][/paste]setMaxStackSize(max)[paste][/paste][paste][/paste][paste][/paste][paste][/paste];IV.
Способ рабочий, но с помощью ModLoader сохранение можно реализовать намного проще:Глава 16. Как создать специальный файл настроек мода.
1. Скачиваем 2. В mod_*.java прописываем строчку
@MLProp public static int *ID = 3000;, где 3000 - ID по умолчанию
Теперь при объявлении блока или предмета вместо id пишем соответственно *ID и так для каждого предмета/блока
(значения будут сохраняться в файле "/config/mod_*.cfg")
V.все имена методов modloader'а надо писать с маленькой буквы (ModLoader.addName(...), ModLoader.addRecipe(...), ModLoader.registerBlock(...) и прочие)
VI. Вместо world.multiplayerWorld идет world.isRemote
TileEntity, Gui или как создать печку
Скрытый текст
Для создания печи нам потребуется создать аж 4 файла, ну и зарегистрировать в mod_*.java. Пояснения по методам, не описанным в базовом учебнике, будут в самом коде.
Начнем пожалуй с файла Block*.java - файл, описывающий сам блок и взаимодейтвие с игрока с ним
Начнем пожалуй с файла Block*.java - файл, описывающий сам блок и взаимодейтвие с игрока с ним
package net[paste][/paste]minecraft[paste][/paste]src;import java[paste][/paste]util[paste][/paste]Random;public class BlockFurnace extends BlockContainer//BlockContainer означает привязанность tileEntity (о нем позже) к блоку[paste][/paste] При этом в блоке вовсе не обязательно хранятся какие либо предметы{ private Random furnaceRand; private final boolean isActive;//активна печка или нет[paste][/paste] Используется в методе, отвечающем за показ частиц private static boolean keepFurnaceInventory = false;//дропать ли предметы при сносе блока, protected Block*(int par1, boolean par2) { super(par1, Material[paste][/paste]rock); furnaceRand = new Random(); isActive = par2;// } public int idDropped(int par1, Random par2Random, int par3) { return Block[paste][/paste]*[paste][/paste]blockID; } public void onBlockAdded(World par1World, int par2, int par3, int par4)//вызывается при устаноке блока { super[paste][/paste]onBlockAdded(par1World, par2, par3, par4); setDefaultDirection(par1World, par2, par3, par4); } private void setDefaultDirection(World par1World, int par2, int par3, int par4)//задает ориентацию блока { if (par1World[paste][/paste]isRemote) { return; } int i = par1World[paste][/paste]getBlockId(par2, par3, par4 - 1); int j = par1World[paste][/paste]getBlockId(par2, par3, par4 + 1); int k = par1World[paste][/paste]getBlockId(par2 - 1, par3, par4); int l = par1World[paste][/paste]getBlockId(par2 + 1, par3, par4); byte byte0 = 3; if (Block[paste][/paste]opaqueCubeLookup[i] && !Block[paste][/paste]opaqueCubeLookup[j]) { byte0 = 3; } if (Block[paste][/paste]opaqueCubeLookup[j] && !Block[paste][/paste]opaqueCubeLookup[i]) { byte0 = 2; } if (Block[paste][/paste]opaqueCubeLookup[k] && !Block[paste][/paste]opaqueCubeLookup[l]) { byte0 = 5; } if (Block[paste][/paste]opaqueCubeLookup[l] && !Block[paste][/paste]opaqueCubeLookup[k]) { byte0 = 4; } par1World[paste][/paste]setBlockMetadataWithNotify(par2, par3, par4, byte0); } public int getBlockTexture(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)//задание текстур для отображения блока в инвентаре { if (par5 == 1) { return blockIndexInTexture + 17; } if (par5 == 0) { return blockIndexInTexture + 17; } int i = par1IBlockAccess[paste][/paste]getBlockMetadata(par2, par3, par4); if (par5 != i) { return blockIndexInTexture; } if (isActive) { return blockIndexInTexture + 16; } else { return blockIndexInTexture - 1; } } public void randomDisplayTick(World par1World, int par2, int par3, int par4, Random par5Random)//отвечает за показ частиц[paste][/paste] Вызывается рандомно { if (!isActive) { return; } int i = par1World[paste][/paste]getBlockMetadata(par2, par3, par4); float f = (float)par2 + 0[paste][/paste]5F; float f1 = (float)par3 + 0[paste][/paste]0F + (par5Random[paste][/paste]nextFloat() * 6F) / 16F; float f2 = (float)par4 + 0[paste][/paste]5F; float f3 = 0[paste][/paste]52F; float f4 = par5Random[paste][/paste]nextFloat() * 0[paste][/paste]6F - 0[paste][/paste]3F; if (i == 4) { par1World[paste][/paste]spawnParticle("smoke", f - f3, f1, f2 + f4, 0[paste][/paste]0D, 0[paste][/paste]0D, 0[paste][/paste]0D);//частицы дыма par1World[paste][/paste]spawnParticle("flame", f - f3, f1, f2 + f4, 0[paste][/paste]0D, 0[paste][/paste]0D, 0[paste][/paste]0D);//частицы огня } else if (i == 5) { par1World[paste][/paste]spawnParticle("smoke", f + f3, f1, f2 + f4, 0[paste][/paste]0D, 0[paste][/paste]0D, 0[paste][/paste]0D); par1World[paste][/paste]spawnParticle("flame", f + f3, f1, f2 + f4, 0[paste][/paste]0D, 0[paste][/paste]0D, 0[paste][/paste]0D); } else if (i == 2) { par1World[paste][/paste]spawnParticle("smoke", f + f4, f1, f2 - f3, 0[paste][/paste]0D, 0[paste][/paste]0D, 0[paste][/paste]0D); par1World[paste][/paste]spawnParticle("flame", f + f4, f1, f2 - f3, 0[paste][/paste]0D, 0[paste][/paste]0D, 0[paste][/paste]0D); } else if (i == 3) { par1World[paste][/paste]spawnParticle("smoke", f + f4, f1, f2 + f3, 0[paste][/paste]0D, 0[paste][/paste]0D, 0[paste][/paste]0D); par1World[paste][/paste]spawnParticle("flame", f + f4, f1, f2 + f3, 0[paste][/paste]0D, 0[paste][/paste]0D, 0[paste][/paste]0D); } } public int getBlockTextureFromSide(int par1) { if (par1 == 1) { return blockIndexInTexture + 17; } if (par1 == 0) { return blockIndexInTexture + 17; } if (par1 == 3) { return blockIndexInTexture - 1; } else { return blockIndexInTexture; } } public boolean blockActivated(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer)//вызывается при пкм по блоку { if (par1World[paste][/paste]isRemote) { return true; } TileEntity* tileentity* = (TileEntity*)par1World[paste][/paste]getBlockTileEntity(par2, par3, par4);//получение tileentity блока if (tileentityfurnace != null) { par5EntityPlayer[paste][/paste]displayGUIFurnace(tileentity*);//открытие GUI (интерфейса) } return true; } public static void updateFurnaceBlockState(boolean par0, World par1World, int par2, int par3, int par4)//обновляет блок при активации/диактивации { int i = par1World[paste][/paste]getBlockMetadata(par2, par3, par4); TileEntity tileentity = par1World[paste][/paste]getBlockTileEntity(par2, par3, par4); keepFurnaceInventory = true; if (par0) { par1World[paste][/paste]setBlockWithNotify(par2, par3, par4, Block[paste][/paste]stoneOvenActive[paste][/paste]blockID); } else { par1World[paste][/paste]setBlockWithNotify(par2, par3, par4, Block[paste][/paste]stoneOvenIdle[paste][/paste]blockID); } keepFurnaceInventory = false; par1World[paste][/paste]setBlockMetadataWithNotify(par2, par3, par4, i); if (tileentity != null) { tileentity[paste][/paste]validate(); par1World[paste][/paste]setBlockTileEntity(par2, par3, par4, tileentity); } } public TileEntity getBlockEntity()//создание самого tileentity { return new TileEntityFurnace(); } public void onBlockPlacedBy(World par1World, int par2, int par3, int par4, EntityLiving par5EntityLiving)//вызывается при установке блока для определения ориентации { int i = MathHelper[paste][/paste]floor_double((double)((par5EntityLiving[paste][/paste]rotationYaw * 4F) / 360F) + 0[paste][/paste]5D) & 3; if (i == 0) { par1World[paste][/paste]setBlockMetadataWithNotify(par2, par3, par4, 2); } if (i == 1) { par1World[paste][/paste]setBlockMetadataWithNotify(par2, par3, par4, 5); } if (i == 2) { par1World[paste][/paste]setBlockMetadataWithNotify(par2, par3, par4, 3); } if (i == 3) { par1World[paste][/paste]setBlockMetadataWithNotify(par2, par3, par4, 4); } } public void onBlockRemoval(World par1World, int par2, int par3, int par4)//вызывается при удалении блока { if (!keepFurnaceInventory) { TileEntity* tileentityf* = (TileEntity*)par1World[paste][/paste]getBlockTileEntity(par2, par3, par4); if (tileentity* != null) { label0: for (int i = 0; i < tileentity*[paste][/paste]getSizeInventory(); i++) { ItemStack itemstack = tileentity*[paste][/paste]getStackInSlot(i);//получение предмета в слоте под номером i if (itemstack == null) { continue; } float f = furnaceRand[paste][/paste]nextFloat() * 0[paste][/paste]8F + 0[paste][/paste]1F; float f1 = furnaceRand[paste][/paste]nextFloat() * 0[paste][/paste]8F + 0[paste][/paste]1F; float f2 = furnaceRand[paste][/paste]nextFloat() * 0[paste][/paste]8F + 0[paste][/paste]1F; do { if (itemstack[paste][/paste]stackSize <= 0) { continue label0; } int j = furnaceRand[paste][/paste]nextInt(21) + 10; if (j > itemstack[paste][/paste]stackSize) { j = itemstack[paste][/paste]stackSize; } itemstack[paste][/paste]stackSize -= j; EntityItem entityitem = new EntityItem(par1World, (float)par2 + f, (float)par3 + f1, (float)par4 + f2, new ItemStack(itemstack[paste][/paste]itemID, j, itemstack[paste][/paste]getItemDamage())); float f3 = 0[paste][/paste]05F; entityitem[paste][/paste]motionX = (float)furnaceRand[paste][/paste]nextGaussian() * f3; entityitem[paste][/paste]motionY = (float)furnaceRand[paste][/paste]nextGaussian() * f3 + 0[paste][/paste]2F; entityitem[paste][/paste]motionZ = (float)furnaceRand[paste][/paste]nextGaussian() * f3; par1World[paste][/paste]spawnEntityInWorld(entityitem);//дроп предмета в мир } while (true); } } } super[paste][/paste]onBlockRemoval(par1World, par2, par3, par4); }}Так, с блоком разобрались, теперь пора переходить к главному файлу при создании любых рабочих блоков - TileEntity*.java
package net[paste][/paste]minecraft[paste][/paste]src;public class TileEntityFurnace extends TileEntity implements IInventory{ private ItemStack furnaceItemStacks[];//массив для хранения предметов в блоке public int furnaceBurnTime;//оставшееся время горения топлива public int currentItemBurnTime;// вообще хз зачем :) public int furnaceCookTime; //время готовки текущего объекта public TileEntityFurnace() { furnaceItemStacks = new ItemStack[3]; furnaceBurnTime = 0; currentItemBurnTime = 0; furnaceCookTime = 0; } public int getSizeInventory()//возвращает количество слотов { return furnaceItemStacks[paste][/paste]length; } public ItemStack getStackInSlot(int par1)//возвращает стак в слоте par1 { return furnaceItemStacks[par1]; } public ItemStack decrStackSize(int par1, int par2) //уменьшает размер стака в слоте par1 на значение par2, возвращ новый стак { if (furnaceItemStacks[par1] != null) { if (furnaceItemStacks[par1][paste][/paste]stackSize <= par2) { ItemStack itemstack = furnaceItemStacks[par1]; furnaceItemStacks[par1] = null; return itemstack; } ItemStack itemstack1 = furnaceItemStacks[par1][paste][/paste]splitStack(par2); if (furnaceItemStacks[par1][paste][/paste]stackSize == 0) { furnaceItemStacks[par1] = null; } return itemstack1; } else { return null; } } public ItemStack getStackInSlotOnClosing(int par1)//дропает предмет из слота par1 при закрытии gui (что этот кусок вообще в печке забыл?? :)) { if (furnaceItemStacks[par1] != null) { ItemStack itemstack = furnaceItemStacks[par1]; furnaceItemStacks[par1] = null; return itemstack; } else { return null; } } public void setInventorySlotContents(int par1, ItemStack par2ItemStack)//задает предмет выбранному слоту { furnaceItemStacks[par1] = par2ItemStack; if (par2ItemStack != null && par2ItemStack[paste][/paste]stackSize > getInventoryStackLimit()) { par2ItemStack[paste][/paste]stackSize = getInventoryStackLimit(); } } public String getInvName()//возвращает имя блока для отображения в gui (регистрируется по всей видимости через modloader, пока не смотрел как) { return "container[paste][/paste]*"; } public void readFromNBT(NBTTagCompound par1NBTTagCompound)//загрузка из файла инвенаря и проч при загрузке мира { super[paste][/paste]readFromNBT(par1NBTTagCompound); NBTTagList nbttaglist = par1NBTTagCompound[paste][/paste]getTagList("Items"); furnaceItemStacks = new ItemStack[getSizeInventory()]; for (int i = 0; i < nbttaglist[paste][/paste]tagCount(); i++) { NBTTagCompound nbttagcompound = (NBTTagCompound)nbttaglist[paste][/paste]tagAt(i); byte byte0 = nbttagcompound[paste][/paste]getByte("Slot"); if (byte0 >= 0 && byte0 < furnaceItemStacks[paste][/paste]length) { furnaceItemStacks[byte0] = ItemStack[paste][/paste]loadItemStackFromNBT(nbttagcompound); } } furnaceBurnTime = par1NBTTagCompound[paste][/paste]getShort("BurnTime"); furnaceCookTime = par1NBTTagCompound[paste][/paste]getShort("CookTime"); currentItemBurnTime = getItemBurnTime(furnaceItemStacks[1]); } public void writeToNBT(NBTTagCompound par1NBTTagCompound)//сохранение предметов и проч при выходе { super[paste][/paste]writeToNBT(par1NBTTagCompound); par1NBTTagCompound[paste][/paste]setShort("BurnTime", (short)furnaceBurnTime); par1NBTTagCompound[paste][/paste]setShort("CookTime", (short)furnaceCookTime); NBTTagList nbttaglist = new NBTTagList(); for (int i = 0; i < furnaceItemStacks[paste][/paste]length; i++) { if (furnaceItemStacks[i] != null) { NBTTagCompound nbttagcompound = new NBTTagCompound(); nbttagcompound[paste][/paste]setByte("Slot", (byte)i); furnaceItemStacks[i][paste][/paste]writeToNBT(nbttagcompound); nbttaglist[paste][/paste]appendTag(nbttagcompound); } } par1NBTTagCompound[paste][/paste]setTag("Items", nbttaglist); } public int getInventoryStackLimit()// максимальный размер стака в печи (больше 64 ставить не имеет смысла, у каждого предмета есть так же свое собственное ограничение) { return 64; } public int getCookProgressScaled(int par1)//возвращает процесс готовки, нужно для GUI { return (furnaceCookTime * par1) / 200; } public int getBurnTimeRemainingScaled(int par1)//возвращает оставшееся время горения топлива, опять же для gui { if (currentItemBurnTime == 0) { currentItemBurnTime = 200; } return (furnaceBurnTime * par1) / currentItemBurnTime; } public boolean isBurning()//возвращает горит ли печь { return furnaceBurnTime > 0; } public void updateEntity()//обновление данных, вызывается 10 раз в сек { boolean flag = furnaceBurnTime > 0; boolean flag1 = false; if (furnaceBurnTime > 0) { furnaceBurnTime--; } if (!worldObj[paste][/paste]isRemote) { if (furnaceBurnTime == 0 && canSmelt()) { currentItemBurnTime = furnaceBurnTime = getItemBurnTime(furnaceItemStacks[1]); if (furnaceBurnTime > 0) { flag1 = true; if (furnaceItemStacks[1] != null) { if (furnaceItemStacks[1][paste][/paste]getItem()[paste][/paste]func_46056_k()) { furnaceItemStacks[1] = new ItemStack(furnaceItemStacks[1][paste][/paste]getItem()[paste][/paste]setFull3D()); } else { furnaceItemStacks[1][paste][/paste]stackSize--; } if (furnaceItemStacks[1][paste][/paste]stackSize == 0) { furnaceItemStacks[1] = null; } } } } if (isBurning() && canSmelt()) { furnaceCookTime++; if (furnaceCookTime == 200) { furnaceCookTime = 0; smeltItem(); flag1 = true; } } else { furnaceCookTime = 0; } if (flag != (furnaceBurnTime > 0)) { flag1 = true; BlockFurnace[paste][/paste]updateFurnaceBlockState(furnaceBurnTime > 0, worldObj, xCoord, yCoord, zCoord); } } if (flag1) { onInventoryChanged(); } } private boolean canSmelt()//проверяет, можно ли приготовить предмет { if (furnaceItemStacks[0] == null) { return false; } ItemStack itemstack = FurnaceRecipes[paste][/paste]smelting()[paste][/paste]getSmeltingResult(furnaceItemStacks[0][paste][/paste]getItem()[paste][/paste]shiftedIndex); if (itemstack == null) { return false; } if (furnaceItemStacks[2] == null) { return true; } if (!furnaceItemStacks[2][paste][/paste]isItemEqual(itemstack)) { return false; } if (furnaceItemStacks[2][paste][/paste]stackSize < getInventoryStackLimit() && furnaceItemStacks[2][paste][/paste]stackSize < furnaceItemStacks[2][paste][/paste]getMaxStackSize()) { return true; } return furnaceItemStacks[2][paste][/paste]stackSize < itemstack[paste][/paste]getMaxStackSize(); } public void smeltItem()//вызывается для приготовления предмета { if (!canSmelt()) { return; } ItemStack itemstack = FurnaceRecipes[paste][/paste]smelting()[paste][/paste]getSmeltingResult(furnaceItemStacks[0][paste][/paste]getItem()[paste][/paste]shiftedIndex); if (furnaceItemStacks[2] == null) { furnaceItemStacks[2] = itemstack[paste][/paste]copy(); } else if (furnaceItemStacks[2][paste][/paste]itemID == itemstack[paste][/paste]itemID) { furnaceItemStacks[2][paste][/paste]stackSize += itemstack[paste][/paste]stackSize; } if (furnaceItemStacks[0][paste][/paste]getItem()[paste][/paste]func_46056_k()) { furnaceItemStacks[0] = new ItemStack(furnaceItemStacks[0][paste][/paste]getItem()[paste][/paste]setFull3D()); } else { furnaceItemStacks[0][paste][/paste]stackSize--; } if (furnaceItemStacks[0][paste][/paste]stackSize <= 0) { furnaceItemStacks[0] = null; } } private int getItemBurnTime(ItemStack par1ItemStack)//возвращает время горения ланного материала { if (par1ItemStack == null) { return 0; } int i = par1ItemStack[paste][/paste]getItem()[paste][/paste]shiftedIndex; if (i < 256 && Block[paste][/paste]blocksList[i][paste][/paste]blockMaterial == Material[paste][/paste]wood) { return 300; } if (i == Item[paste][/paste]stick[paste][/paste]shiftedIndex) { return 100; } if (i == Item[paste][/paste]coal[paste][/paste]shiftedIndex) { return 1600; } if (i == Item[paste][/paste]bucketLava[paste][/paste]shiftedIndex) { return 20000; } if (i == Block[paste][/paste]sapling[paste][/paste]blockID) { return 100; } if (i == Item[paste][/paste]blazeRod[paste][/paste]shiftedIndex) { return 2400; } else { return ModLoader[paste][/paste]addAllFuel(par1ItemStack[paste][/paste]itemID, par1ItemStack[paste][/paste]getItemDamage()); } } public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer)//проверяет расстояние, сможет ли игрок его использовать { if (worldObj[paste][/paste]getBlockTileEntity(xCoord, yCoord, zCoord) != this) { return false; } return par1EntityPlayer[paste][/paste]getDistanceSq((double)xCoord + 0[paste][/paste]5D, (double)yCoord + 0[paste][/paste]5D, (double)zCoord + 0[paste][/paste]5D) <= 64D; } public void openChest() { } public void closeChest() { }}пока не закончил, позже допишу
Создание модели для блока
Скрытый текст
Ну что ж, начнем.
Для начала в файл своего блока добавим такие строчки:
в конструктор класса блока добавляем следующую строчку
Примеры:
2. Рендеринг
В тот же файл блока добавляем строчки
3. Создание модели с
[*]) и с помощью параметров меняем размер, положение и тд.
[*]Создаем нужное количество фигур, не забывая смещать текстуру (Texture Offset слева и изображение самой текстуры справа), чтобы текструры от разных кубиков не накладывались друг на друга. Если нехватает места, тыкаем Edit/Edit Project и меняем Texture Width/Height на бОльшие
[*]Когда закончили с созданием, нажимаем File/Export as/Texturemap, затем открываем получившуюся картинку фотошопом, пэинтом или чем там удобно и рисуем нужные текстуры (что получится можно посмотреть, если нажать Edit/Load Texture или просто Load Texture и выбрать файл)
[*]Когда закончили с тектурой нажимаем File/Export As/Java (использование TMT я не рассматриваю) и начинаем писать
[*]Открываем Java файл, меняем названия public class Model* и public Model* если нужно и добавляем код:
[*]Создаем файл TileEntity*Renderer.java и прописываем в него такой код
*картинки не мои, взяты Только начал писать, кто какие ошибки в оригинальной статье нашел пишите в комменты. Так же планирую разжевать создание печки и добавить создание модели для блока с помощью techne (я знаю что эти статьи есть на русском, но собрать их в одно место да и расписать поподробнее думаю стоит)
Для начала в файл своего блока добавим такие строчки:
public boolean isOpaqueCube() { return false; }См. "Нормальное стекло" в setBlockBounds
в конструктор класса блока добавляем следующую строчку
setBlockBounds(x1, y1, z1, x2, y2, z2);Эта функция задает размер блока по 2м координатам. Размер влияет как на внешний вид, так и на физическое взаимодействие игрока с блоком
Примеры:
2. Рендеринг
В тот же файл блока добавляем строчки
public boolean renderAsNormalBlock() { return false; } public boolean isOpaqueCube() { return false; } public int getRenderType() { return N; }Где N - номер рендера. Для каждого блока, имеюoего нестанадартную форму (трава, яйцо дракона, факел и тд) используется свой номер. Вот эти номера*:
3. Создание модели с
[*]) и с помощью параметров меняем размер, положение и тд.
[*]Создаем нужное количество фигур, не забывая смещать текстуру (Texture Offset слева и изображение самой текстуры справа), чтобы текструры от разных кубиков не накладывались друг на друга. Если нехватает места, тыкаем Edit/Edit Project и меняем Texture Width/Height на бОльшие
[*]Когда закончили с созданием, нажимаем File/Export as/Texturemap, затем открываем получившуюся картинку фотошопом, пэинтом или чем там удобно и рисуем нужные текстуры (что получится можно посмотреть, если нажать Edit/Load Texture или просто Load Texture и выбрать файл)
[*]Когда закончили с тектурой нажимаем File/Export As/Java (использование TMT я не рассматриваю) и начинаем писать
[*]Открываем Java файл, меняем названия public class Model* и public Model* если нужно и добавляем код:
public void renderModel(float f1) { Shape1[paste][/paste]render(f1); Shape2[paste][/paste]render(f1); //[paste][/paste][paste][/paste][paste][/paste][paste][/paste][paste][/paste][paste][/paste] }и так для каждого добавленного кубика
[*]Создаем файл TileEntity*Renderer.java и прописываем в него такой код
package net[paste][/paste]minecraft[paste][/paste]src;import org[paste][/paste]lwjgl[paste][/paste]opengl[paste][/paste]GL11;public class TileEntity*lRenderer extends TileEntitySpecialRenderer{ private Model* aModel; public TileEntity*Renderer() { aModel = new Model*(); } public void render*ModelAt(TileEntity* tileentity1, double d, double d1, double d2, float f) { GL11[paste][/paste]glPushMatrix(); GL11[paste][/paste]glTranslatef((float)d + 0[paste][/paste]5F, (float)d1 - 0[paste][/paste]5F, (float)d2 + 0[paste][/paste]5F); //задает координаты центра модели (d-x, d1-y, d2-z) bindTextureByName("/textures/*[paste][/paste]png"); //адрес текстуры GL11[paste][/paste]glPushMatrix(); aModel[paste][/paste]renderModel(0[paste][/paste]0625F); GL11[paste][/paste]glPopMatrix(); GL11[paste][/paste]glPopMatrix(); } public void renderTileEntityAt(TileEntity tileentity, double d, double d1, double d2, float f) { render*ModelAt((TileEntity*)tileentity, d, d1, d2, f); }}[*]Созаем файл TileEntity*.java и добавляем такие строчки (если файл есть, то ничего не делаем)
package net[paste][/paste]minecraft[paste][/paste]src;public class TileEntity* extends TileEntity{ public TileEntity*() { }}[*]Теперь переходим к Block*.java. Добавляем такие строчки
public boolean isOpaqueCube() { return false; } public boolean renderAsNormalBlock() { return false; } public int getRenderType() { return -1; }[*]Конец уже близко. Теперь открываем mod_*.java и добавляем в load()
TileEntity*Renderer tileent1 = new TileEntity*Renderer(); ModLoader[paste][/paste]RegisterTileEntity(TileEntity*[paste][/paste]class, "*", tileent1); //"*" название блока в [paste][/paste]setBlockName("*");[/list]Ну вот практически и все. Т.к иконку в инвентаре для блоков с использованием модели и рендера задать нельзя (я пока особо не копался, может потом найду как), то создадим предмет, при клике которым поставися блок. Вот его код для Item*.java
package net[paste][/paste]minecraft[paste][/paste]src;import java[paste][/paste]util[paste][/paste]Random;public class Item* extends Item{ private int blockID; public Item*(int i, int j) { super(i); blockID=j; } public boolean onItemUse(ItemStack itemstack, EntityPlayer entityplayer, World world, int i, int j, int k, int l) { if (l == 0) { j--; } if (l == 1) { j++; } if (l == 2) { k--; } if (l == 3) { k++; } if (l == 4) { i--; } if (l == 5) { i++; } world[paste][/paste]setBlockWithNotify(i, j, k, blockID); itemstack[paste][/paste]stackSize--; return true; }}Ну и регистрируем в mod_*.java
public Item * = new Item* (itemID, blockID)[paste][/paste]setItemName("*");Ну вот вроде бы и все.
*картинки не мои, взяты Только начал писать, кто какие ошибки в оригинальной статье нашел пишите в комменты. Так же планирую разжевать создание печки и добавить создание модели для блока с помощью techne (я знаю что эти статьи есть на русском, но собрать их в одно место да и расписать поподробнее думаю стоит)