Іноді використання формату моделі Minecraft недостатньо. Якщо вам потрібно додати динамічний рендер до візуальних елементів вашого блока, вам потрібно буде використовувати BlockEntityRenderer.
Наприклад, нумо зробимо так, щоб блок лічильника зі статті про блоки-сутності показував кількість натискань у зверху.
Створення BlockEntityRenderer
Рендер блока-сутності використовує систему надсилання/рендера, де ви спочатку надсилаєте дані, необхідні для рендера об’єкта на екрані, а потім гра рендерить об’єкт, використовуючи його поданий стан.
Створюючи BlockEntityRenderer для CounterBlockEntity, важливо помістити клас у відповідний вихідний набір, наприклад src/client/, якщо ваш проєкт використовує розділені вихідні набори для клієнта та сервера. Доступ до пов’язаних із рендером класів безпосередньо у вихідному наборі src/main/ небезпечний, оскільки ці класи можуть бути завантажені на сервер.
По-перше, нам потрібно створити BlockEntityRenderState для нашого CounterBlockEntity, щоб зберігати дані, які використовуватимуться для рендера. У цьому випадку нам знадобиться, щоб `clicks' були доступні під час рендера.
java
public class CounterBlockEntityRenderState extends BlockEntityRenderState {
private int clicks = 0;
public int getClicks() {
return clicks;
}
public void setClicks(int clicks) {
this.clicks = clicks;
}
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
Потім ми створюємо BlockEntityRenderer для нашого CounterBlockEntity.
java
public class CounterBlockEntityRenderer implements BlockEntityRenderer<CounterBlockEntity, CounterBlockEntityRenderState> {
public CounterBlockEntityRenderer(BlockEntityRendererProvider.Context context) {
}
@Override
public CounterBlockEntityRenderState createRenderState() {
return new CounterBlockEntityRenderState();
}
@Override
public void extractRenderState(CounterBlockEntity blockEntity, CounterBlockEntityRenderState state, float tickProgress, Vec3 cameraPos, @Nullable ModelFeatureRenderer.CrumblingOverlay crumblingOverlay) {
}
@Override
public void submit(CounterBlockEntityRenderState state, PoseStack matrices, SubmitNodeCollector queue, CameraRenderState cameraState) {
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Новий клас має конструктор із BlockEntityRendererProvider.Context як параметр. У Context є кілька корисних штук рендера, наприклад ItemRenderer або TextRenderer. Крім того, включивши такий конструктор, стає можливим використовувати конструктор як сам функціональний інтерфейс BlockEntityRendererProvider:
java
public class ExampleModBlockEntityRenderer implements ClientModInitializer {
@Override
public void onInitializeClient() {
BlockEntityRenderers.register(ModBlockEntities.COUNTER_BLOCK_ENTITY, CounterBlockEntityRenderer::new);
}
}1
2
3
4
5
6
2
3
4
5
6
Ми перевизначимо кілька методів для налаштування стану рендера разом із методом render, де буде налаштовано логіку рендера.
createRenderState можна використовувати для ініціалізації стану рендера.
java
@Override
public CounterBlockEntityRenderState createRenderState() {
return new CounterBlockEntityRenderState();
}1
2
3
4
2
3
4
extractRenderState можна використовувати для оновлення стану рендера за допомогою даних сутності.
java
@Override
public void extractRenderState(CounterBlockEntity blockEntity, CounterBlockEntityRenderState state, float tickProgress, Vec3 cameraPos, @Nullable ModelFeatureRenderer.CrumblingOverlay crumblingOverlay) {
// :::1
BlockEntityRenderer.super.extractRenderState(blockEntity, state, tickProgress, cameraPos, crumblingOverlay);
state.setClicks(blockEntity.getClicks());
// :::1
}1
2
3
4
5
6
7
2
3
4
5
6
7
Ви маєте зареєструвати рендер блока-сутності у своєму класі ClientModInitializer.
BlockEntityRenderers — це реєстр, який зіставляє кожен BlockEntityType зі спеціальним кодом рендера на відповідний BlockEntityRenderer.
Малювання на блоках
Тепер, коли у нас є рендер, ми можемо малювати. Метод render викликається кожним кадром, і саме в ньому відбувається магія рендера.
Переміщення
По-перше, нам потрібно змістити та повернути текст так, щоб він знаходився вище блока.
INFO
Як випливає з назви, PoseStack є стеком, що означає, що ви можете надсилати та витягувати перетворення. Гарне емпіричне правило полягає в тому, щоб додати новий блок на початку методу render і відкрити його в кінці, щоб рендер одного блока не впливав на інші.
Більше інформації про PoseStack можна знайти в статті про основні концепції рендера.
Щоб полегшити розуміння необхідних перекладів і поворотів, візуалізуємо їх. На цьому зображенні зелений блок — це місце, де буде намальовано текст, усталено у найдальшій нижній лівій точці блока:

Отже, спочатку нам потрібно перемістити текст наполовину блока на осях X і Z, а потім перемістити його вгору до верхньої частини блока на осі Y:

Це робиться за допомогою одного виклику translate:
java
matrices.translate(0.5, 1, 0.5);1
Ось і переклад зроблено, залишаються обертання і масштаб.
Усталено, текст малюється на площині X-Y, тому нам потрібно повернути його на 90 градусів навколо осі X, щоб він був спрямований вгору на площині X-Z:
![Зелений блок у верхній центральній точці, спрямований догори] (/assets/develop/blocks/block_entity_renderer_3.png)
PoseStack не має функції rotate, натомість нам потрібно використовувати multiply і Axis.XP:
java
matrices.multiply(Axis.XP.rotationDegrees(90));1
Тепер текст у правильному положенні, але він завеликий. BlockEntityRenderer промальовує весь блок на куб [-0.5, 0.5], тоді як TextRenderer використовує координати Y [0, 9]. Таким чином, нам потрібно зменшити його в 18 разів:
java
matrices.scale(1/18f, 1/18f, 1/18f);1
Тепер усе перетворення виглядає так:
java
matrices.pushPose();
matrices.translate(0.5, 1, 0.5);
matrices.mulPose(Axis.XP.rotationDegrees(90));
matrices.scale(1/18f, 1/18f, 1/18f);1
2
3
4
2
3
4
Малювання тексту
Як згадувалося раніше, Context, переданий у конструктор нашого рендера, має TextRenderer, який ми можемо використовувати для вимірювання тексту (width), який корисний для центрування.
Щоб намалювати текст, ми передамо необхідні дані в чергу рендера. Оскільки ми малюємо деякий текст, ми можемо використати метод submitText, наданий через екземпляр OrderedRenderCommandQueue, який передається в метод render.
java
String text = state.getClicks() + "";
float width = textRenderer.width(text);
// draw the text. params:
// text, x, y, color, ordered text, shadow, text layer type, light, color, background color, outline color
queue.submitText(
matrices,
-width / 2, -4f,
Component.literal(text).getVisualOrderText(),
false,
Font.DisplayMode.SEE_THROUGH,
state.lightCoords,
0xffffffff,
0,
0
);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Метод submitText приймає багато параметрів, але найважливіші з них:
FormattedCharSequenceдля малювання;- його координати
xіy; - RGB-значення
color; Matrix4f, що описує, як його слід трансформувати (щоб отримати один ізPoseStack, ми можемо використати.last().pose(), щоб отриматиMatrix4fдля самого верхнього запису).
І після всієї цієї роботи ось результат:


