1. 前言#
這篇文章介紹如何透過方塊來打開自訂UI的畫面。
基岩版不使用Scripting API的話,目前的UI都是修改原版居多,而且是透過資源包來修改。
第一個例子,Dewdimpple製作的 Custom Crafting Table加入了鍋爐的新方塊,開啟修改過的合成台UI,可以用來烤雞肉。實際上它還是九宮格,只是透過自訂UI把其他格子遮住,而且在合成表中用crating_tag來限定只能在這個合成台中操作。
再來看dakonblackrose製作的 Readable BookShelf,打開這個書櫃後可以看原神的漫畫。
這二個原作者的檔案我做了些修正,使之能共存。文章最後可以下載我修改過的版本。
2. 原理#
透過方塊添加開啟自訂合成台的組件,接著在原版合成台的檔案中,塞入自己做的UI,並跟原版合成台UI共存。
於是就樣就給了我們一個突破口:不一定要做成合成台的介面,但一樣能用這個方法來叫出自訂UI。
在開始之前,建議看AgentMindStorm的這部影片,你會對JSON的UI有個基本概念。
3. 實作#
這裡不做合成台,只做一個像是第二個漫畫例子的UI,這邊要做一個資訊看板。
影片版教學:
- 首先要在行為包新增一個叫做"Info Block"的方塊。
這裡主要在行為包中添加組件"minecraft:crafting_table"
,設定合成表,目前只能3x3。然後再設定UI的custom_description
,就把它想成這個UI的唯一代號。
customUI_B/blocks/info_block.json
內容:
//...
"minecraft:crafting_table": {
"custom_description": "container.newblock.info", // Name shown in GUI, you will use this name in the ui
"grid_size": 3, // Currently only supports 3
"crafting_tags": [
"info"
]
},
//...
custom_description
是字串形式,如果寫成mygui. xxx
,你就可以在資源包*texts
裡面給它設定多國語言。
- 切換到資源包
新增UI要先在_ui_defs.json
聲明自製UI檔案的位置,通常是在最前面插入。
資源包/ui/_ui_defs.json
內容:
{
// Alphabetical order please :)
"ui_defs": [
"ui/infoui.json" //file name must be unique to avoid conflicts with other addons that also uses this
]
}
- 然後從原版Minecraft資源包複製這二個檔案:
inventory_screen.json
和inventory_screen_pocket.json
。
一個是電腦版背包界面,一個是手機版的界面,要修改的地方大同小異。
- 開啟
inventory_screen.json
,刪除到只剩下crafting_screen
,在裡面插入要開啟的UI。modification
裡面用插入controls
陣列的方式來加入UI。
[email protected]_panel
,前面的info
可以想成只是個變數,後面infoui.final_panel
才是實際有功用/要呼叫的面板。
資源包/ui/inventory_screen.json
內容:
{
"namespace": "crafting",
//Insert new UI into crafting screen "controls" array
"[email protected]_screen_base": {
"modifications": [
{
"array_name": "controls",
"operation": "insert_back",
"value": {
//Your custom ui
"[email protected]_panel": {
//Bindings, to avoid conflicts with other custom UI Add-Ons.
"bindings": [
{
"binding_name": "#crafting_label_text"
},
{
"binding_type": "view",
"source_property_name": "(#crafting_label_text = 'container.newblock.info')",
"target_property_name": "#visible"
}
]
}
}
},
//Use this to make sure the vanilla crafting table will not be overwritten. However, if multiple Add-On use same names, many dulpicated crafting screens will be created :( Minecraft will show you warnings, too.
//Therefore, "vanilla crafting table" and "crafting_screen" should be unique, too. But "crafting_screen" is required, so this problem is unsolved.
{
"array_name": "controls",
"operation": "insert_back",
"value": {
"[email protected]_screen_base": {
"bindings": [
{
"binding_name": "#crafting_label_text"
},
{
"binding_type": "view",
"source_property_name": "(#crafting_label_text = container.crafting)",
"target_property_name": "#visible"
}
]
}
}
}
]
}
}
除此之外,還要設定
binding name
(14~22行),填入剛剛在行為包設定的custom_description
,以UI的文字當作顯示的依據,這樣它才只會在呼叫時顯示,避免跟其他Add-On以及原版的合成台重疊。再來把原版的合成台UI加回來(28~45行),否則合成台會空白一片。
資源包/ui/inventory_screen_pocket.json
檔案亦同:
/*
Similar to inventory_screen.json
*/
{
"namespace": "crafting_pocket",
"crafting_screen_pocket@crafting_pocket.inventory_screen_pocket_base": {
"modifications": [
{
"array_name": "controls",
"operation": "insert_back",
"value": {
//Your custom ui
"[email protected]_panel": {
//Bindings, to avoid conflicts with other custom UI Add-Ons.
"bindings": [
{
"binding_name": "#crafting_label_text"
},
{
"binding_type": "view",
"source_property_name": "(#crafting_label_text = 'container.newblock.info')",
"target_property_name": "#visible"
}
]
}
}
},
{
"array_name": "controls",
"operation": "insert_back",
"value": {
"[email protected]_screen_base": {
"$screen_content": "crafting.recipe_inventory_screen_content",
"$screen_bg_content": "common.screen_background",
"$top_half_variant": "crafting.crafting_panel_top_half",
"bindings": [
{
"binding_name": "#crafting_label_text",
"binding_type": "global"
},
{
"binding_type": "view",
"source_property_name": "(#crafting_label_text = container.crafting)",
"target_property_name": "#visible"
}
]
}
}
}
]
}
}
- 新增一個檔案
infoui.json
,這個就是自己做的UI檔案,上面有一個專有的namespace,也是用來避免重複。
資源包/ui/infoui.json
內容:
/*
infoui.final -> main_panel -> scrolling -> homescreen
*/
{
"namespace": "infoui", //Unique namspace
/*
Elements
*/
//Background image
"[email protected]_image": {
"texture": "textures/ui/background",
"alpha": 1
},
//UI Contents
"homescreen": {
"type": "stack_panel",
"offset": [
0,
0
],
//Headers, images and texts. Text + Image
//Use size y to control line spacing
"controls": [
{
"row0": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
50
],
"controls": [
{
"header_0@how_to_play_common.header": {
"$text": "newblock.info.header1"
}
}
]
}
},
{
"row00": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
50
],
"controls": [
{
"paragraph_1@how_to_play_common.paragraph": {
"$text": "newblock.info.text0"
}
}
]
}
},
{
"row1t": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
10
],
"controls": [
{
"paragraph_1@how_to_play_common.paragraph": {
"$text": "newblock.info.text1"
}
}
]
}
},
{
"row1": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
250
],
"controls": [
{
"1": {
"type": "image",
"texture": "textures/ui/info1"
}
}
]
}
},
{
"row2t": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
10
],
"controls": [
{
"paragraph_1@how_to_play_common.paragraph": {
"$text": "newblock.info.text2"
}
}
]
}
},
{
"row2": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
250
],
"controls": [
{
"1": {
"type": "image",
"texture": "textures/ui/info2"
}
}
]
}
},
{
"row3t": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
10
],
"controls": [
{
"paragraph_1@how_to_play_common.paragraph": {
"$text": "newblock.info.text3"
}
}
]
}
},
{
"row3": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
250
],
"controls": [
{
"1": {
"type": "image",
"texture": "textures/ui/info3"
}
}
]
}
},
{
"row4t": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
10
],
"controls": [
{
"paragraph_1@how_to_play_common.paragraph": {
"$text": "newblock.info.text4"
}
}
]
}
},
{
"row4": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
250
],
"controls": [
{
"1": {
"type": "image",
"texture": "textures/ui/info4"
}
}
]
}
},
{
"row5t": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
10
],
"controls": [
{
"paragraph_1@how_to_play_common.paragraph": {
"$text": "newblock.info.text5"
}
}
]
}
},
{
"row5": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
250
],
"controls": [
{
"1": {
"type": "image",
"texture": "textures/ui/info5"
}
}
]
}
},
{
"row6t": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
10
],
"controls": [
{
"paragraph_1@how_to_play_common.paragraph": {
"$text": "newblock.info.text6"
}
}
]
}
},
{
"row6": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
250
],
"controls": [
{
"1": {
"type": "image",
"texture": "textures/ui/info6"
}
}
]
}
},
{
"row7t": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
10
],
"controls": [
{
"paragraph_1@how_to_play_common.paragraph": {
"$text": "newblock.info.text7"
}
}
]
}
},
{
"row7": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
250
],
"controls": [
{
"1": {
"type": "image",
"texture": "textures/ui/info7"
}
}
]
}
},
{
"row8t": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
20
],
"controls": [
{
"paragraph_1@how_to_play_common.paragraph": {
"$text": "newblock.info.text8"
}
}
]
}
},
{
"row8": {
"type": "stack_panel",
"orientation": "horizontal",
"size": [
"100%",
250
],
"controls": [
{
"1": {
"type": "image",
"texture": "textures/ui/info8"
}
}
]
}
}
]
},
/*
UI
*/
"[email protected]_panel": {
"$scrolling_content": "infoui.homescreen",
"$show_background": false,
"anchor_from": "center",
"anchor_to": "center"
},
"[email protected]_panel": {
"layer": 1,
"size": [
"90%",
"90%"
],
"controls": [
{
"[email protected]_panel": {
"offset": [
0,
0
],
"$show_close_button": true
}
},
//Background
{
"bg@background_image": {
"layer": 2,
"size": [
"90%",
"95%"
],
"offset": [
0,
0
]
}
},
//UI contents
{
"sg@scrolling": {
"size": [
"85%",
"90%"
],
"layer": 3
}
}
]
},
//Final panel to show
//use lang file to change your container title
"final_panel": {
"type": "panel",
"controls": [
{
"main@main_panel": {}
}
],
"bindings": [
{
"binding_name": "#crafting_label_text"
},
{
"binding_type": "view",
"source_property_name": "(#crafting_label_text = 'container.newblock.info')", //custom_description in the behavior
"target_property_name": "#visible"
}
]
}
}
- 剩下的就是往UI裡面塞入元素了。
載入UI最先呼叫的就是404行的final_panel
,同樣也給它設定binding name
。
順序就是:final_panel -> main_panel -> scrolling -> homescreen
因為這是一個捲動的視窗,在360~401行我設計了背景,以及將捲動的內容限縮在背景內。接著在16~350行填入文字和圖片,文字的部份還要在資源包/texts/en_US.lang
設定實際顯示的文字內容。
一些已知的屬性:
source_property_name
用於呼叫UIlayer
用於重疊,越小越後面size
可用數字或100%、px來決定大小
- 這樣UI就完成了。
這個UI的效果主要是給玩家查詢Add-On使用方法,我把mcpedl上寫的文章放進來,UI裡面含有文字和圖片,可捲動。
這個UI有個小問題,crafting screen
的插入方法,如果多個Add-On都這樣寫,會導致原版合成台的介面重複顯示。目前我還沒想到解決方法,如果有請跟我說吧。
關於UI的部分還有很多可以學習的地方,但如果你問我為什麼這樣那樣寫不行,我應該會說:
啊哈哈,佐佑理不清楚。
範本檔案下載#
內含三個Add-On,鍋爐Custom Crafting Table UI、原神漫畫UI,以及本文的範例。
也可於 Github檢視原始碼。