🇩🇪 Deutsch (German)
🇩🇪 Deutsch (German)
Erscheinungsbild
🇩🇪 Deutsch (German)
🇩🇪 Deutsch (German)
Erscheinungsbild
Diese Seite ist für folgende Version geschrieben:
1.21.10
Manchmal reicht das Nutzen von Minecraft's Modellformat nicht aus. Wenn du dynamisches Rendering zu dessen visuellen Elemten hinzufügen willst, wirst du einen BlockEntityRenderer nutzen müssen.
Lasst uns als Beispiel den Zählerblock aus dem Artikel zu Block Entitäten die Zahl an Klicks auf der Oberseite anzeigen lassen.
Zuerst müssen wir einen BlockEntityRenderer für unsere CounterBlockEntity erstellen.
Beim Erstellen eines BlockEntityRenderer für die CounterBlockEntity ist es wichtig, wenn das Projekt geteilte Quellen für den Client und den Server nutzt, die Klasse in das passende Quellenverzeichnis, wie src/client/, zu platzieren. Der Zugriff auf Rendering-bezogene Klassen direkt im src/main/ Quellenverzeichnis ist nicht sicher, da diese Klassen möglicherweise am Server nicht geladen sind.
public class CounterBlockEntityRenderer implements BlockEntityRenderer<CounterBlockEntity, CounterBlockEntityRenderState> {
public CounterBlockEntityRenderer(BlockEntityRendererFactory.Context context) {
}
@Override
public CounterBlockEntityRenderState createRenderState() {
return new CounterBlockEntityRenderState();
}
@Override
public void updateRenderState(CounterBlockEntity blockEntity, CounterBlockEntityRenderState state, float tickProgress, Vec3d cameraPos, @Nullable ModelCommandRenderer.CrumblingOverlayCommand crumblingOverlay) {
}
@Override
public void render(CounterBlockEntityRenderState state, MatrixStack matrices, OrderedRenderCommandQueue queue, CameraRenderState cameraState) {
}
}Die neue Klasse hat einen Konstruktor mit einem BlockEntityRendererFactory.Context als Parameter. Der Context hat einige nützliche Rendering-Hilfsmittel, wie den ItemRenderer oder TextRenderer. Durch die Aufnahme eines derartigen Konstruktors, wird es außerdem möglich den Konstuktor als funktionales Interface der BlockEntityRendererFactory selbst zu verwenden:
public class ExampleModBlockEntityRenderer implements ClientModInitializer {
@Override
public void onInitializeClient() {
BlockEntityRendererFactories.register(ModBlockEntities.COUNTER_BLOCK_ENTITY, CounterBlockEntityRenderer::new);
}
}Du solltest Renderer für Blockentitäten in deiner Klasse ClientModInitializer registrieren.
BlockEntityRendererFactories ist eine Registrierung, die jeden BlockEntityType mit benutzerdefinierten Rendering-Code dem entsprechenden BlockEntityRenderer zuordnet.
Jetzt, da wir den Renderer haben, können wir zeichnen. Die Methode render wird bei jedem Frame aufgerufen und ist der Ort, an dem die Magie des Renderns passiert.
Zunächst müssen wir den Text versetzen und drehen, damit er sich auf der oberen Seite des Blocks befindet.
INFO
Wie der Name bereits vermuten lässt ist der MatrixStack ein Stapel, was bedeutet, dass du Transformationen darauf hinzufügen (push) und davon entfernen (pop) kannst. Eine gute Faustregel ist es, einen neuen Block an den Anfang der render-Methode hinzuzufügen und ihn am Ende wieder zu entfernen, so dass das Rendern eines Blocks die anderen nicht beeinflusst.
Mehr Informationen zu dem MatrixStack kann in dem Artikel zu den grundlegenden Konzepten des Rendering gefunden werden.
Zum besseren Verständnis der erforderlichen Verschiebungen und Drehungen sollten wir sie visualisieren. In diesem Bild ist der grüne Block die Position, an der der Text gezeichnet werden würde, standardmäßig am äußersten linken unteren Punkt des Blocks:

Zunächst müssen wir den Text auf der X- und Z-Achse in die Mitte und ihn dann an der Y-Achse an den oberen Rand des Blocks verschieben:

Died wird durch einen einzelnen translate Aufruf gemacht:
matrices.translate(0.5, 1, 0.5);Somit ist die Verschiebung erledigt, Drehung und Skalierung bleiben.
Standardmäßig wird der Text auf der XY-Ebene gezeichnet, also müssen wir ihn um 90 Grad um die X-Achse drehen, damit er auf der XZ-Ebene nach oben zeigt:

Der MatrixStack hat keine rotate Methode, stattdessen müssen wir multiply und RotationAxis.POSITIVE_X verwenden:
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90));Jetzt ist der Text an der korrekten Position, aber ist zu groß. Der BlockEntityRenderer ordnet den ganzen Block zu einem [-0.5, 0.5] Würfel zu, während der TextRenderer X-Koordinaten von [0, 9] verwendet. Somit müssen wir es um den Faktor 18 herunter skalieren:
matrices.scale(1/18f, 1/18f, 1/18f);Jetzt sieht die ganze Transformation wie folgt aus:
matrices.push();
matrices.translate(0.5, 1, 0.5);
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90));
matrices.scale(1/18f, 1/18f, 1/18f);Wie bereits früher erwähnt, hat der an den Konstruktor unseres Renderers übergebene Context einen TextRenderer, welchen wir für das Zeichnen von Text einsetzen können. Für dieses Beispiel werden wir ihn in einem Feld speichern.
Der TextRenderer hat Methoden um Text zu messen (getWidth), welche für das Zentrieren nützlich ist, und um ihn zu zeichnen (draw).
String text = state.getClicks() + "";
float width = textRenderer.getWidth(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,
Text.literal(text).asOrderedText(),
false,
TextRenderer.TextLayerType.SEE_THROUGH,
state.lightmapCoordinates,
0xffffffff,
0,
0
);Die Methode draw nimmt einige Paramter, aber die Wichtigsten sind:
Text (oder String);x und y Koordinaten;color Wert;Matrix4f, die beschreibt, wie er transformiert werden soll (um eine aus einem MatrixStack zu erhalten, können wir .peek().getPositionMatrix() verwenden, um die Matrix4f für den obersten Eintrag zu erhalten).Und nach dieser ganzen Arbeit, ist hier das Ergebnis:
