Add-On開發教學 #6 合成台方塊自訂UI

    前言

    這篇文章介紹如何透過方塊來打開自訂UI的畫面。

    基岩版不使用Scripting API的話,目前的UI都是修改原版居多,而且是透過資源包來修改。

    第一個例子,Dewdimpple製作的Custom Crafting Table加入了鍋爐的新方塊,開啟修改過的合成台UI,可以用來烤雞肉。實際上它還是九宮格,只是透過自訂UI把其他格子遮住,而且在合成表中用crating_tag來限定只能在這個合成台中操作。


    再來看dakonblackrose製作的Readable BookShelf,打開這個書櫃後可以看原神的漫畫。


    這二個原作者的檔案我做了些修正,使之能共存。文章最後可以下載我修改過的版本。


    原理

    透過方塊添加開啟自訂合成台的組件,接著在原版合成台的檔案中,塞入自己做的UI,並跟原版合成台UI共存。

    於是就樣就給了我們一個突破口:不一定要做成合成台的介面,但一樣能用這個方法來叫出自訂UI。

    在開始之前,建議看這部影片,你會對JSON的UI有個基本概念。


    實作

    這裡不做合成台,只做一個像是第二個漫畫例子的UI,這邊要做一個資訊看板。

    影片版教學:


    1.首先要在行為包新增一個叫做"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裡面給它設定多國語言。


    2.切換到資源包

    新增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
      ]
    }


    3.然後從原版Minecraft資源包複製這二個檔案:inventory_screen.jsoninventory_screen_pocket.json

    一個是電腦版背包界面,一個是手機版的界面,要修改的地方大同小異。


    4.開啟inventory_screen.json,刪除到只剩下crafting_screen,在裡面插入要開啟的UI。modification,裡面用插入controls陣列的方式來加入UI。

    infoui@infoui.final_panel,前面的info可以想成只是個變數,後面infoui.final_panel才是實際有功用/要呼叫的面板。

    資源包/ui/inventory_screen.json

    {
    	"namespace": "crafting",
    	//Insert new UI into crafting screen "controls" array
    	"crafting_screen@crafting.inventory_screen_base": {
    		"modifications": [
    			{
    				"array_name": "controls",
    				"operation": "insert_back",
    				"value": {
    					//Your custom ui
    					"infoui@infoui.final_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": {
    					"vanilla_crafting_screen@crafting.inventory_screen_base": {
    						"bindings": [
    							{
    								"binding_name": "#crafting_label_text"
    							},
    							{
    								"binding_type": "view",
    								"source_property_name": "(#crafting_label_text = container.crafting)",
    								"target_property_name": "#visible"
    							}
    						]
    					}
    				}
    			}
    		]
    	}
    }


    5.除此之外,還要設定binding name (14~22行),填入剛剛在行為包設定的custom_description,以UI的文字當作顯示的依據,這樣它才只會在呼叫時顯示,避免跟其他Add-On以及原版的合成台重疊。

    6.再來把原版的合成台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
              "infoui@infoui.final_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": {
              "vanilla_crafting_screen@crafting.inventory_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"
                  }
                ]
              }
            }
          }
        ]
      }
    }


    7.新增一個檔案 infoui.json,這個就是自己做的UI檔案,上面有一個專有的namespace,也是用來避免重複。

    資源包/ui/infoui.json

    /*
     infoui.final -> main_panel -> scrolling -> homescreen
    */
    {
      "namespace": "infoui", //Unique namspace
      /*
      Elements
      */
      //Background image
      "background_image@horse.overlay_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
      */
      "scrolling@common.scrolling_panel": {
        "$scrolling_content": "infoui.homescreen",
        "$show_background": false,
        "anchor_from": "center",
        "anchor_to": "center"
      },
      "main_panel@common.root_panel": {
        "layer": 1,
        "size": [
          "90%",
          "90%"
        ],
        "controls": [
          {
            "common_panel@common.common_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"
          }
        ]
      }
    }


    8.剩下的就是往UI裡面塞入元素了。

    載入UI最先呼叫的就是404行的final_panel,同樣也給它設定binding name

    final_panel -> main_panel -> scrolling -> homescreen

    因為這是一個捲動的視窗,在360~401行我設計了背景,以及將捲動的內容限縮在背景內。接著在16~350行填入文字和圖片,文字的部份還要在資源包/texts/en_US.lang設定實際顯示的文字內容。

    一些已知的屬性:

    • source_property_name用於呼叫UI
    • layer用於重疊,越小越後面
    • size可用數字或100%、px來決定大小


    9.這樣UI就完成了。

    這個UI的效果主要是給玩家查詢Add-On使用方法,我把mcpedl上寫的文章放進來,UI裡面含有文字和圖片,可捲動。


    這個UI有個小問題,crafting screen的插入方法,如果多個Add-On都這樣寫,會導致原版合成台的介面重複顯示。目前我還沒想到解決方法,如果有請跟我說吧。


    關於UI的部分還有很多可以學習的地方,但如果你問我為什麼這樣那樣寫不行,我應該會說:

    啊哈哈,佐佑理不清楚。


    範本檔案下載

    內含三個Add-On,鍋爐Custom Crafting Table UI、原神漫畫UI,以及本文的範例。

    下載網址

    也可於Github檢視原始碼。

    留言

    此網誌的熱門文章

    Minecraft基岩版多人連線教學: 方法一覽

    【詳細解說】什麼是Minecraft基岩版,跟Java版有什麼差?

    Minecraft基岩版安裝模組(Add-On)&資源包&光影教學