最佳实践
最大化兼容性并最小化UI崩溃风险
JSON-UI与其他所有附加组件系统不同,因为JSON-UI是无版本控制的。您对UI所做的任何更改都可能因Mojang更新和修复JSON-UI系统而失效。幸运的是,您可以采取一些措施来防止UI在Mojang对原版UI进行更改时崩溃。
仅修改必要内容
最小化UI崩溃风险的最有效方法是仅进行您希望进行的更改。例如,如果您只想禁用经验条阴影,您可能会认为应该将此添加到资源包的hud_screen.json文件中。
{
"progress_text_label": {
"type": "label",
"shadow": false,
"text": "#level_number",
"color": "$experience_text_color",
"anchor_from": "top_middle",
"anchor_to": "bottom_middle",
"bindings": [
{
"binding_name": "#level_number",
"binding_type": "global"
},
{
"binding_name": "#level_number_visible",
"binding_type": "global",
"binding_name_override": "#visible"
}
]
}
}这在技术上是正确的,但容易因多种原因崩溃:如果Mojang更改了绑定的名称怎么办?如果他们更改了锚点怎么办?如果Mojang为元素添加了偏移量怎么办?您所做的更改会与原版UI进行策略性合并,因此包含额外的细节是多余的,并且很可能导致您的自定义UI崩溃。只需在元素中包含阴影属性即可避免这种情况:
{
"progress_text_label": {
"shadow": false
}
}这不仅在未来更不容易崩溃,而且看起来更简洁,并大幅减小了文件大小。
通过仅修改必要内容,您可以减少UI中潜在的故障点,从而大大有助于防止自定义UI在原版UI更新时崩溃。最后一点,如果您在资源包中包含原版UI文件的所有内容并进行更改,那么您使用JSON-UI的方式是错误的。
利用修改策略
使用wiki上记录的修改策略是另一种减少Mojang更新UI时发生崩溃更改的好方法。例如,许多附加组件创作者会在HUD中添加元素以显示游戏相关信息。常见的做法是将自定义UI(custom_ui@namespace.custom_ui)合并到hud_screen.json的根面板中。
{
"root_panel": {
"type": "panel",
"$xp_control_offset|default": [ 0, -13 ],
"variables": [
{
"requires": "$education_edition",
"$left_helpers": "hud.left_helpers_edu"
},
{
"requires": "(not $education_edition)",
"$left_helpers": "hud.left_helpers"
}
],
"controls": [
{
"custom_ui@namespace.custom_ui": {} // <--- 人们倾向于在此处添加自定义UI!
},
{ "left_helpers@$left_helpers": {} },
{ "right_helpers@hud.right_helpers": {} },
{ "emote_helpers@hud.emote_helpers": {} },
{ "centered_gui_elements@centered_gui_elements": {} },
{ "centered_gui_elements_at_bottom_middle@centered_gui_elements_at_bottom_middle": {} }
... // 其余控件在此处继续。
]
}
}通过直接将自定义控件与原版根面板合并,您大大增加了未来UI崩溃的几率。例如,如果Mojang将来更改根面板中的控件名称,您的UI可能会引用不存在的或已显著更改的UI元素,这可能导致错误和/或崩溃。
为避免这种情况,请使用修改策略。
{
"root_panel": {
"modifications": [
{
"array_name": "controls",
"operation": "insert_front",
"value": [
{
"custom_ui@namespace.custom_ui": {}
}
]
}
]
}
}使用修改数组进行的修改会与原版UI以及您使用的其他资源包进行策略性合并,而不会更改根面板中的同级控件。这提高了与其他资源包的兼容性,并减少了UI崩溃的可能性。
避免修改嵌套树中的控件
另一个常见的故障点是修改深度嵌套的控件。以下是一个带有嵌套控件的UI元素示例。
{
"label": {
"type": "label",
"text": "hello world",
"color": [1, 1, 1]
},
"bg_image": {
"type": "image",
"texture": "textures/ui/Black",
"alpha": 0.7
},
"panel_with_label_and_bg": {
"type": "panel",
"size": ["100%c", "100%c"],
"controls": [
{
"bg_image@bg_image": {
"size": ["100%c + 2px", "100%c + 2px"],
"controls": [
{
"label@label": {
"layer": 5
}
}
]
}
}
]
}
}为了最好地修改此UI,您应尽可能避免嵌套树。例如,如果您想将标签颜色从白色修改为灰色,并将背景图像设置为透明,请在元素定义上修改颜色和不透明度,而不是在树中(记住只修改必要的内容!):
{
"label": {
"color": [0.5, 0.5, 0.5]
},
"bg_image": {
"alpha": 0
}
}然而,有时无法避免修改树。在这种情况下,您应使用以下语法来定位嵌套树中的特定控件。例如,要修改背景图像大小和标签层,请使用以下语法。
{
"panel_with_label_and_bg/bg_image": {
"size": ["100%c", "100%c"]
},
"panel_with_label_and_bg/bg_image/label": {
"layer": -5
}
}/将定位指定元素的子控件。请注意,如果指定的目标子控件名称不存在,将导致资源包错误。您的UI将正常运作,但最好尽可能避免定位嵌套树中的控件。
使用单一入口点
为了将自定义UI添加到特定屏幕,UI需要在某一点与原版UI合并。这称为入口点,最佳实践是尽可能让您的自定义UI在单一入口点与原版UI合并,以减少UI崩溃的可能性。以下是在hud_screen.json中使用2个入口点的示例:
{
"root_panel": {
"modifications": [
{
"array_name": "controls",
"operation": "insert_front",
"value": [
{
"custom_ui_control_1@namespace_1.custom_ui_control_1": {}
}
]
}
]
},
"hud_content": {
"modifications": [
{
"array_name": "controls",
"operation": "insert_front",
"value": [
{
"custom_ui_control_2@namespace_2.custom_ui_control_2": {}
}
]
}
]
}
}我们可以将入口点减少到一个,并将custom_ui_control_1和custom_ui_control_2合并到hud_content或root_panel控件中,方法如下:
{
"root_panel": {
"modifications": [
{
"array_name": "controls",
"operation": "insert_front",
"value": [
{
"custom_ui_control_1@namespace_1.custom_ui_control_1": {}
},
{
"custom_ui_control_2@namespace_1.custom_ui_control_2": {}
}
]
}
]
}
}使用单一入口点减少了UI崩溃的可能性,因为如果Mojang更新了hud_content控件名称,某些自定义UI可能会崩溃。使用单一入口点还使您的UI更易于调试,因为您只需要考虑一个入口点。
避免在原版命名空间中工作
如果您正在修改UI的大部分内容或添加大量自定义UI,应尽可能避免在原版命名空间文件中工作。您可以通过在UI定义文件中添加具有唯一命名空间的自定义UI文件来实现这一点。请记住,当您需要将UI合并到入口点时,可以在控件定义中使用element@namespace.element语法引用其他命名空间中的元素。通过在自定义命名空间中添加自定义UI,您可以减少与原版控件名称冲突的可能性,这可能会导致问题。此外,与大多数其他附加组件系统一样,命名空间可以支持前缀,例如wiki:namespace,可以引用为element@wiki:namespace.element。前缀还可以帮助避免与原版命名空间冲突。
最大化性能
就FPS而言,JSON-UI是仅次于实体的第二昂贵的附加组件子系统。您是否曾想过为什么打开库存会使FPS减半?简短的答案是JSON-UI非常未优化,导致FPS降低。添加大量自定义UI可能会显著增加开销,导致游戏中持续帧率降低、屏幕加载时间长以及整体用户体验差。
最小化UI中的运算符数量
运算符在UI中用于评估条件、执行数学运算和修改字符串。这些运算符对于条件渲染等技术非常有用,但使用这些会增加大量开销。例如,如果您有一个变量"$var": "(2 * (-1 * $number))",将其简化为"$var": "(-2 * $number)"会显著提高性能。最好尽可能简化表达式,并删除不必要的表达式,以尽可能加快评估时间。
最小化UI中的绑定数量
与运算符类似,使用许多绑定也会显著增加开销。设置屏幕打开时间长的部分原因是所有切换和选项都链接到特定的绑定,而这些绑定很多。删除不贡献功能的绑定或可以简化的绑定是提高性能的另一种好方法。
避免添加不必要的控件
也许提高JSON-UI性能的最佳方法是删除未使用或不必要的控件。在此示例中,子控件panel不需要,因为它是一个空面板。
{
"element": {
"type": "image",
"texture": "textures/ui/Black",
"controls": [
{
"panel": {
"type": "panel"
}
},
{
"label": {
"type": "label",
"text": "hello world"
}
}
]
}
}要解决此问题,您可以从UI树中删除它,或在panel控件中添加"ignored": true。使用"ignored": true"与删除它相同。控件及其所有子控件将不会在UI中评估,从而获得与根本不存在相同的性能。使用"visible": false不会产生相同的效果,控件仍将被评估。
{
"element": {
"type": "image",
"texture": "textures/ui/Black",
"controls": [
{
"label": {
"type": "label",
"text": "hello world"
}
}
]
}
}{
"element": {
"type": "image",
"texture": "textures/ui/Black",
"controls": [
{
"panel": {
"type": "panel",
"ignored": true
}
},
{
"label": {
"type": "label",
"text": "hello world"
}
}
]
}
}有时,您可以将多个控件简化和合并为一个元素。例如,如果您想根据#hud_title_text_string为特定数字1-5渲染特定图像,您可能会认为应添加5个单独的控件以分别评估:
{
"image_template": {
"type": "image",
"texture": "$texture",
"bindings": [
{
"binding_name": "#hud_title_text_string"
},
{
"binding_type": "view",
"source_property_name": "(#hud_title_text_string = $binding_text)",
"target_property_name": "#visible"
}
]
},
"image_1@image_template": {
"$texture": "textures/ui/example_1",
"$binding_text": "1"
},
"image_2@image_template": {
"$texture": "textures/ui/example_2",
"$binding_text": "2"
},
"image_3@image_template": {
"$texture": "textures/ui/example_3",
"$binding_text": "3"
},
"image_4@image_template": {
"$texture": "textures/ui/example_4",
"$binding_text": "4"
},
"image_5@image_template": {
"$texture": "textures/ui/example_5",
"$binding_text": "5"
}
}通过更周到的设计,可以将其显著简化为具有更少运算符、绑定和控件的单个控件。
{
"image": {
"type": "image",
"texture": "#texture",
"bindings": [
{
"binding_name": "#hud_title_text_string"
},
{
"binding_type": "view",
"source_property_name": "(((#hud_title_text_string * 1) > 0) and ((#hud_title_text_string * 1) < 6))",
"target_property_name": "#visible"
},
{
"binding_type": "view",
"source_property_name": "('textures/ui/example_' + #hud_title_text_string)",
"target_property_name": "#texture"
}
]
}
}总的来说,为了提高UI的性能,重要的是要准确理解您要做什么,并尽可能根据这些一般准则进行调整。单个控件未优化可能不会产生明显的差异。但是,当您开始留下许多未优化的控件时,性能可能会在一个已经未优化的UI系统上开始受到影响。



