【HZ/ICMod開發(fā)教程】2 - UI PART2
注意:本教程僅為簡單介紹UI的窗口,不會涉及詳細的函數。如果想要了解更多請閱讀官方文檔。
在上期教程中我們簡要介紹了ICMod的UI構成與基本控件,接下來將詳細介紹控件用法以及如何組成一個“精美”的界面。
StandardWindow
在此糾正上一篇專欄的嚴重錯誤:StandartWindow在現在版本的InnerCore中已經棄用,使用StandardWindow替代。
StandardWindow
是一個由三部分組成的WindowGroup
,分別為main
, inventory
和header
,對應關系如下圖:

在此使用工業(yè)的火力發(fā)電機界面作為示例:

這是一個簡單的界面,但基本上包含了常用控件,讓我們簡要分析一下其代碼:
//源碼使用TypeScript編寫且分不同部分在多個文件中,為方便講解取主要代碼并使用JavaScript重寫
const GUI_SCALE = 3.2; //聲明GUI_SCALE常量,以便統一模組內各界面
new UI.StandardWindow({
? ? standard: {
? ? ? ? header: {text: {text: Translation.translate("Generator")}}, //此處使用Translate類函數以實現多語言,詳見官方文檔
? ? ? ? inventory: {standard: true},
? ? ? ? background: {standard: true}
? ? },
? ? drawing: [//兩個scale元素對應的當其為空時的貼圖
? {type: "bitmap", x: 530, y: 144, bitmap: "energy_bar_background", scale: GUI_SCALE},
? {type: "bitmap", x: 450, y: 150, bitmap: "fire_background", scale: GUI_SCALE},
?],
? ? elements: {
? "energyScale": {type: "scale", x: 530 + GUI_SCALE * 4, y: 144, direction: 0, value: 0.5, bitmap: "energy_bar_scale", scale: GUI_SCALE}, //注意此處x的值比對應的bitmap加了GUI_SCALE*4,這是因為它的貼圖兩端各少了4個像素,以便完整顯示進度
? "burningScale": {type: "scale", x: 450, y: 150, direction: 1, value: 0.5, bitmap: "fire_scale", scale: GUI_SCALE},
? "slotEnergy": {type: "slot", x: 441, y: 75},
? "slotFuel": {type: "slot", x: 441, y: 212},
? "textInfo1": {type: "text", x: 642, y: 142, width: 300, height: 30, text: "0/"},
? "textInfo2": {type: "text", x: 642, y: 172, width: 300, height: 30, text: "10000"}
?}
});
可以看出在StandardWindow中使用的是絕對坐標,而且可與貼圖像素對應,因此只需要耐心的慢慢調整就能夠得到一個不錯的界面。
與方塊實體互動
此部分不是本教程的主要內容,因此將簡要帶過。
在IC中,注冊方塊實體的函數為TileEntity.registerPrototype(blockID: number, customPrototype: TileEntityPrototype): void
,參數只有兩個:方塊ID
和方塊實體原型
。
方塊實體原型是一個JS對象,包含方塊實體的數據和事件。要將創(chuàng)建的StandardWindow對象與方塊實體綁定則需要在方塊實體原型中設置getGuiScreen
函數并返回界面對象。這樣IC會自動為該界面對象創(chuàng)建相應的容器對象,你可以在方塊實體原型的函數中使用this.container
來獲得容器對象并使用其提供的方法與界面交互。
更進一步
StandardWindow的可玩性遠不止如此,你可以試著除去StandardWindow的默認控件,并從0開始自定義界面,或者在方塊實體原型的getGuiScreen
方法中編寫邏輯以使在不同的情況下打開不同的界面。
示例為我去年3月份初次嘗試UI時所寫的會根據MC設置的UI檔案切換StandardWindow的方塊實體(PS:圖二的界面標題有偏移Bug,在之后的版本中已被修復)。整個界面從開始構思到完成大概用了5天時間(大部分時間用在編寫庫上,但如今得益于WindowGroup,可以更快地完成該任務。


Window
Window
是最基本的窗口,相較于StandardWindow,Window更為靈活,適合用作彈窗或者HUD。
在創(chuàng)建一個Window對象的時候,可以像StandardWindow那樣傳入一個包含Drawing和Elements等的對象。但能夠突出Window特色的是location參數,你可以自定義Window的大小,在屏幕上的位置,內邊距和可滑動窗口大?。?span id="s0sssss00s" class="md-pair-s ">PS:Window內的unit
為此Window寬度的千分之一)。
值得一提,內邊距padding
的會覆蓋x|y|width|height
的效果。
舉個簡單的空白窗口界面的例子:
?
//定義一些常量
const GUI_SCALE = 5;
const WIDTH = 1000;
const HEIGHT = UI.getScreenHeight();
//創(chuàng)建窗口
let testWindow = new UI.Window({
? ? location: { //此處運算是為方便居中
? ? ? ? x: (WIDTH - 300) / 2,
? ? ? ? y: (HEIGHT - 225) / 2,
? ? ? ? width: 300,
? ? ? ? height: 225
? ? },
? ? drawing: [ //貼圖是從拆原版包扒的XD
? ? ? ? {type: "background", color: android.graphics.Color.TRANSPARENT},
? ? ? ? {type: "frame", bitmap: "background_panel", width: 1000, height: 750, scale: GUI_SCALE}
? ? ],
? ? elements: {
? ? ? ? "closeButton": {type: "closeButton", x: 904, y: 26, bitmap: "close_button_default", bitmap2: "close_button_pressed", scale: GUI_SCALE}
? ? }
});
//一些常規(guī)設置
testWindow.setCloseOnBackPressed(true);
testWindow.setBlockingBackground(true);
效果圖:

TabbedWindow
TabbedWindow
可用于創(chuàng)建標簽式窗口,如原版的玩家背包一樣,具有多個可切換的標簽頁。
TabbedWindow和StandardWindow一樣,都繼承自WindowGroup。在創(chuàng)建TabbedWindow對象時,所傳入的參數與Window相同,與之不同的是TabbedWindow多了一些函數。
使用new UI.TabbedWindow()
創(chuàng)建一個空白的TabbedWindow,效果如下(PS:部分異形屏可能會被遮擋部分邊界):

簡單地說明一下這個窗口,左上角的關閉按鈕是一個索引值為0的FakeTab
(PS:FakeTab指沒有對應標簽頁的標簽),標簽頁的上限為12個,即索引值的范圍為0-11
(左側為0-5
,右側為6-11
)。
要添加一個標簽頁可以使用setTab(index: number, tabOverlay: ElementSet, tabContent: WindowContent, isAlwaysSelected?: boolean): void
函數,index
即標簽頁的索引值,tabOverlay
是標簽的元素集(如關閉按鈕),tabContent即為標簽頁展示的窗口內容,格式與之前介紹的基本無異。
因為setTab函數并不返回創(chuàng)建的Window對象,所以你需要用到getWindowForTab(index: number): Window
來函數獲取對應索引值的Window實例化對象,以完成對其的動態(tài)修改。
WindowGroup
WindowGroup
是StandardWindow和TabbedWindow的父類,其方法基本上都可以在StandardWindow和TabbedWindow中使用。
WindowGroup誕生的目的是為了能夠將復雜的界面模塊化,以便提高代碼的復用率并降低調試難度,使開發(fā)效率提高,最為典型的例子就是IC中的工作臺界面(PS:此處不談其交互邏輯,僅談其界面)。

該界面由三部分組成,分別為Main
·,Slots
和Grid
,對應下圖中的青色,粉色和黃色部分:

閱讀workbench.js
中工作臺界面部分的代碼,可以看出該三部分都是先定義WindowContent的JSON描述,然后一一創(chuàng)建Window對象,并使用addWindowInstance(name: string, window: Window): void
函數將Window對象添加到WindowGroup對象并聲明其對應的名稱ID。你也可以直接使用addWindow(name: string, content: WindowContent): Window
函數向WindowGroup添加窗口。

至此,關于UI界面的內容就基本上介紹完畢,當然全部內容不止這些,你需要認真地查閱官方文檔以及去學習他人的優(yōu)秀作品。 另外,雖然本教程對于UI與容器的互動只是簡單地帶過,但此部分內容是相當重要的,應當注重學習(PS:誰讓你們當時評論不提容器和方塊實體呢XD)。