Пишем моды для minecraft — статья 2

Пишем моды для minecraft — статья 2, image #1

И, здравствуйте, уважаемые!

С вами ZigTheHedge и сегодня мы научимся структурировать свой мод, и сделаем простейший предмет.

Учитывая, что я пишу статьи, как отдельный учебник, крайне советую прочесть первую статью. Так как сегодня мы будем использовать названия и договоренности из первой статьи. Напоминаю: наш мод называется tutorial, и подписан он каноническим именем ru.zigthehedge. И, держа эту информацию в голове, давайте начнем.

Откроем IDEA и загрузим наш проект.
Слева у нас будет панель, в которой открыт наш проект. И там мы видим все файлы, которые находятся в каталоге с проектом, а также свернутый список "External Libraries" (внешние библиотеки). Если открыть список "External Libraries" мы увидим все библиотеки, которые привязаны к проекту магическими силами Gradle. Из всего списка нас может заинтересовать библиотека forgeSrc-1.12.2-версия_forge.jar - это сам forge, и там же находятся декомпилированные исходники майнкрафта! Да-да! Именно открыв эту библиотеку, мы можем посмотреть, что из себя представляет майнкрафт изнутри!

Пишем моды для minecraft — статья 2, image #2

Зачем я вам про это рассказал? Дело в том, что ванилла - это один из лучших источников, к которым следует обращаться, если вы зашли в тупик и не знаете, как сделать какую-то механику. Просто вспомните, какой блок, предмет, моб, или механизм в ванильном майнкрафте имеет функционал схожий с тем, которого пытаетесь добиться вы, и найдите его исходники в ванилле. Зачастую, там можно сломать шею, но со временем вы привыкнете.

Возвращаемся к нашему основному проекту, и поговорим о необходимых для любого мода требованиях. Во-первых, любой мод имеет главный класс. Этот класс загружается forge'ем при старте майнкрафта с вашим модом, и уже этот класс определяет то, как будет работать ваш мод. Называть класс можно, разумеется, как угодно, но, для собственного удобства, а так же для удобства тех, кто будет читать ваш код, главный класс принято именовать так же, как у вас называется сам мод и располагать его в корне проекта.

Кстати: классы в Java, по соглашению, называются в CamelCase с заглавной первой буквой.

Выберем наш package (модное название каталогов в Java), нажмем правый клик и выберем "New -> Java Class".

Пишем моды для minecraft — статья 2, image #3

Зададим имя нашему главному классу. Впишем в поле "Name" значение: "Tutorial". Нажем "Ok".
У нас создался класс, в котором, собственно, ничего нет. И вот здесь мы, наконец, начинаем программировать. Правда, сначала нам надо создать "скелет" нашего мода. Создадим в нашем новом классе 3 свойства:

...
public class Tutorial {
public static final String MODID = "tutorial";
public static final String NAME = "Tutorial Mod";
public static final String VERSION = "1.0";
}

Кстати: Тремя точками я буду обозначать места в файлах, где уже может быть что-то написано, что не важно конкретно «сейчас». Это общепринятая практика, так что — привыкайте.

Что мы здесь написали? Мы создали три публичных статических неизменяемых переменных типа «String». Рассказать вам, что означают эти прилагательные? Окей:

  • public — публичный доступ. Означает, что к этому элементу можно обращаться из любых мест кода (изо всех классов и их методов)
  • static — статический признак. Означает, что в памяти для любого количества экземпляров объекта выделяется одна и та же ячейка. Соответственно (в случае с переменной) ее значение будет всегда одним и тем же для всех экземпляров классов. Это экономит память и позволяет обращаться к объекту без создания экземпляра класса.
  • final — неизменяемое значение. Значение этого объекта нельзя изменить после инициализации.

Ну а String - это стандартный класс java для описание строк.

Теперь про сами переменные. Они содержат в себе техническое название мода (MODID), человеческое название мода (NAME) и версию мода (VERSION). Удостоверьтесь, чтобы значения MODID и VERSION совпадали со значениями в build.gradle!

Далее, нам необходимо сказать forge'у, что этот вот класс является главным для нашего мода. Для этого служит аннотация из forge под названием @Mod. Давайте добавим ее непосредственно перед объявлением нашего главного класса:

...
@Mod(modid = Tutorial.MODID, name = Tutorial.NAME, version = Tutorial.VERSION)
public class Tutorial {
...

Ииии… Вау! Куча красных строк! Куча ошибок! Аааа! Зиг все сломал!

Вот здесь начинаются отличия в средах разработки. Дело в том, что эта аннотация не из java. Она предоставляется forge'ем. Соответственно, нам надо сказать компилятору, где ее искать. Для этого в java служат конструкции вида «import package».

Если вы разрабатываете в IDEA, просто поставьте курсор на @Mod, который подсвечен красным, и нажмите Alt+Enter.

Пишем моды для minecraft — статья 2, image #4

Из меню выберите «Import class». И, магическими силами IDEA, в ваш класс добавятся все необходимые import'ы (в нашем случае, только один). В итоге, наш класс будет выглядеть вот так (без троеточий):

package ru.zigthehedge.tutorial;

import net.minecraftforge.fml.common.Mod;

@Mod(modid = Tutorial.MODID, name = Tutorial.NAME, version = Tutorial.VERSION)
public class Tutorial {
public static final String MODID = "tutorial";
public static final String NAME = "Tutorial Mod";
public static final String VERSION = "1.0";
}

Кстати: если у вас по нажатию Alt+Enter нет пункта «Import class», значит вы не подключили библиотеки forge и дальше разрабатывать мод не получится. Вернитесь к первой статье и проделайте все процедуры более внимательно!

Теперь нам надо создать еще два класса. Это так называемые proxy-классы. Сначала я расскажу, зачем они нужны, а потом приступим к созданию.

Дело в том, что ваш мод будет выполняться одновременно в двух контекстах: на стороне клиента и на стороне сервера (даже если вы просто запускаете у себя в майнкрафте одиночную игру, у вас все равно запускается встроенный в майнкрафт сервер). А эти контексты различаются набором доступных классов. Например, серверная часть ничего не знает о выводе изображений на экран (у него просто нет экрана!). Для этого, чтобы не писать два мода, как это было во времена 1.2.5, был придуман способ логически разделять стороны, и мод в итоге получается универсальным и для клиента и для сервера.

Разделение будет присутствовать во многих частях мода, но начинается оно как раз с прокси-классов. Давайте их создадим: проведите аналогичные действия по созданию класса, как вы это делали при создании главного класса, и создайте классы CommonProxy и ClientProxy.

Пишем моды для minecraft — статья 2, image #5

Откройте класс ClientProxy и в строке его объявления допишите «extends CommonProxy».

package ru.zigthehedge.tutorial;

public class ClientProxy extends CommonProxy {
}

Кстати: Такой подход хоть и рекомендуется самим forge, но является немного нелогичным. В основе разделения должны быть классы ServerProxy (в котором будет код, который выполняется исключительно на стороне сервера) и ClientProxy (код которого выполняется исключительно на стороне клиента). Мы же создаем класс, общий для клиента и сервера (CommonProxy) и отдельный класс для клиента ClientProxy, который будет включать в себя и код из CommonProxy.

Теперь нам надо сказать нашему моду, что у него есть прокси. Делается это в главном классе при помощи следующей аннотации:

...
public static final String VERSION = "1.0";
@SidedProxy(clientSide = "ru.zigthehedge.tutorial.ClientProxy", serverSide = "ru.zigthehedge.tutorial.CommonProxy")
public static CommonProxy proxy;
}

Не забывайте про Alt+Enter, чтобы добавить недостающие import'ы при использовании @SidedProxy.

Значение параметров clientSide и serverSide в аннотации указывают на полный путь до прокси-классов клиента и сервера. Создание статического экземпляра класса CommonProxy непосредственно после аннотации, дает понять компилятору, что в этом свойстве будет находиться ссылка на ClientProxy, если мод запускается на стороне клиента, и на CommonProxy, если мод запускается на стороне сервера.

Кстати: можно создать отдельный ServerProxy класс, который будет наследовать CommonProxy, чтобы он использовался на стороне сервера, но, на моей практике, ни разу не возникало такой необходимости, так как я не сталкивался с методами, которые существуют только на стороне сервера.

Теперь давайте развяжем стороны для трех главных событий forge. Но сначала пара слов о «событиях»:

Почти каждое действие в майнкрафте дублируется соответствующим событием. Событие — это механика майнкрафта, которое позволяет коду «подписываться» на действия. То-есть, например, игрок теряет здоровье, когда падает с высоты. Значит, есть часть кода, которая подписана на событие "игрок упал", проверяет высоту, с которой игрок упал и, если высота больше безопасной, и у игрока нет зачарования на Feather Falling, отнимает у игрока сердечки. На событиях завязана вся логика майнкрафта, и это чертовски удобно! Главное, втянутся в саму суть событий.

Итак, события. Есть три события, которые forge отправляет всем подписаным модам при загрузке майнкрафта. Это FMLPreInitializationEvent, FMLInitializationEvent и FMLPostInitializationEvent. Если вы когда-нибудь следили за полоской загрузки модов в сборке, вы видели там надписи «Pre-init Phase», «Init Phase» и «Post-init Phase». Это как раз и есть действия, которые инициируют вышеобозначенные события. Ответом на эти события, моды регистрируют свои внутренние механики, задают экземпляры классов с подпиской на другие события, регистрируют пользовательские интерфейсы блоков, регистрируют сетевые пакеты и прочее служебное добро, которое будет необходимо для работы мода. Для начала, давайте создадим в наших прокси-классах методы, которые будут вызываться в ответ на полученное событие. В классе CommonProxy создадим методы:

...
public class CommonProxy {
    public void preInit(FMLPreInitializationEvent e)
{
    }
public void init(FMLInitializationEvent e)
{
    }
public void postInit(FMLPostInitializationEvent e)
{
    }
}

Не забываем про импорт соответствующих модулей (Alt + Enter).

Обратите внимание: совершенно неважно, как вы назовете методы. Главное — это типы их входных параметров. Я назвал методы по типам фаз, на события которых мы подписываемся.

Теперь перегрузим эти же методы в ClientProxy. Нужно это для того, чтобы клиентская сторона мода и серверная сторона мода по-разному реагировали на эти события. А, учитывая то, что наш ClientProxy наследует методы CommonProxy, мы можем открыть наш ClientProxy и воспользоваться удобным механизмом для перегрузок нашей IDEA. Поставим курсор в «тело» класса и нажмем Ctrl+O.

Пишем моды для minecraft — статья 2, image #6

IDEA нам показала список методов, которые можно перегрузить. Нас интересуют три: preInit, init и postInit. Ведь именно так мы назвали методы в нашем CommonProxy. Выбираем их все и нажимаем «Ok». Наш класс клиентского прокси преобразился автоматически в нечто подобное:

package ru.zigthehedge.tutorial;

import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;

public class ClientProxy extends CommonProxy {
@Override
public void preInit(FMLPreInitializationEvent e) {
super.preInit(e);
}
    @Override
public void init(FMLInitializationEvent e) {
super.init(e);
}
    @Override
public void postInit(FMLPostInitializationEvent e) {
super.postInit(e);
}
}

Кстати: аннотация @Override скорее избавляет нас от невнимательности, чем несет какое-то более важное значение. Компилятор ругнется на ошибку, если наш метод, который аннотирован как @Override по факту ничего не перегружает. Это помогает избежать ошибок, если вы перегружаете методы вручную.

Теперь давайте подпишем наш мод на получение этих событий. Делается это в главном классе мода:

...
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent e)
{
proxy.preInit(e);
}
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
proxy.init(event);
}
@Mod.EventHandler
public void postInit(FMLPostInitializationEvent e)
{
proxy.postInit(e);
}
...

Аннотация @Mod.EventHandler используется для того, чтобы рассказать forge о том, что именно последующий метод класса надо вызвать, когда возникает событие. Опять же - названия методов не важны, важны типы входящих параметров.

В обработчиках событий (тела наших новых методов) мы вызываем соответствующие методы из прокси-классов. Forge автоматически вызовет методы из ClientProxy, если мод работает на стороне клиента, и из CommonProxy, если на стороне сервера.

Ну вот. Костяк нашего мода мы сделали. Теперь можно приступить к созданию предмета.

Кстати: когда я только начинал учиться разрабатывать моды, и сам читал руководства, мне всегда было непонятно - почему сначала рассматривают предмет, а не блок? Очень скоро ответ на этот вопрос становится очевидным ;)

Итак. Давайте создадим класс для нашего предмета.

Кстати: Если мы создаем предмет, который ничего не делает и использоваться будет исключительно как компонент в рецептах, мы можем не создавать свой класс, а воспользоваться ванильным, но, я сразу хочу, чтобы вы создали свой класс, чтобы вы уже понимали, как работать с различными механиками предметов, когда это вам действительно понадобится.

Назовем класс «техническим именем» нашего предмета. Допустим, мы хотим создать ключ от замка! Неожиданно, правда? Имя класса будет в представлять собой что-то вроде Key. Но! Не спешите тыкать по проекту. В рамках этого туториала, мы создадим всего один предмет, но в вашем реальном моде количество разных предметов может исчисляться десятками, а то и сотнями! Поэтому, давайте для наших предметов создадим свой package (то-есть, подкаталог).

Пишем моды для minecraft — статья 2, image #7

В качестве имени нашего нового пакета, используем слово «items». Что весьма логично.

Кстати: пакеты принято называть в snake_case.

В получившемся пакете уже можно создать наш класс для предмета.

package ru.zigthehedge.tutorial.items;
public class Key {
}

Далее, мы хотим создать предмет, а это уже готовый ванильный класс. Поэтому, чтобы не переписывать всю внутреннюю логику работы с предметом, имеет смысл унаследовать оригинальный класс и перегрузить те методы, которые необходимы именно для нашего предмета. Добавляем к объявлению класса «extends Item»:

...
import net.minecraft.item.Item;

public class Key extends Item {

}

Теперь нам необходимо как-то отличать наш предмет от остальных. В Forge для этого используется механизм «регистров» (registers) (не пугайтесь, это не те регистры, которые находятся внутри процессора, это скорее коллекция всех предметов, блоков, измерений, жидкостей и так далее). И, чтобы отличать предметы друг от друга внутри этих регистров, используются «регистровые имена» (register names). Как правило, они для удобства совпадают с названием классов, однако, буквы должны всегда быть в нижнем регистре и из спец-символов разрешено только «нижнее подчеркивание» (то-есть, снова snake_case).

Чтобы нам задать регистровое имя для нашего предмета, создадим конструктор нашего класса и напишем там необходимый код:

...
public class Key extends Item {
    public Key ()
{
setRegistryName("key");
setUnlocalizedName(Tutorial.MODID + ".key");
setCreativeTab(CreativeTabs.MISC);
}

Кстати: Конструктор - это метод класса, не имеющий никакого типа возвращаемого значения, и вызываемый автоматически при создании экземпляра класса.

Метод setRegistryName задает регистровое имя для нашего предмета, а метод setUnlocalizedName — регистровое имя для локализации названия предмета. В майнкрафте именно таким образом реализован механизм локализации. То-есть, именно это «нелокализованное» имя будет искаться в языковых файлах, когда клиенту нужно будет вывести название предмета на экран. В «нелокализованном имени» так же указывается техническое имя нашего мода и, через точку, регистровое имя предмета.

И, чтобы мы с вами смогли отыскать наш предмет в игре, я добавил вызов метода setCreativeTab(). Он добавляет наш предмет в одну из вкладок креативного инвентаря. В моем случае в Misc. Именно это означает параметр вызова этого метода CreativeTabs.MISC.

Мы создали класс предмета. Теперь его необходимо зарегистрировать и создать экземпляр, который и будет использоваться в дальшейшем в моде.

Обычно, все экземпляры предметов и блоков мода хранятся в отдельных классах. Давайте создадим такой класс для предметов. Назвать класс можно как угодно, но я предпочитаю называть его именем мода + словом Items. Таким образом, мы сможем отличить наш класс от классов других модов и ваниллы. Так что, создадим класс TutorialItems. Создавать его будем в пакете items.

package ru.zigthehedge.tutorial.items;
public class TutorialItems {
}

Внутри класса создадим указатель на экземпляр нашего нового предмета с аннотацией @GameObject. Это аннотация от forge, которая позволяет ему указать, что в свойстве, следующим за аннотацией, содержится экземпляр класса с игровым объектом. Почему я сказал «указатель»? Потому что мы по факту не создаем экземпляр, а только назначаем переменную. Само создание будет немного потом.

...
public class TutorialItems {
    @GameRegistry.ObjectHolder(Tutorial.MODID + ":key")
public static Key key;
}

В качестве аргумента для аннотации используется конструкция: техническое имя мода, двоеточие, регистровое имя объекта. В нашем случае — предмета.

За аннотацией следует создание публичного статичного свойства с типом нашего класса.

Кстати: Имена предметам и блокам обычно дают в UPPERCASE, однако, я предпочитаю camelCase (то-есть, такой же CamelCase, как обычно, но со строчной буквой в начале), а UPPERCASE храню для final-переменных.

Чтож, мы создали экземпляр нашего предмета, однако теперь нам нужно зарегистрировать (буквально — поместить в forge-регистр) и присвоить ему модель. Давайте начнем со второго. В нашем TutorialItems классе создадим еще один метод:

...
public static Key key;
@SideOnly(Side.CLIENT)
public static void initModels() {
}
...

Методу предшествует аннотация @SideOnly. Эта аннотация включает метод только для определенной стороны. И ее удобно использовать, чтобы не включать исполнение в серверную часть мода. Для этого мы указываем в качестве аргумента Side.CLIENT.

Сам метод является статическим и будет вызываться из наших прокси для того, чтобы присвоить предметам модель.

Однако, нам необходимо еще и сам метод создания модели описать. Перейдем в класс с нашим предметом (то-бишь, в Key) и добавим метод для регистрации модели предмета:

...
@SideOnly(Side.CLIENT)
public void initModel() {
ModelLoader.setCustomModelResourceLocation(this, 0, new ModelResourceLocation(getRegistryName(), "inventory"));
}
...

В этом методе мы вызываем класс Forge, который отвечает за модели всего в игре. В том числе и за модели предметов. Далее, при помощи статического метода setCustomModelResourceLocation, мы указываем, что текущий предмет (this) в варианте с индексом ноль (0) должен искать модель по параметрам текущего регистрового имени (getRegistryName() любезно нам его вернет) в ванианте для «inventory» (то-есть, для инвентаря).

Теперь вызов этого нашего метода необходимо поместить в метод initModels нашего класса с предметами TutorialItems.

...
public static void initModels() {
key.initModel();
...

Обратите внимание: наш метод не является статическим. И для его вызова необходим созданный нами ранее экземпляр класса с предметом.

Теперь мы, наконец, готовы зарегистрировать наш предмет в Forge. Для этого вернемся к классу общего прокси CommonProxy. Во-первых, нам надо добавить аннотацию @Mod.EventBusSubscriber перед объявлением класса, чтобы дать понять Forge, что этот класс будет подисываться на сообщения.

...
@Mod.EventBusSubscriber
public class CommonProxy {
    public void preInit(FMLPreInitializationEvent e)
{
...

И создать метод, который и будет подписан на сообщение о фазе регистрации предметов в игре.

...
@SubscribeEvent
public static void registerItems(RegistryEvent.Register<Item> event) {
}
...

Я назвал метод registerItems, чтобы было понятнее, что именно он делает. Тип сообщения, на которое подписывается метод — RegistryEvent.Register<Item> — то-есть, сообщение регистрации объектов типа Item. И не забываем, что все методы, которые подписываются как обработчик сообщений, должны быть аннотированы при помощи @SubscribeEvent.

Мы подписались на сообщение. Теперь нам надо на него отреагировать! В нашем случае, когда Forge предлагает модам зарегистрировать свои предметы, нам необходимо зарегистрировать свой. Сделать это просто. В registerItems добавим строчку:

...
@SubscribeEvent
public static void registerItems(RegistryEvent.Register<Item> event) {
event.getRegistry().register(new Key());
}
...

Мы берем событие, которое к нам прилетело (event), запрашиваем из этого события регистр (getRegistry()) и этот регистр просим зарегистрировать (register()) экземпляр нашего предмета.

Теперь нам надо откуда-то вызвать метод TutorialItems.initModels. Модели предметов, равно как и их регистрация, создаются в определенную фазу по определенному событию. Однако, модели — вещь исключительно клиентская (серверу абсолютно без разницы, как выглядит предмет и какая у него текстура) поэтому, отредактируем наш ClientProxy:

...
@Mod.EventBusSubscriber(Side.CLIENT)
public class ClientProxy extends CommonProxy {
@Override
public void preInit(FMLPreInitializationEvent e) {
...

Добавим аннотацию перед определением класса, чтобы класс мог подписываться на события.

Обратите внимание: в этот раз я указал сторону (Side.CLIENT) в аннотации. Это даст понять Forge, что этот класс может подписываться только на клиентские события.

И добавим сам метод с подпиской на событие о регистрации моделей:

...
@SubscribeEvent
public static void registerModels(ModelRegistryEvent event) {
TutorialItems.initModels();
}
...

Я назвал метод registerModels, хотя, как я уже в который раз повторяю, имя метода не играет роли. Главное, чтобы вам самим было понятно, что он делает. Ну и, по возможности, не забывайте о тех людях, которые вам будут помогать отлаживать ваш код.

Оффтоп: мне очень нравится глагол "отлаживать". С одной стороны — этот глагол произошел от существительного «отладка», с другой — его значение вполне можно интерпретировать, как «избавление от лажи». И, поверьте, второе случается гораздо чаще. Особенно, когда вы только начинаете программировать.

Внутри метода мы просто вызываем статический метод initModels нашего класса с предметами.

Казалось бы, все, да? Нет! Дело в том, что на текущий момент, если мы запустим игру, ошибок не будет. Но наш предмет будет иметь имя item.tutorial.key.name, а выглядеть он будет как квадрат с противного цвета текстурой, чем-то напоминающей кусок шахматной доски какого-то извращенца.

Давайте исправлять. Первым пунктом, нам нужно создать структуру каталогов для ресурсов проекта. Сделать это нужно так, чтобы в итоге у нас получилось вот такое вот дерево:

src
|--- main
| |------ resources
| | |--------- assets
| | | |------------ tutorial (техническое название мода)
| | | | |--------------- lang (здесь будут файлы локализации)
| | | | |--------------- models
| | | | | |------------------ item (здесь хранятся файлы с моделями предметов)
| | | | |--------------- textures
| | | | | |------------------ items (здесь хранятся текстуры предметов)

И начнем создавать файлы прямо по порядку следования каталогов. В IDEA сделаем правый клик на каталоге resources/asstes.tutorial/lang и выберем «New → File». В качестве имени файла укажем «en_US.lang» (регистр букв важен!). В этом файле мы опишем соответствия внутренним «нелокализованным» именам предметов и блоков и читаемых названий на английском языке.

Кстати: Если вам нетерпится перевести свой мод на русский, просто создайте по тому же пути файл с именем «ru_RU.lang» — в него вы будете вписывать перевод на русский. Однако, файл с английским языком все еще необходим.

Кстати 2: В IDEA существует плагин, который добавляет поддержку для .lang файлов. Сделан плагин специально для мододелов майнкрафта, и IDEA вам предложит его установить как только вы откроете .lang файл в редакторе. Соглашайтесь, или не соглашайтесь по своему усмотрению.

В открывшемся файле нам надо очень аккуратно прописать перевод названия нашего предмета на английский язык. Делается это следующей строчкой:

item.tutorial.key.name=Key

Обратите внимание! Нелокализованное имя для предмета формируется следующим образом: item. техническое имя мода. регистровое имя предмета. name=. Пробелы после name не допускаются равно как и пробелы после знака равенства!

Если хотите, в файле ru_RU.lang можете прописать аналогичную строку с переводом названия предмета на русский:

item.tutorial.key.name=Ключ

Дальше. В models/items создадим файл с именем «регистровое имя предмета.json». В нашем случае: key.json

{
"parent": "item/generated",
"textures": {
"layer0": "tutorial:items/key"
}
}

Это, ребятки, ванильное описание модели предмета. За более подробной информацией, я вас отправлю читать вики по ванилле по ссылке: https://minecraft.gamepedia.com/Model#Item_models

А от себя лишь скажу, что в значении «layer0» указывается относительный путь до файла-текстуры предмета без указания расширения. «tutorial:items/key» означает, что наша текстура расположена в src/main/resources/assets/tutorial/textures/items/key.png

Обратите внимание: все текстуры в майнкрафте должны иметь формать PNG, а размер текстур в ванильном майнкрафте всего 16х16 пикселей.

Теперь осталось наваять какую-нибудь текстуру в любимом графическом редакторе, сохранить ее в файл в формате PNG, назвать «key» (со строчной буквы!!!) и поместить в textures/items.

Художник из меня, конечно, тот еще, но тем, кому лень рисовать текстуру ключа, вот вам мой вариант:

Пишем моды для minecraft — статья 2, image #8

Теперь пришла пора запустить наш мод! Сверху справа в окне IDEA выбираем конфигурацию «Minecraft Client» и нажимаем на иконку с зеленым жучком!

Пишем моды для minecraft — статья 2, image #9

У нас должен запуститься клиент майнкрафта. Создаем новый мир, включаем креативный режим, открываем инвентарь и ищем предмет с нашим именем, то-есть «Key». Если вы все сделали правильно, вы его найдете! И даже увидите, что он выглядит как красный ключ (или вы нарисовали свою текстуру?).

Пишем моды для minecraft — статья 2, image #10

А на этом все на сегодня. Думаю, я и так сломал вам мозги достаточно для одного дня. Переваривайте! И, удачи в моддинге!

3916 views·11 shares