屏幕(Screens)
屏幕(Screens)通常是 Minecraft 中所有图形用户界面(GUI)的基础:负责接收用户输入,在服务器端进行验证,并将结果同步回客户端。它们可以与 菜单 结合,创建类似背包视图的通信网络,也可以作为独立界面,由模组开发者通过自己的 网络 实现进行处理。
屏幕由许多部分组成,因此要完全理解 Minecraft 中的“屏幕”实际上是什么并不容易。因此,本文档将依次介绍屏幕的各个组成部分及其应用方式,最后再讨论屏幕本身。
相对坐标(Relative Coordinates)
任何内容在被渲染时,都需要某种标识来指定其显示位置。由于有许多抽象层,大多数 Minecraft 的渲染调用都使用 x、y、z 三个值来表示坐标平面。x 值从左到右递增,y 值从上到下递增,z 值则是从远到近递增。然而,这些坐标并不是固定在某个特定范围内的。它们的取值范围会根据屏幕大小以及游戏选项中的“GUI 缩放比例”而变化。因此,必须格外注意,确 保传递给渲染调用的坐标值能够正确缩放——也就是要相对化(relativize)到可变的屏幕尺寸。
关于如何相对化你的坐标,请参考 屏幕 部分。
如果你选择使用固定坐标,或者屏幕缩放处理不正确,渲染出来的对象可能会显示异常或者位置错乱。检查坐标相对化是否正确的一个简便方法,是在视频设置中点击“GUI 缩放”按钮。这个值会作为你的显示宽高的除数,用于决定 GUI 的实际渲染比例。
GUI 图形(Gui Graphics)
Minecraft 渲染任何 GUI 通常都是通过 GuiGraphics 实现的。GuiGraphics 是几乎所有渲染方法的第一个参数;它包含了一些用于渲染常用对象的基础方法。这些方法主要分为五类:有色矩形、字符串、纹理、物品和提示框(tooltips)。此外,还有一个用于渲染组件片段的方法(#enableScissor / #disableScissor)。GuiGraphics 还暴露了 PoseStack,用于应用各种变换,以确保组件能被正确渲染到指定位置。此外,颜色采用 ARGB 格式。
有色矩形(Colored Rectangles)
有色矩形是通过位置颜色着色器(position color shader)绘制的。所有填充方法都可以选择性地传入一个 RenderType,以指定矩形的渲染方式。有三种类型的有色矩形可以被绘制。
首先,是水平方向和垂直方向的单像素宽有色线条,分别为 #hLine 和 #vLine。#hLine 需要传入两个 x 坐标,定义左端和右端(包含两端),一个顶部 y 坐标,以及颜色。#vLine 需要传入左侧 x 坐标,两个 y 坐标(分别定义顶部和底部,包含两端),以及颜色。
其次,是 #fill 方法(method),用于在屏幕上绘制一个矩形。线段相关的方法内部实际上也会调用这个方法。该方法需要传入左侧 x 坐标、顶部 y 坐标、右侧 x 坐标、底部 y 坐标以及颜色。#fillRenderType 方法的作用类似,不过它在绘制顶点时不会对坐标位置进行修正。
最后,还有 #fillGradient 方法(method),它可以绘制带有垂直渐变效果的矩形。该方法需要传入右侧 x 坐标、底部 y 坐标、左侧 x 坐标、顶部 y 坐标、z 坐标,以及底部和顶部的颜色。
字符串(Strings)
字符串的绘制是通过其 Font 完成的,通常会包含用于普通、可透视和偏移模式的专用着色器(shader)。有两种字符串对齐方式 可以被渲染,并且都带有阴影效果:左对齐字符串(#drawString 方法)和居中对齐字符串(#drawCenteredString 方法)。这两种方法都需要传入用于渲染的字体、要绘制的字符串、表示字符串左侧或中心的 x 坐标、顶部 y 坐标,以及颜色。
如果需要让文本在指定范围内自动换行,可以使用 #drawWordWrap 方法。该方法默认渲染为左对齐字符串。
字符串通常应以 Component 形式传递,因为它们能处理多种用例,包括该方法的另外两种重载形式。
纹理(Textures)
纹理的绘制是通过“位块传输”(blitting)实现的,因此相关方法名为 #blit。在此过程中,图片的位数据会被直接复制并绘制到屏幕上。这些操作会使用位置纹理着色器进行渲染。
每个 #blit 方法都需要传入一个 Function<ResourceLocation, RenderType>,用于决定如何渲染纹理,以及一个 ResourceLocation,它代表纹理的绝对路径:
// 指向 'assets/examplemod/textures/gui/container/example_container.png'
private static final ResourceLocation TEXTURE = ResourceLocation.fromNamespaceAndPath("examplemod", "textures/gui/container/example_container.png");
虽然 #blit 方法有许多不同的重载版本,这里我们只讨论其中的两种。
第一种 #blit 方法需要传入两个整数、两个浮点数,最后再传入四个整数,假定图片为 PNG 文件。参数分别为:屏幕上的左侧 x 和顶部 y 坐标、PNG 内部的左侧 x 和顶部 y 坐标、要渲染的图片宽度和高度,以及 PNG 文件的整体宽度和高度。
必须指定 PNG 文件的尺寸,这样才能将坐标归一化,获得正确的 UV 值。
第二种 #blit 方法在参数末尾多了一个整数,用于指定要绘制图片的色彩遮罩(tint color)。这个颜色会被视为 ARGB 值,如果未指定则默认为 0xFFFFFFFF。
blitSprite
#blitSprite 是 #blit 的一种特殊实现,用于将纹理绘制到 GUI 纹理图集(texture atlas)上。大多数覆盖在背景之上的纹理,比如熔炉 GUI 中的“燃烧进度”覆盖层,都是精灵(sprite)。所有精灵纹理的位置都是相对于 textures/gui/sprites,并且不需要指定文件扩展名。
// 指向 'assets/examplemod/textures/gui/sprites/container/example_container/example_sprite.png'
private static final ResourceLocation SPRITE = ResourceLocation.fromNamespaceAndPath("examplemod", "container/example_container/example_sprite");
有一组 #blitSprite 方法,其参数与 #blit 相同,只是涉及 PNG 的坐标、宽度和高度的四个整数参数有所不同。
另一组 #blitSprite 方法则接收更多的纹理信息,以便渲染精灵(sprite)的一部分。这些方法会接收纹理的宽度和高度、精灵中的 x 和 y 坐标、屏幕上的左上角 x 和 y 坐标、色彩遮罩(ARGB 格式),以及要渲染的图像宽度和高度。
如果精灵的尺寸与纹理尺寸不一致,那么可以通过三种方式对精灵进行缩放:stretch(拉伸)、tile(平铺)和 nine_slice(九宫格)。stretch 会将图像从纹理尺寸拉伸到屏幕尺寸;tile 会将纹理不断重复绘制,直到填满屏幕尺寸;nine_slice 会将纹理分为一个中心、四条边和四个角,以九宫格方式平铺到目标屏幕尺寸。
这种缩放方式通过在与纹理文件同名的 mcmeta 文件中添加 gui.scaling JSON 对象来设置。
// 针对某个纹理文件 example_sprite.png
// 位于 example_sprite.png.mcmeta
// 拉伸(stretch)示例
{
"gui": {
"scaling": {
"type": "stretch"
}
}
}
// 平铺(tile)示例
{
"gui": {
"scaling": {
"type": "tile",
// 开始平铺的尺寸
// 通常就是纹理的尺寸
"width": 40,
"height": 40
}
}
}
// 九宫格(nine slice)示例
{
"gui": {
"scaling": {
"type": "nine_slice",
// 开始平铺的尺寸
// 通常就是纹理的尺寸
"width": 40,
"height": 40,
"border": {
// 被切分为边框纹理的纹理边距
"left": 1,
"right": 1,
"top": 1,
"bottom": 1
},
// 如果为 true,则纹理的中心部分会像
// stretch 类型一样拉伸,而不是九宫格平铺
"stretch_inner": true
}
}
}