有的时候,用 Minecraft 自带的模型格式和渲染器并不足够。 如果需要为你的方块的视觉效果添加动态渲染,则需要使用 BlockEntityRenderer。
举个例子,让我们来制作一个在 方块实体 文章中出现的 Counter Block,这个方块会在方块顶部显示点击次数。
创建一个 BlockEntityRenderer
首先,我们需要为我们的 CounterBlockEntity 创建一个 BlockEntityRenderer。
在为 CounterBlockEntity 创建 BlockEntityRenderer 时,如果您的项目对客户端和服务器端使用了不同的源代码集,则需要确保将该渲染器类放置于对应的源代码集中,例如客户端相关的类应放在 src/client/ 目录下。 直接访问 src/main/ 源代码集中与渲染相关的类并不安全,因为这些类可能已在服务器上加载。
java
public class CounterBlockEntityRenderer implements BlockEntityRenderer<CounterBlockEntity> {
public CounterBlockEntityRenderer(BlockEntityRendererFactory.Context context) {
}
@Override
public void render(CounterBlockEntity entity, float tickProgress, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, Vec3d cameraPos) {
}
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
我们的新类有一个以 BlockEntityRendererFactory.Context 为参数的构造函数。 Context 有几个非常有用的渲染辅助工具,比如 ItemRenderer 或 TextRenderer。 此外,通过包含这样一个构造函数,就可以将该构造函数用作 BlockEntityRendererFactory 功能接口本身:
java
public class FabricDocsBlockEntityRenderer implements ClientModInitializer {
@Override
public void onInitializeClient() {
BlockEntityRendererFactories.register(ModBlockEntities.COUNTER_BLOCK_ENTITY, CounterBlockEntityRenderer::new);
}
}1
2
3
4
5
6
2
3
4
5
6
你应该在 ClientModInitializer 类中注册你的方块实体渲染器。
BlockEntityRendererFactories 是一个注册表,用于将带有自定义渲染代码的每个 BlockEntityType 映射到各自的 BlockEntityRenderer。
在方块上绘画
现在我们有了渲染器,我们就可以开始绘画了。 render 方法在每一帧都会被调用,这就是渲染魔法发生的地方。
四处移动方块
首先,我们需要偏移和旋转文本,使其位于方块的顶部。
INFO
顾名思义,MatrixStack 是一个_堆栈_,这意味着您可以压入和弹出变换。 一个好的经验法则是在 render 方法开始时压入一个新的方块,并在结束时弹出,这样一个方块的渲染就不会影响到其他方块。
更多关于 MatrixStack 的信息可以在 基本渲染术语文章 中找到。
为了更容易理解所需的平移和旋转,让我们将它们可视化。 在该图中,绿色方块是绘制文本的位置,默认情况下位于方块的最左下角:

因此,首先我们需要在 X 轴和 Z 轴上将文本移动到方块的一半,然后在 Y 轴上将其移动到方块的顶部:

这些都可以以单个 translate 调用来实现:
java
matrices.translate(0.5, 1, 0.5);1
我们已经完成了 平移,接下来是 旋转 和 缩放。
默认情况下,文字会在 XY 平面上渲染,所以我们需要将其绕 X 轴旋转 90 度,让他面向上方的 XZ 平面:

MatrixStack 没有 rotate 函数,所以我们需要使用 multiply 和 RotationAxis.POSITIVE_X:
java
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90));1
那么现在的文字就在正确的位置了,但是文字现在太大了。 BlockEntityRenderer 映射整个方块到一个 [-0.5, 0.5] 的立方体,而 TextRenderer 使用 [0, 9] 的 Y 坐标。 因此,我们需要将其缩小 18 倍:
java
matrices.scale(1/18f, 1/18f, 1/18f);1
那么,我们整个的变换看起来就像这样:
java
matrices.push();
matrices.translate(0.5, 1, 0.5);
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90));
matrices.scale(1/18f, 1/18f, 1/18f);1
2
3
4
2
3
4
绘制文字
如前所述,传入渲染器构造函数的 Context 包含一个 TextRenderer ,我们可以用它来绘制文本。 在这个例子中,我们将它保存在一个字段中。
TextRenderer 有一个方法来测量文字 (即 getWidth),这对于将其居中放置非常有用,然后绘制它(使用 draw)。
java
String text = entity.getClicks() + "";
float width = textRenderer.getWidth(text);
// draw the text. params:
// text, x, y, color, shadow, matrix, vertexConsumers, layerType, backgroundColor, light
textRenderer.draw(
text,
-width/2, -4f,
0xffffffff,
false,
matrices.peek().getPositionMatrix(),
vertexConsumers,
TextRenderer.TextLayerType.SEE_THROUGH,
0,
light
);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
draw 方法接受许多的参数,但是最重要的几个是:
- 要被绘画的
Text(或者String); - 文字的
X和Y坐标; - 文字的 RGB 颜色
color值; - 描述其转换方式的
Matrix4f(要从一个MatrixStack中获取,我们可以使用.peek().getPositionMatrix()来获取最顶端条目的Matrix4f)。
经过我们的努力,这就是最终结果:


