Rendererizador do Bloco-Entidade 26.2
Aprendendo como apimentar a renderização com renderizador do bloco-entidade.
Às vezes, usar o modelo do Minecraft não é o suficiente. Se você precisa adicionar renderização dinâmica ao visual do seu bloco, você precisa usar BlockEntityRenderer.
Por exemplo, vamos fazer o Bloco Contador do artigo Bloco-Entidade mostrar o número de ativações na sua face superior.
Criando a BlockEntityRender
A renderização de bloco-entidade usa um sistema de envio/renderização onde você primeiramente envia os dados necessários para renderizar um objeto na tela, o jogo então renderiza os objetos usando o seu estado enviado.
Quando criando a BlockEntityRenderer para o CounterBlockEntity, é importante colocar a classe no Source Set apropriado, como src/client/ se seu projeto usa sources separados para client e servidor. Acessar classes relacionadas a renderização diretamente no source set src/main/ não é seguro porque essas classes podem não estar carregadas em um servidor.
Primeiro, nós precisamos criar um BlockEntityRenderState para que o nosso CounterBlockEntity registre os dados que serão usados para a renderização. Nesse caso, nós precisaremos que os clicks estejam disponíveis durante a renderização.
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
Então nós criamos um BlockEntityRenderer para o nosso 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
A nossa classe tem um construtor com BlockEntityRendererProvider.Context como um parâmetro. O Context têm algumas utilidades de renderização úteis, como o ItemRenderer ou Font. Também, ao incluir o construtor dessa forma, torna-se possível o uso do construtor como a própria interface funcional 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
Nós vamos sobrescrever alguns métodos para configurar o estado de renderização com o método submit onde a lógica de renderização será configurada.
createRenderState pode ser usado para inicializar o estado de renderização.
java
@Override
public CounterBlockEntityRenderState createRenderState() {
return new CounterBlockEntityRenderState();
}1
2
3
4
2
3
4
extractRenderState pode ser usado para atualizar o estado de renderização com os dados da entidade.
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
Você deve registrar a renderização bloco-entidade na sua classe ClientModInitializer.
BlockEntityRenderers é um registro que mapeia cada BlockEntityType com um código de renderização customizado para o seu respectivo BlockEntityRenderer.
Desenhando nos Blocos
Agora que temos uma renderização, nós podemos desenhar. O método submit é chamado a cada frame, e é aqui que a mágica da renderização acontece.
Movimentando
Primeiro, nós precisamos deslocar e rodar o texto para que esteja no topo do bloco.
INFO
Como o nome sugere, o PoseStack é uma pilha, que significa que você pode empilhar ou desempilhar transformações. Uma boa regra geral é empilhar uma nova no começo do método submit e desempilhar ela no final, assim a renderização de um bloco não afetará os demais.
Mais informações sobre o PoseStack pode ser encontrado no artigo de Conceitos de Renderização Básicos.
Para fazer as translações e rotações mais fáceis de entender, vamos visualizá-las. Nessa imagem, o bloco verde está onde o texto seria renderizado, por padrão no canto mais distante à esquerda do bloco:

Primeiro nós precisamos mover o texto até o meio do bloco nos eixos X e Z, e então movê-lo para o topo do bloco no eixo Y:

Isso é feito com uma única chamada translate:
java
matrices.translate(0.5, 1, 0.5);1
A translação está concluída, restam rotação e escala.
Por padrão, o texto é renderizado no plano XY, então nós precisamos rotacioná-lo 90 graus ao redor do eixo X para deixar a face virada para cima no plano ZX:

O PoseStack não possui uma função rotate, ao invés disso, nós precisamos usar mulPose e Axis.XP:
java
matrices.mulPose(Axis.XP.rotationDegrees(90));1
Agora o texto está na posição correta, mas é muito grande. O BlockEntityRenderer mapeia o bloco inteiro para um cubo [-0.5, 0.5], enquanto um Font usa as coordenadas Y de [0, 9]. Como tal, nós precisamos diminuí-lo por um fator de 18:
java
matrices.scale(1/18f, 1/18f, 1/18f);1
Agora, a transformação toda se parece com isso:
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
Renderizando Texto
Como mencionado anteriormente, o Context fornecido para o construtor da nossa renderização possui a fonte que nós podemos usar para medir o texto (width), o que é útil para centralização.
Para renderizar o texto, nós vamos fornecer os dados necessários para a fila de renderização. Já que estamos renderizando alguns textos, nós podemos usar o método submitText provido pela instância SubmitNodeCollector fornecido para o método 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
O método submitText recebe vários parâmetros, mas o mais importante deles são:
- o
FormattedCharSequencepara renderizar; - as suas coordenadas
xey; - o valor RGB de
color; - o
PoseStackdescrevendo como ele deve ser transformado.
E após tudo isso, aqui está o resultado:


