Рендеры сущностей блока 26.1.2
Узнайте, как разнообразить рендеринг с помощью блочных рендереров сущностей.
WARNING
Эта страница написана для версии 26.1.2. Документация для более старых версий может быть неполной.
Иногда использования формата моделей Minecraft недостаточно. Если вам нужно добавить динамический рендеринг к визуальным эффектам блока, вам нужно использовать BlockEntityRenderer.
Например, давайте сделаем так, чтобы блок Counter из статьи Block Entities показывал кол-во кликов на своей верхней стороне.
Создание BlockEntityRenderer
Рендеринг блоков, сущностей использует систему submit/render, в которой вы сначала отправляете данные, необходимые для рендеринга объекта на экран, а затем игра рендерит объект, используя его отправленное состояние.
При создании BlockEntityRenderer для CounterBlockEntity важно поместить класс в соответствующий набор исходных текстов, например src/client/, если ваш проект использует раздельные наборы исходных текстов для клиента и сервера. Обращение к классам, связанным с рендерингом, непосредственно в наборе исходных текстов src/main/ небезопасно, поскольку эти классы могут быть загружены на сервере.
Во-первых, нам нужно создать BlockEntityRenderState для нашего CounterBlockEntity, чтобы хранить данные, которые будут использоваться для рендеринга. В этом случае нам нужно, чтобы клики были доступны во время рендеринга.
java
public class CounterBlockEntityRenderState extends BlockEntityRenderState {
private int clicks = 0;
public int getClicks() {
return this.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 или Font. Также, включение такого конструктора позволяет использовать конструктор в качестве функционального интерфейса 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
Мы переопределим несколько методов, чтобы настроить состояние рендера, а также метод submit, в котором будет реализована логика рендеринга.
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) {
BlockEntityRenderer.super.extractRenderState(blockEntity, state, tickProgress, cameraPos, crumblingOverlay);
state.setClicks(blockEntity.getClicks());
}1
2
3
4
5
2
3
4
5
Вы должны зарегистрировать рендеринг блочных сущностей в классе ClientModInitializer.
BlockEntityRenderers - это реестр, который сопоставляет каждый BlockEntityType с пользовательским кодом рендеринга с соответствующим BlockEntityRenderer.
Рисование на блоках
Теперь, когда у нас есть рендерер, мы можем рисовать. Метод submit вызывается каждый кадр — именно здесь происходит вся магия рендеринга.
Передвижение
Сначала нам нужно сместить и повернуть текст так, чтобы он находился на верхней стороне блока.
INFO
Как следует из названия, PoseStack — это стек, то есть вы можете добавлять (push) и удалять (pop) преобразования. Хорошее правило — делать push в начале метода submit и pop в конце, чтобы рендер одного блока не влиял на другие.
Больше информации о PoseStack можно найти в статье Основы рендеринга.
Чтобы упростить понимание необходимых смещений и вращений, давайте визуализируем их. На этом изображении зелёный блок показывает, где будет отрисован текст — по умолчанию в самой дальней нижней левой точке блока:

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

Это делается одним вызовом translate:
java
matrices.translate(0.5, 1, 0.5);1
Смещение выполнено, остаются вращение и масштабирование.
По умолчанию текст рисуется в плоскости XY, поэтому нужно повернуть его на 90 градусов вокруг оси X, чтобы он был направлен вверх в плоскости XZ:

У PoseStack нет функции rotate, вместо этого нужно использовать mulPose и Axis.XP:
java
matrices.mulPose(Axis.XP.rotationDegrees(90));1
Теперь текст находится в правильной позиции, но он слишком большой. BlockEntityRenderer отображает весь блок в кубе [-0.5, 0.5], тогда как Font использует координаты 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, передаваемый в конструктор нашего рендерера, содержит Font, который можно использовать для измерения текста (width), что полезно для центрирования.
Чтобы отрисовать текст, мы будем отправлять необходимые данные в очередь рендеринга. Поскольку мы рисуем текст, можно использовать метод submitText, предоставляемый через экземпляр SubmitNodeCollector, переданный в метод submit.
java
String text = state.getClicks() + "";
float width = this.font.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; PoseStack, описывающий, как его следует преобразовать.
И после всей этой работы вот результат:


