Как сделать энтити в майнкрафт

Обновлено: 06.07.2024

В данной статье я предоставляю свой перевод гайда с сайта shadowfacts по работе с TileEntity. Исходники примера доступны тут.

В Майнкрафте класс Block используется для представления не просто единичного блока в мире, а блока как типа. Инстанс (экземпляр) Block содержит свойства для каждого экземпляра вашего блока, существующего в мире. Если мы хотим что бы наш блок содержал уникальные данные для каждого отдельно взятого экземпляра нам нужно использовать TileEntity .

Существует распространённый миф что TileEntity это плохо, особенно для производительности. Это не так. Они могут негативно влиять на производительность если они реализованы не умело, как в прочем и любые другие объекты.

Тайлы бывают двух типов: обновляющиеся (ticking) и не обновляющиеся (non-ticking). Обновляющиеся тайлы обновляются каждый игровой тик (обычно 20 раз в секунду). Они влияют на производительность интенсивнее и требуют аккуратной реализации. Не обновляющиеся тайлы существуют для простого хранения данных и в этом туториале мы создадим простой, не обновляющийся TileEntity .

Прежде чем мы создадим тайл, мы добавим класс, который упростит их создание в будущем.

В первую очередь создадим класс BlockTileEntity :

Этот класс предоставляет нам следующее: * Он расширяет BaseBlock , содержащий нашу удобную базовую реализацию Block

Теперь когда у нас есть удобная основа самое время создать блок.

Создадим BlockCounter , расширяющий BlockTileEntity .

Наш блок расширяет BlockTileEntity и содержит обобщение TileEntityCounter (который предстоит создать), так как этот тайл принадлежит этому блоку.

В конструкторе мы просто напросто вызываем суперконструктор и передаём туда все параметры, которые будем указывать при создании блока.

В методе getTileEntityClass() вернём TileEntityCounter.class (мы ещё создадим его). Это позволит зарегистрировать его ассоциировав с именем блока.

В createTileEntity() мы возвращаем новый экземпляр класса TileEntityCounter .

Как было подмечено выше, перед тем как работать со счётчиком тайла, мы должны удостовериться, что действия происходят на сервере. Делаем мы это потому как в Майнкрафте клиент и сервер полностью разделены и некоторые методы вызываются для обеих сторон.

В многопользовательской игре множество клиентов подключены к одному серверу. В этом случае разделение между сторонами очевидно, но в одиночной игре всё немного сложнее. В многопользовательской игре сервер, к которому происходит подключение, отражает физический сервер и все отдельные подключенные клиенты являются физическими клиентами.

В одиночной игре клиент и сервер тоже разделены даже несмотря на то, что они исполняются на одном компьютере (на одной JVM, но в разных потоках). В одиночной игре клиент подключается к локальному, приватному серверу, функции которого схожи с физическим сервером. В этом случае серверный поток отражает логический сервер, а клиентский поток отражает логический клиент, так как обе логические стороны выполняются на одной физической стороне.

Поле World.isRemote используется для проверки стороны, на которой происходит выполнение (будь она логической или физической). Оно равно true для физического клиента в многопользовательской игре и для логического клиента в одиночной игре. Это поле равно false для физического сервера в многопользовательской игре и для логического сервера в одиночной.

Делая проверку в условии !world.isRemote , мы обеспечиваем уверенность что действия будут производится на сервере (физическом или логическом).

Теперь когда у нас есть блок, мы должны создать тайл для него.

Создадим класс TileEntityCounter :

Содержимое весьма не хитрое: * Класс расширяет стандартный TileEntity * Содержит приватное поле count , содержащее значение счётчика. * Переопределяет writeToNBT() и readFromNBT() что бы наши данные были сохранены и загружены с диска. * Предоставляет методы getCount() , incrementCount() , decrementCount() для работы с полем count . Кроме того, в методах, изменяющих значение счётчика ( count ) происходит вызов markDirty() из TileEntity . Он даёт игре понять что данные были изменены с момента последнего сохранения и их нужно перезаписать.

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

В нашем случае мы сохраняем значение поля count как целочисленный тип Integer с ключём в виде строки " count " в NBT в методе writeToNBT() и читаем его в readFromNBT() .

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

Регистрацию будем производить через вызов GameRegistry.registerTileEntity() и передавать в него класс тайла и его регистрационное имя. Это требуется для того, что бы при загрузке Майнкрафт определял какой TileEntity к какому блоку принадлежит и пересоздавал его загружая из NBT.

Не забывайте, регистрация Block , ItemBlock и TileEntity происходит во время преинициализации в CommonProxy , а регистрация рендера для ItemBlock происходит в процессе преинициализации в ClientProxy (для рендера через ModelLoader ).

Увеличение значения счетчика

Нажав ПКМ по нижней стороне несколько раз вы увидите это:

Уменьшение значения счетчика

В Minecraft сущностями являются и блоки, которые сохраняют дополнительную информацию за 4-битным полем (табличка, сундук), а также и сложно анимированные структуры, имеющие более широкий количественный эквивалент битового поля (кристалл Края, шалкеровый ящик).

Движение сущностей [ ]

Гравитация работает в Minecraft иначе, чем в реальном мире, так как ускорение различно для разных объектов. Кроме того, есть сила сопротивления, пропорциональная скорости, опять же зависящая от объекта.

Объект Ускорение
блоки/такт 2
Ускорение
м/с 2
Сопротивление
1/такт
Предельная скорость
блоков/такт
Предельная скорость
м/с
Игроки и другие живые существа 0.08 32 0.02 3.92 78.4
Предметы, падающие блоки, TНT 0.04 16 0.02 1.96 39.2
Лодки и вагонетки 0.04 16 0.05 0.76 15.2
Брошенные куриные яйца, снежки, зелья и жемчуг Края 0.03 12 0.01 3.00 60.0
Выпущенные стрелы 0.05 20 0.01 5.00 100.0

Содержание

Особенности [ ]

Почти все сущности имеют следующие свойства:

  • Положение, скорость и вращение (в соответствии с обычной ньютоновской физикой). , которую они занимают, представляющий собой трехмерное поле с фиксированной высотой и шириной (если смотреть сверху, то видно квадрат с фиксированным положением (не вращается)).
  • Текущее здоровье.
  • Возможность быть передвинутым текущей водой, давящей на сущность извне.
  • Отсутствие возможности прохождения через твёрдые блоки.
  • Отсутствие возможности располагать любые блоки внутри объёма, который занимает сущность, кроме выпавших предметов, которые автоматически выталкиваются из блоков. Также если сущность оказалась внутри блоков (например, после падения песка или гравия), то сущность может непроизвольно выйти из блока, но не войти в блок. (Например, если дверь закрылась внутри игрока, то игрок может, подпрыгнув, встать на одну половинку двери, если над игроком есть воздух, а также просто пройти на любую половину).
  • Приобретение освещённости в соответствии с освещённостью блока, на котором находится сущность. Например, если вагонетка окажется внутри твёрдого блока, она станет чёрной (потому что твёрдые блоки всегда имеют уровень света 0), а стрелы становятся чёрными, если застряли в потолке.
  • Наличие круглой тени. Она отображается в виде спрайта на том блоке, на котором находится сущность.

EntityTrowable или предмет, который можно кинуть

Глава 1. Подготовка:
1.Будет неплохо, если у нас уже есть готовый предмет, или на худой конец основа мода и настроенный прокси. Если вы этого не сделали, советую заглянуть сюда: Прокси, Предмет.
2.Также нам потребуется сделать инстанцию.
Для этого в главном классе нашего мода (в моём случае он называется FlyApple) добавим вот такой код:

Если вы проделали всё вышеописанное, можете переходить к следующей главе.

Глава 2. Класс entity:
В основном пакете мода (в моём случае это ru.mousecray.flyapple ) создадим пакет entity .
Далее создадим класс EntityFlyApple:

Теперь зарегистрируем entity. Для этого в классе CommonProxy в методе preInit
добавим вот эту строчку:

new ResourceLocation("flyapple", "flying_apple") - Ссылка на ресурсы нашего entity (modid:name).
EntityFlyApple.class - Имя класса нашего entity.
"flyapple:flying_apple" - Имя entity. Рекомендуется писать в стиле modid:name чтобы не было конфликтов с майнкрафтом.
0 - Идентификатор нашего entity. В одном моде, у каждого entity должен быть свой уникальный id.
FlyApple.instance - То, о чём мы говорили в первой главе - инстанция.
64, 20 - 1. Радиус, в котором entity будет замечать других существ; 2.Частота этого наблюдения.
true - Нужно ли слать пакеты об изменении скорости. Не имеет смысла, для неподвижных существ.

Теперь мы можем запустить игру и посмотреть, что получилось.

Мы можем призвать наш entity, однако не увидим его. Ведь мы не сделали ему рендер.

Глава 3. Рендер:
В отличии от других примеров по созданию entity мы Не будем делать свой рендер, мы просто используем ванильный класс RenderSnowball .
Итак, приступим!

После 1.12 случилось? обновление, и стандартный метод регистрации стал @Deprecated. В связи с этим нам нужно создавать класс-наследник IRenderFactory. Суть в том, что для каждого "снаряда", придётся создавать новый класс IRenderFactory , пусть даже анонимный. Это не очень удобно, поэтому мы создадим один общий класс для всех EntityTrowable . Начнём.
В главном пакете мода создадим пакет render , а в нём класс SnowballRenderFactory :

Разновидности сущностей [ ]

Список сущностей в Minecraft и некоторые их свойства.

Твёрдые сущности препятствуют движению другим сущностям.

Размер указан в длине ребра блока (принимается за 1 метр), и это размер привязанного к координатной сетке граничного поля сущности с размерами в обеих горизонтальных осях (X и Z) и вертикальной (Y). Поле не вращается при визуальном вращении объекта.

Tile Entities Часть I:Основы

Изначально туториал содержал метку "Перевод", однако оригинальное содержимое сильно переработано, описана синхронизация и дополнительно добавлена информация для версии игры 1.7.10. Всвязи с этим я позволил себе убрать метку, однако ссылку на исходную статью оставляю.

Это первый туториал из серии статей о TileEntity.

В Майнкрафте класс Block используется для представления не просто единичного блока в мире, а блока как типа. Инстанс (экземпляр) Block содержит свойства для каждого экземпляра вашего блока, существующего в мире. Если мы хотим что бы наш блок содержал уникальные данные для каждого отдельно взятого экземпляра нам нужно использовать TileEntity.

Существует распространённый миф что TileEntity плохо влияет на производительность - это не так. Они могут негативно влиять на производительность если они реализованы не умело, как в прочем и любые другие объекты.

Тайлы бывают двух типов: обновляющиеся (ticking) и не обновляющиеся (non-ticking).
Обновляющиеся тайлы обновляются каждый игровой тик (обычно 20 раз в секунду). Они влияют на производительность интенсивнее и требуют аккуратной реализации. Не обновляющиеся тайлы существуют для простого хранения данных. Ну а теперь подробнее по реализации:

Все создаваемые тайлы обновляются автоматически, что значит метод updateEntity() вызывается каждый тик. Отключить обновление можно переопределив canUpdate() и вернув в нём false ( true по умолчанию).

Класс TileEntity лишён реализации автоматического обновления по умолчанию. Если обновление необходимо, то реализуйте интерфейс ITickable, который добавит метод update() .

Разделение: клиент и сервер

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

В многопользовательской игре множество клиентов подключены к одному серверу. В этом случае разделение между сторонами очевидно, но в одиночной игре всё немного сложнее. В многопользовательской игре сервер, к которому происходит подключение, отражает физический сервер и все отдельные подключенные клиенты являются физическими клиентами.

В одиночной игре клиент и сервер тоже разделены даже несмотря на то, что они исполняются на одном компьютере (на одной JVM, но в разных потоках). В одиночной игре клиент подключается к локальному, приватному серверу, функции которого схожи с физическим сервером. В этом случае серверный поток отражает логический сервер, а клиентский поток отражает логический клиент, так как обе логические стороны выполняются на одной физической стороне.

Хранение данных TileEntity между сессиями обеспечивается NBT. Данный формат используется для хранения данных в виде пар ключ-значение, которые легко сериализуется в байты и сохраняется на диск. Вы можете ознакомиться с классом NBTTagCompound для представления о типах данных, которые он может хранить. Ванильный код содержит множество хороших примеров по сохранению и чтению сложных структур данных.

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

Вспомогательные средства

Прежде чем мы создадим тайл, мы добавим класс, который упростит их создание в будущем.

Заметки [ ]

Картина также является сущностью, хоть она и неподвижна и привязана к сетке блоков. Двери, порталы, кровати и другие объекты больше одного блока на самом деле являются группами смежных блоков.

Стрелы и TНT предположительно, имеют бесконечное здоровье, потому что они не уничтожаются взрывами ТНТ (могут быть выстрелены из пушек) и огнем.

Лодки и вагонетки [ ]

Смотрите также: Способы передвижения (содержит информацию о скорости)

Лодки и вагонетки постепенно восстанавливают свое здоровье. Например, если быстро бить лодку рукой, то можно её уничтожить, но удары с паузами лодку не уничтожат. Количество отображаемых колебаний на лодках и вагонетках при ударе, по-видимому, указывает на их текущее здоровье.

Падающие блоки [ ]

Следующее описание относится к песку. Гравий, цемент и яйцо дракона ведут себя точно так же.

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

Правый клик [ ]

Сущности с действиями по правому щелчку (лодки, вагонетки, оцелоты и волки), в отличие от используемых блоков (вроде сундуков), не мешают выполнению действия с инструментом, который держится в руке. Например, если попытаться посадить прирученного волка с ведром воды в руке, волк сядет, но вы также выльете на волка воду.

Кормление волков едой не мешает потреблению пищи самим игроком, несмотря на отсутствие визуального эффекта. Если попытаться покормить волка с 2 кусками сырой свинины в руке, волк съест один кусок, а игрок съест второй (для получения этого эффекта нужно зажать ПКМ ). При использовании одного куска волк получит еду быстрее, чем игрок сможет её съесть, поэтому съедание будет остановлено из-за отсутствия еды.

Сущности могут оседлать или прицепиться к другим сущностям. Если так случилось, то за столкновение отвечает объём нижней/внешней сущности (свинья, лодка), и эта же сущность отвечает за движение. Возможны следующие комбинации:

    или игрок в вагонетке.
  • Игрок на свинье, лошади, ламе, в лодке. (скелет-иссушитель) на пауке (пещерный паук) (скелет-наездник).
  • Ребёнок-зомби (или ребёнок зомби-свиночеловека в Нижнем мире) на курице (Зомби-наездник)

Может существовать несколько уровней, например, игрок, оседлавший свинью в вагонетке.

Из-за ошибки, на 1.11 лодка может сесть в вагонетку.

Сущностей можно посадить друг на друга с помощью команд /summon и /entitydata . Например, /summon spider

,]> заспаунит паука, на котором сидят скелет и зомби.

Читайте также: