FairyGUI中常用的方法,對比UGUI
素材管理
可以直接將圖片(Gif)、聲音(mp3)、動畫、文字等素材從資源瀏覽器拖動到庫中。
資源Url
UIPackage.getItemURL(“包名“,“資源名”)
AudioClip clip = (AudioClip)UIPackage.GetItemAsset(“包名稱”,”聲音名稱”);
選單”編輯”->“建立點陣圖字型”,(輸入字元,顯示對應圖片)
多資訊文字(GRichTextField)支援連結和圖文混排。
用例: aTextField.text = “請去找王大錘”;
列表 (GList)
AddChild : 增加一個專案。
AddChildAt :在指定的位置增加一個專案。
RemoveChild : 刪除一個專案。
RemoveChildAt : 刪除一個指定位置的專案。
RemoveChildren : 刪除一個範圍內的專案,或者全部刪除。
GList內建了物件池。
使用物件池後的方法:
AddItemFromPool : 從池裡取出(如果有)或者新建一個物件,新增到列表中
GetFromPool : 從池裡取出(如果有)或者新建一個物件
ReturnToPool : 將物件返回池裡
RemoveChildToPool : 刪除一個列表專案,並將物件返回池裡
RemoveChildToPoolAt : 刪除一個指定位置的專案,並將物件返回池裡
RemoveChildrenToPool:刪除一個範圍內的專案,或者全部刪除,並將刪除的物件都返回池裡
新增物件時不使用池,物件池將不斷增大。
正確的做法:從池中建立物件。即使用AddItemFromPool或GetFromPool。
虛擬列表 GList.SetVirtual
使用GList.numItems設定列表專案的數量。注意與GList.numChildren區分,後者是當前列表容器的顯示物件數量。
但要注意顯示物件和列表專案的數量在數量上和順序上都是不一致的,也就是GetChildAt(0)獲得的顯示物件並不等於列表的第一條專案。
列表滾動到目標位置(第500個),呼叫GList.AddSelection(500)
虛擬列表只能通過numItems改變列表專案的數量,不允許使用AddChild或RemoveChild增刪物件。例如如果要清空列表,必須要通過設定numItems=0,而不是RemoveChildren。
迴圈列表 GList.SetVirtualAndLoop()。
GList.ensureBoundsCorrect()通知GList立刻重排。(手動觸發重新排列)
裝載器 GLoader
執行時設定裝載器內容的方式是:
aLoader.url = “ui://xxxxx”;
aLoader.url = “demo/aimage”; //這裡載入的是路徑為Assets/Resources/demo/aimage的一個貼圖
例如,你希望從AssetBundle中獲取資源,那麼你可以擴充套件GLoader。首先編寫你的Loader類,例如:
這是一個egret中使用到的自定義loader,使用getResByUrl這個API載入外部資源。
然後我們就可以用fairygui.UIObjectFactory.setLoaderExtension(MyGLoader);註冊我們要使用的Loader類。註冊完成後,遊戲中所有裝載器都是由MyGLoader例項化產生。
元件
點選穿透 預設情況下,元件的矩形區域將攔截點選,勾選後,點選事件可以穿透元件中沒有內容的空白區域。
控制器 Controller
Controller c1 = aComponent.GetController(“c1”)
c1.selectedIndex = 1;//1是頁面索引
或
c1.selectedPage = ‘pageName’;//pageName就是頁面名稱
說明:
對所有參與的頁面,元件將分別儲存不同的座標值;對所有沒有參與的頁面,元件將儲存同一個座標值。
緩動功能 (位移的插值?)
按鈕控制器 Button
單選組(RadioGroup)
在程式中,要獲得或設定哪個按鈕被選中也非常簡單,使用控制器的selectedIndex或者selectedPage就可以了。
設定關聯 ( 類似錨點,相對位置的設定 )
這裡有一個垂直中線的關聯,下面會說明,暫且不表。置可以看到,在文字內容發生變化時,文字的中線位置沒有發生變化,位所以問號圖示的置不變;但文字的右側位發生了變化,所以歎號圖示隨之發生了移動,保持了與文字右側的距離。
在程式碼中設定關聯
(比如瀏覽器視窗被玩家拖大拖小),元件依然保持在右側位置,那麼可以這樣呼叫:
aComoponet.addRelation(GRoot.inst, RelationType.Right_Right);
又例如希望一個動態新增到舞臺的元件始終保持滿屏大小,可以這樣呼叫
aComoponet.setSize(GRoot.inst.width, GRoot.inst.height);
aComoponet.addRelation(GRoot.inst, RelationType.Size);
RelationType.Size相當於RelationType.Width_Width和RelationType.Height_Height的組合。這裡強調一下,使元件變為滿屏大小這個操作必須由你完成,也就是上面程式碼中的setSize呼叫。關聯並不能完整這項任務,因為關聯是不管元件當前的大小的,它只會在目標變化時保持兩者大小的差別。
A:和容器元件”垂直中線“關聯
B:和容器元件”右->右”關聯
C:和容器元件“右->右”,”底->底“關聯
D:和容器元件”底->底”,“垂直中線%”關聯
E:和容器元件“底->底”關聯
然後執行時把這個介面設定為滿屏就可以了。
aComponent.SetSize(GRoot.inst.width, GRoot.inst.height);
aComoponet.addRelation(GRoot.inst, RelationType.Size);//當螢幕改變時仍然保持全屏
FairyGUI教程:(九)元件的擴充套件
FairyGUI提供的控制元件有:
圖片(GImage)、動畫(GMovieClip)、圖形(GGraph)、文字(GTextField)、多資訊文字(GRichTextFIeld)、列表(GList)、裝載器(GLoader)、元件(GComponent)。這些都是非常基礎的控制元件,他們是UI製作中的最小粒度。但僅靠這些基礎控制元件是不足以製作出各種複雜的UI介面的。我們通過”擴充套件“功能,從元件(GComponent)中擴展出來以下這些複合元件:按鈕(GButton),標籤(GLabel),下拉框(GComboBox),進度條(GProgressBar),滑動條(GSlider),滾動條(GScrollbar)。
可以看到有六種“擴充套件”選擇。元件可以隨意在這些“擴充套件”中切換。選擇哪種“擴充套件”,元件就有了哪個擴充套件的屬性和行為特性。
指定統一點選音效
如果希望遊戲中所有按鈕的點選都有聲音反饋,你並不需要每個按鈕都設定一次聲音屬性,因為在遊戲中可以使用程式碼UIConfig.buttonSound=’xxx’為所有按鈕設定一個點選聲音。
滾動條
執行時使用的滾動條需要通過以下程式碼設定:
UIConfig.horizontalScrollBar = ‘xxx’;
UIConfig.verticalScrollBar = ‘yyy’;
這裡xxx和yyy就是滾動條資源的url。
UIConfig.defaultScrollBarDisplay = ScrollBarDisplayType.Auto; // 設定為,滾動時顯示滾動條
9、自定義擴充套件
當基礎元件、擴充套件元件都不能滿足你的需求時,你可以編寫自定義的擴充套件。使用API UIObjectFactory.setPackageItemExtension完成定義。例如:
UIObjectFactory.setPackageItemExtension(UIPackage.getItemURL(“包名“,”元件A”), MyComponent );
public class MyComponent extends GComponent
{
override protected function constructFromXML(xml:XML):void
{
super.constructFromXML(xml);
//在這裡繼續你的初始化
}
}
這樣就為元件A指定了一個實現類MyComponent 。以後所有元件A創建出來的物件(包括在編輯器裡使用的元件A)都為MyComponent 型別。例如:
var obj:MyComponent = UIPackage.createObject(“包名“, ”元件A”) as MyComponent ;
注意:如果元件A只是一個普通的元件,沒有定義“擴充套件”,那麼基類是GComponent,如上例所示;如果元件A的擴充套件是按鈕,那麼MyComponent的基類應該為GButton,如果擴充套件是進度條,那麼基類應該為GProgressBar,等等。這個不要弄錯。
控制器Tween效果
動效的播放在程式碼中啟動,例如:
someComponent.GetTransition(“peng”).Play();
Play有多種原型,例如可以重複播放一定次數,可以在播放結束時回撥等。要中途停止動效的播放,可以呼叫:
someComponent.GetTransition(“peng”).Stop();
Stop方法也可以帶引數,原型是
public void Stop(bool setToComplete, bool processCallback);
setToComplete表示是否將元件的狀態設定到播放完成的狀態,如果否,元件的狀態就會停留在當前時間。processCallback是否呼叫Play設定的播放完成回撥函式。
注意:UI動效播放完畢後,元件的狀態將停留在最後一幀,而不是回到第一幀,如果你希望動效播放完後元件的狀態復原到播放前,你可以最後新增一幀重新設定元件的狀態。
高階用法:
public void SetValue(string label, params object[] aParams)
改變指定幀的數值,例如某幀Label為aa,這幀是設定某個元件的XY值的,那麼呼叫setValue(aa, 100,200)就可以將原來此幀設定XY的數值改為100,200。
public void SetHook(string label, TransitionHook callback)
可以設定執行到某幀時發生一個呼叫。
多國語言支援
2、執行時動態載入語言檔案。這種方法相對比較靈活。
Unity程式碼片段:
string fileContent; //自行載入語言檔案,這裡假設已載入到此變數
FairyGUI.Utils.XML xml = new FairyGUI.Utils.XML(fileContent);
UIPackage.SetStringsSource(xml);
(十二)UI適配策略 (解析度 設定)
GRoot.inst.setContentScaleFactor(設計解析度寬度,設計解析度高度,適配策略);
someComponent.setSize(GRoot.inst.width, GRoot.inst.height);// 滿屏
GRoot是FairyGUI的根元件,它的大小與螢幕解析度的關係為
GRoot.inst.width = 螢幕寬度/GRoot.contentScaleFactor;
GRoot.inst.height = 螢幕高度/GRoot.contentScaleFactor;
(十三)顯示架構API
FairyGUI在原生的渲染引擎上封裝了一層顯示物件結構。
基礎類顯示物件:
GObject:顯示物件的基類。
GGraph:圖形物件。對應於編輯器中顯示的一個圖形。
GImage:圖片物件。對應於編輯器中顯示的一個圖片。
GMovieClip:動畫物件。對應於編輯器中顯示的一個動畫。
GLoader:裝載器物件。對應於編輯器中顯示的一個裝載器。
GTextField:文字物件。對應於編輯器中顯示的一個文字。
GRichTextField:多媒體文字。對應於編輯器中顯示的一個多媒體文字。
GTextInput:輸入文字物件。對應於編輯器中顯示的一個文字,且文字型別被設定為“輸入”。
容器類顯示物件:
GComponent:元件物件。對應於編輯器中建立的一個元件。
GList:列表物件。對應於編輯器中顯示的一個列表。
容器的擴充套件:
GLabel:標籤物件。當一個元件的擴充套件為“標籤”時,即為此型別。
GButton:按鈕物件。當一個元件的擴充套件為“按鈕”時,即為此型別。
GComboBox:組合框物件。當一個元件的擴充套件為“組合框”時,即為此型別。
GScrollBar:滾動條物件。當一個元件的擴充套件為“滾動條”時,即為此型別。
GProgressBar:進度條物件。當一個元件的擴充套件為“進度條”時,即為此型別。
GSlider:滑動條物件。當一個元件的擴充套件為“滑動條”時,即為此型別。
FairyGUI和Flash/Cocos類似,採用樹狀的結構組織顯示物件。容器可以包含一個或多個基礎顯示物件,也可以包含容器。這個樹狀結構稱為顯示列表。如下圖:
處於最前面的且元件範圍包含點選位置的元件將捕獲滑鼠或觸控事件,並且開始冒泡傳遞(請參考事件系統 Flash/Starling/Egret/Unity)
對於元件(GComponent),寬度x高度的範圍內點選檢測都是有效的,無論這個範圍內是否有子元件。舉個例子說明。元件A和元件B分別為:
將B先新增進舞臺,然後再新增A到舞臺,也就是說,A顯示B的前面,效果如下圖:
可以看到,雖然A在B的上面,但紅色方塊是可見的,因為A在此區域並沒有內容。當點選圖中綠色點的位置時,點選事件將在A上面觸發,而B是點選不了的。這是因為在A的範圍內,點選是不能穿透的。
如果希望A能被穿透應該怎樣?可以通過GComponent.opaque屬性設定。當設定為false時,這個元件將能被穿透。例如A.opaque=false,這時,只有點選4個白塊時,A才接收到點選事件,如果點選綠色點位置,B將接收到點選事件。這個特性在設計一些全屏介面時尤其要注意。也可以在編輯器裡直接設定:
滾動支援
你可以在編輯器裡很方便的對一個元件新增滾動特性。如果一個元件在編輯器裡設定為滾動,我們可以通過GComponent.scrollPane獲得滾動控制物件。
ScrollPane提供了多個API訪問和控制滾動:
ScrollPane.percX/ScrollPane.percY:獲得或設定當前滾動的位置,以百分比來計算,取值範圍是0-1。如果希望滾動條從當前值到設定值有一個動態變化的過程,可以使用ScrollPane.setPercX和ScrollPane.setPercY,這兩個API提供了一個是否使用緩動的引數。
ScrollPane.posX/ScrollPane.posY:獲得或設定當前滾動的位置,以畫素來計算。取值範圍決定於當前顯示的內容大小與視口大小的差別。例如,一個垂直滾動的元件,如果視口大小為100畫素,實際內容大小為300畫素,那麼posY的取值範圍是0~200畫素,當posY=200時,滾動條滾動到最下方。posX/posY與percX/percY不同在於,除非開發者自己設定或者使用者拖動滾動條,percX/percY是不變的,舉個例子,當前某滾動容器的percX=0.1, posX=100,如果往容器裡新增一定內容後,內容的寬度增大,percX的值仍將保持不變,依然是0.1,但posX的值會發生相應的改變。所以如果希望容器內增刪內容後滾動條不發生滾動,可以先記錄posX/posY的位置,新增完之後再設定posX/posY為此前記錄的位置。
ScrollPane.scrollToView:調整滾動位置,使指定的元件出現在視口內。
視窗系統
視窗使用前首先要設定視窗中需要顯示的內容,這通常是在編輯器裡製作好的,可以直接使用Window.contentPane進行設定。建議把設定contentPane等初始化操作放置到Window.onInit方法中。
另外,FairyGUI還提供了一套機制用於視窗動態建立。動態建立是指初始時僅指定視窗需要使用的資源,等視窗需要顯示時才實際開始構建視窗的內容。首先需要在視窗的建構函式中呼叫Window.addUISource。這個方法需要一個IUISource型別的引數,而IUISource是一個介面,使用者需要自行實現載入相關UI包的邏輯。當視窗第一次顯示之前,IUISource的載入方法將會被呼叫,並等待載入完成後才返回執行Window.OnInit,然後窗口才會顯示。
呼叫Window.show顯示視窗的流程:
如果你需要視窗顯示時播放動畫效果,那麼覆蓋doShowAnimation編寫你的動畫程式碼,並且在動畫結束後呼叫onShown。
覆蓋onShown編寫其他需要在視窗顯示時處理的業務邏輯。
呼叫Window.hide隱藏視窗的流程:
如果你需要視窗隱藏時播放動畫效果,那麼覆蓋doHideAnimation編寫你的動畫程式碼,並且在動畫結束時呼叫Window.hideImmediately(注意不是直接呼叫onHide!)。
覆蓋onHide編寫其他需要在視窗隱藏時處理的業務邏輯。
通常視窗會包括一個可用於拖動的標題欄,關閉按鈕等。我們在編輯器建立視窗元件時,可以為它建立一個名稱為frame的元件,frame元件的擴充套件應該選擇為“標籤”,這樣外部元件能夠為其設定圖示和標題屬性。frame元件中約定的內容還包括:
closeButton:一個名稱為closeButton的按鈕將自動作為視窗的關閉按鈕。
dragArea:一個名稱為dragArea的圖形(型別設定為空白)將自動作為視窗的檢測拖動區域,當用戶在此區域內按住並拖動時,視窗隨之被拖動。
contentArea:一個名稱為contentArea的圖形(型別設定為空白)將作為視窗的主要內容區域,這個區域只用於Window.showModalWait。當呼叫Window.showModalWait時,視窗會被鎖定,如果設定了contentArea,則只鎖定contentArea指定的區域,否則鎖定整個視窗。如果你希望視窗在modalWait狀態下依然能夠拖動和關閉,那麼就不要讓contentArea覆蓋標題欄區域。
注意以上的約定均為可選,是否含有元件frame,或者元件frame裡是否含有約定的功能元件,並不會影響視窗的正常顯示和關閉。
Popup管理
在UI系統中我們經常需要一些彈出一些元件,這些元件在使用者點選空白地方的情況下就會自動消失,又或者由開發者控制消失。GRoot提供了幾個API管理這些Popup元件。
GRoot.showPopup:彈出一個元件,如果指定了目標,則會調整彈出的位置到目標的下方,形成一個下拉的效果。同時提供了引數可以用來指定是向上彈出或者向下彈出。
GRoot.hidePopup: 預設情況下,使用者點選空白地方就會自動關閉彈出的元件。也可以呼叫此API手工關閉。不指定引數時,所有當前的彈出都關閉。
FairyGUI會根據元件的大小自動計算彈出位置,以確保元件顯示不會超出螢幕。
(十五)事件系統:Unity
發表回覆
Unity平臺參考了Flash的事件機制,設計了自己獨特的事件系統。EventDispatcher是事件分發的中心,GObject就是一個EventDispatcher。每個事件型別都對應一個EventListener,接收事件並呼叫處理函式。例如需要編寫某個元件單擊的處理邏輯:
aObject.onClick.Add(aCallback);
void aCallback()
{
//some logic
}
事件回撥函式
每個事件可以註冊一個或多個回撥函式。函式原型為:
public delegate void EventCallback0();
public delegate void EventCallback1(EventContext context);
函式可以不帶引數或帶一個引數。
EventContext
EventContext是回撥函式的引數型別。
EventContext.sender:獲得事件的分發者。
EventContext.initiator:獲得事件的發起者。一般來說,事件的分發者和發起者是相同的,但如果事件已發生冒泡,則可能不相同。參考下面冒泡的描述。
EventContext.type:事件型別。
EventContext.inputEvent:如果事件是鍵盤/觸控/滑鼠事件,通過訪問inputEvent物件可以獲得這類事件的相關資料。
EventContext.data:事件的資料。根據事件不同,可以有不同的含義。
事件冒泡和事件捕獲
一些特殊的事件,比如滑鼠/觸控事件,具備向父元件傳遞的特性,這個傳遞過程叫做冒泡。例如當手指接觸A元件時,A元件觸發TouchBegin事件,然後A元件的父元件B觸發TouchBegin事件,然後B的父元件C也觸發TouchBegin事件,以此類推,直到舞臺根部。這個設計保證了所有關聯的顯示物件都有機會處理觸控事件,而不只是最頂端的顯示物件。
冒泡過程可以被打斷,通過呼叫EventContext.StopPropagation()可以使冒泡停止向父元件推進。
從上面的冒泡過程可以看出,事件處理的順序應該是:A’s listeners->B’s listeners->C’s listeners,這裡還有一種機制可以讓鏈路上任意一個物件可以提前處理事件,這就是事件捕獲。事件捕獲是反向的,例如在上面的例子中,就是C先捕獲事件,然後是B,再到A。所以所有事件處理的完整順序應該是:
C’s capture listeners->B’s capture listeners->A’s capture listeners->A’s listeners->B’s listeners->C’s listeners
捕獲傳遞鏈是不能中止的,冒泡傳遞鏈可以通過StopPropagation中止。
事件捕獲的設計可以使父元件優於子元件和孫子元件檢查事件。
並非所有事件都有冒泡設計,請參考下面關於各個事件的說明。並非只有冒泡事件才有捕獲設計。在非冒泡事件中,capture的回撥優於普通回撥,僅此而已,可以作為一個優先順序特性來使用。
InputEvent
對鍵盤事件和滑鼠/觸控事件,可以通過EventContext.inputEvent獲得此類事件的相關資料。InputEvent.x/InputEvent.y:滑鼠或手指的位置;這是舞臺座標,因為UI可能因為自適配發生了縮放,所以如果要轉成UI元件中的座標,要使用GObject.GlobalToLocal轉換。
InputEvent.keyCode:按鍵程式碼;
InputEvent.modifiers:參考UnityEngine.EventModifiers。
InputEvent.mouseWheelDelta:滑鼠滾輪滾動值。
InputEvent.touchId:拖動使用手指;在PC平臺,該值為0,沒有意義。
InputEvent.isDoubleClick:是否雙擊。
EventListener
EventListener.Add/EventListener.Remove:新增或刪除一個回撥,回撥函式可以帶一個引數或者不帶引數。引數的型別是object,它的實際含義隨不同事件不同而不同;
EventListener.AddCapture/EventListener.RemoveCapture:新增或刪除一個捕獲期回撥。
事件型別
GObject.onClick:單擊。冒泡事件。事件資料為InputEvent物件。
GObject.onRightClick:右鍵單擊。冒泡事件。事件資料為InputEvent物件。
GObject.onTouchBegin:滑鼠或手指按下。冒泡事件。事件資料為InputEvent物件。
GObject.onTouchEnd:滑鼠或手指釋放。冒泡事件。事件資料為InputEvent物件。
GObject.onRollOver:滑鼠移入元件。事件資料為InputEvent物件。
GObject.onRollOut:滑鼠移出元件。事件資料為InputEvent物件。
GObject.onAddedToStage:元件被新增到舞臺。無事件資料。
GObject.onRemovedFromStage:元件從舞臺移出。無事件資料。
GObject.onKeyDown:元件接收到按鍵事件。只有獲得焦點的情況下才能接收按鍵事件。冒泡事件。事件資料為InputEvent物件。
GObject.onClickLink:文字中的連結被點選。事件資料為href值,字串型別。
GObject.onPositionChanged:元件的位置改變。無事件資料。
GObject.onSizeChanged:元件的大小改變。無事件資料。
GObject.onDragStart/GObject.onDragEnd:拖動是指玩家按住元件拖動然後釋放的過程。注意只有設定了GObject.draggable屬性的元件才會觸發拖動事件。拖動過程中可以獲得兩個通知:開始和結束。當onDragStart中,呼叫EventContext.PreventDefault()可以立刻取消拖動。無事件資料。
GTextField.onFocusIn/GTextField.onFocusOut:只有輸入型別的文字才會觸發這個事件。當輸入文字獲得焦點時,在移動裝置上會彈出小鍵盤。無事件資料。
GTextField.onChanged:只有輸入型別的文字才會觸發這個事件。無事件資料。
GMovieClip.onPlayEnd:動畫設定的播放次數已經播放完畢。無事件資料。
GList.onClickItem:當GList容器內的元件被點選時觸發的事件型別;事件資料為當前點選的GObject物件。
GComponent.onScroll:如果容器是滾動型別容器,則滾動發生時產生該事件。
GComponent.onDrop:注意要和普通拖動區別,一個元件被拖動並釋放後並不會觸發Drop事件。Drop事件需配合DragDropManager使用。當DragDropManager拖動的圖示在某個元件上釋放時,這個元件就會觸發onDrop。事件資料為DragDropManager.StartDrag中傳遞的source值。
GButton.onChanged:當單選或者多選按鈕選中狀態改變時會觸發該事件(只有在使用者點選後狀態改變才會觸發,如果是程式改變不會觸發)。無事件資料。
GComboBox.onChanged:當用戶從下拉列表中選擇一項時觸發該事件。無事件資料。
GSlider.onChanged:當用戶拖動滑塊改變Slider的值時觸發該事件。無事件資料。
Controller.onChanged:當控制器頁面改變會觸發該事件(改變selectedIndex會觸發該事件,setSelectedIndex則不會)。無事件資料。
(十八)在Unity專案中使用FairyGUI
1. 使用UIPanel
只需3步就可以使用將編輯器中製作好的介面放入到Unity的場景中。第一步,從GameObject選單中選擇FairyGUI->UIPanel:
第二步,在Inspector裡點選PackageName或者ComponentName,將彈出選擇元件的視窗:
第三步:這個窗口裡列出了所有工程裡能找到的UI包,選擇一個包和元件,然後點選OK。
可以看到,UI元件的內容顯示出來了。(注意:Unity4版本目前不支援顯示內容,只能顯示線框)
如果UI包修改了,或者其他一些情況導致UIPanel顯示不正常,可以使用下面的選單重新整理:
當執行後,獲得UIPanel的UI的方式是:
UIPanel panel = gameObject.GetComponent();
GComponent view = panel.ui;
與其他UIPackage.CreateObject創建出來的介面不同,UIPanel在GameObjec銷燬時(手動銷燬或者過場景等)時會一併銷燬。
UIPane只儲存了UI包的名稱和元件的名稱,它不對紋理或其他資源產生任何引用,也就是UI使用的資源不會包含在場景資料中。
在編輯狀態下,無論UI元件引用了哪些UI包的資源,包括放置在Resources目錄下的和不放置在Resources下的,都能夠正確顯示。但當執行後,UIPanel只能自動載入放置在Resources目錄或其子目錄下的UI包,也只會載入自身所在的UI包,其他情況的UI包(例如引用到的UI包或打包到AssetBundle的UI包)不能自動載入。你需要在UIPanel建立前使用UIPackage.AddPackage準備好這類UI包。UIPanel在Start事件或者第一次訪問UIPanel.ui屬性時建立UI介面,你仍然有機會在Awake裡完成這些操作。
下面是UIPanel的一些屬性說明:
Render Mode: 有三種:
Screen Space Overlay (預設值),表示這個UI在螢幕空間顯示,這時Transform的Scale將被鎖定,而且不建議修改Transform中的其他內容(讓他們保持為0)。如果要修改面板在螢幕上的位置,使用UI Transform(參考下面關於UI Transform的說明)。
Screen Space Camera 表示這個UI在螢幕空間顯示,但不使用FairyGUI預設的正交相機,而是使用指定的正交相機。
World Space 表示這個UI在世界空間顯示,由透視相機渲染。預設的使用場景的主相機,如果不是,那麼設定Render Camera。當使用這種模式時,使用Transfrom修改UI在世界空間中的位置、縮放、旋轉。但你仍然可以使用UI Transform。
注意:Render Mode只定義了FairyGUI對待這個UI的方式,通常是座標相關的操作(如點選檢測等),但和渲染無關。UI由哪個相機渲染是由GameObject的layer決定的。所以如果發現UI沒有顯示出來,可以檢查一下GameObject的layer是否正確。例如如果是Screen Space,GameObject應該在UI層,如果是WorldSpace,則是在Default層或者其他自定義的層次。
Render Camera:當Render Mode是Screen Space Camera或者World Space時可以設定。如果不設定,預設為場景的主相機。
Sorting Order:調整UIPanel的顯示順序。越大的顯示在越前面。
Fairy Batching:是否啟用Fairy Batching。關於Fairy Batching請參考下面的說明。切換這個值,可以在編輯模式下實時看到DrawCall的變化(切換後點擊一下Game,Stat裡顯示的內容才會更新),這可以使你更加容易決定是否啟用這項技術。
Touch Disabled:勾選後,將關閉點選檢測。當這個UI沒有可互動的內容時可以勾選以提高點選檢測時的效能。例如頭頂血條這些型別的UI,就可以勾選。
UI Transform:當Render Mode是Screen Space時可以使用這裡的設定調整UI在螢幕上的位置。你仍然可以調整Transform改變UI的位置,但我不建議你這樣做,因為Transform中的座標位置是沒有經過不同解析度自適應的調整的。當Render Mode是World Space時,建議使用Transform設定UI的位置,你仍然可以調整UI Transform改變UI的位置,但調整的效果可能不那麼直觀。同時你可以使用Scene檢視中下圖所示的原點調整UI Transform的位置屬性:
Fit Screen:這裡可以設定UIPanel適配螢幕。
Fit Size:UI將鋪滿螢幕。
Fit Width And Set Middle:UI將橫向鋪滿螢幕,然後上下居中。
Fit Height And Set Center:UI將縱向鋪滿螢幕,然後左右居中。
這裡提供的選項不多,因為FairyGUI推薦的是在FairyGUI編輯器中整體設計,而不是在Unity裡擺放小元件。例如如果需要不同的UI在螢幕上的各個位置佈局,你應該在FairyGUI編輯器中建立一個全屏大小的元件,然後在裡面放置各個子元件,再用關聯控制佈局;最後將這個全屏元件放置到Unity,將Fit Screen設定為Fit Size即可。錯誤的做法是把各個子元件放置到Unity裡再佈局。
HitTest Mode:這裡可以設定UIPanel處理滑鼠或觸控事件的方式。
Default: 這是預設的方式。FairyGUI會用內建的機制檢測滑鼠或觸控動作,不使用射線,UIPanel也不需要建立碰撞體,效能比較高。
Raycast: 在這種方式下,UIPanel將自動建立碰撞體,並且使用射線方式進行點選檢測。這種方式適合於UIPanel還需要和其他3D物件互動的情況。
2. 動態載入UI介面
在很多情況下,你並不需要將UI介面放到場景中。使用程式碼載入編輯器製作好的介面也非常簡單:
GComponent view = UIPackage.CreateObject(“包名”, “元件名”) as GComponent
動態載入的介面不會自動銷燬,例如一個揹包視窗,你並不需要在每次過場景都銷燬。如果要銷燬介面,呼叫Dispose方法即可,例如
view.Dispose();
也可以動態建立UIPanel為任意遊戲物件掛接UI介面,方法為:
UIPanel panel = yourGameObject.AddComponent();
panel.packageName = “包名”;
panel.componentName = “元件名”;
panel.CreateUI();
UIPanel的生命週期將和yourGameObject保持一致。再次提醒,注意yourGameObject的layer。
3. 載入UI包
Unity專案載入UI包有以下幾種方式,開發者可以根據專案需要選擇其中一種或者多種混搭使用:
1)將打包後的檔案直接釋出到Unity的Resources目錄或者其子目錄下,
然後在程式碼中呼叫UIPackage.AddPackage(“demo”), demo就是釋出時填寫的檔名。如果在子目錄下,呼叫UIPackage.AddPacakge(“路徑/demo”)即可。
2)將釋出後的檔案打包為兩個AssetBundle,即定義檔案和資源各打包為一個bundle(desc_bundle+res_bundle)。這樣做的好處是一般UI的更新都是修改元件位置什麼的,不涉及圖片資源的更新,那麼只需要重新打包和推送desc_bundle就行了,不需要讓玩家更新通常體積比較大的res_bouble,節省流量。打包程式由開發者按照自己熟悉的方式自行實現。以demo為例,請遵循以下規則打包:
a)demo.bytes單獨打包為desc_bundle;
b)其他資源([email protected]等),打包到res_bundle(在此例中就是atlas0和sprites)
然後在程式碼中呼叫UIPackage.AddPackage(desc_bundle, res_bundle)。bundle的載入由開發者自行實現。
3)將釋出後的檔案打包為一個AssetBundle。打包程式由開發者按照自己熟悉的方式自行實現。以demo為例,將demo.bytes和其他資源([email protected]等),都放入bundle。然後在程式碼中呼叫UIPackage.AddPackage(bundle, bundle)。bundle的載入由開發者自行實現。
在使用AssetBundle的載入方案中,將由FairyGUI接管bundle並負責bundle資源的釋放。
4. UI適配
可以通過兩種方式設定UI自適應,第一種方式是在遊戲的啟動建立裡任意物件掛一個FairyGUI/UIContentScaler元件:
這裡選項的含義可以參考教程裡關於UI適配的說明。
另外一種方式是通過程式碼,可以參考教程裡關於UI適配的說明。
5.圖集的處理
FairyGUI編輯器釋出到Unity的資源通常包含一個或多個UI圖集,以demo工程為例,這裡[email protected]就是一個圖集
圖集是由UI編輯器自動生成的,但在Unity裡可以改變圖集的屬性,常用的設定有:
1)Generate Mip Maps
不勾選。UI不使用Mip Maps功能。
2)Filter Mode
使用Bilinear,這樣影象在縮放時能產生比較平滑的過渡效果,副作用是會產生一定的模糊。而且單色的影象縮放會產生不必要的漸變邊緣。而使用Point,則影象在縮放時會塊狀化。
一般UI圖集使用Bilinear即可。你也可以在UI編輯器裡將圖片安排到不同圖集,然後每個圖集設定不同的Filter Mode以滿足特殊需求。
3)Max Size
一般設定到2048。
4)Format
UI圖集一般都是PNG格式,並帶有透明通道。同時,UI對畫質的要求比較高,所以建議選擇AutoMatic TrueColor。但TrueColor有一個最大的問題是檔案大,而且佔用記憶體較高,例如1024×1024的圖集,將佔用4MB的記憶體,2048×2048則達到16MB。
如果對記憶體使用比較敏感,也可以選用壓縮格式,即AutoMatic Compressed。在桌面平臺上即相同於DXT5,在Android平臺上相當於ETC1,在IPhone平臺則為PVRTC。這些格式能夠大大降低記憶體佔用,也因為它們是顯示卡直接支援的格式,所以Unity在載入時省去了解碼點陣圖的步驟,能夠加快載入速度。但它們都是屬於有失真壓縮,在顯示質量上肯定不如TrueColor,特別是圖集顏色十分豐富,或者有顏色漸變時,失真會比較厲害。特別重要的是,ETC1是不支援透明通道的,PVRTC對透明通道的支援也比較弱,所以並不適合帶透明通道的圖集。FairyGUI提供了ETC1/PVRTC+A的解決方案。首先,在釋出時勾選“為Alpha通道建立單獨的貼圖”,如下圖:
這樣,就產生了兩個不含透明通道的貼圖,一張去除了原圖透明通道的貼圖,和一張將原貼圖透明通道數值轉換為等價灰度的貼圖。這兩張貼圖都可以設定為Automatic Compressed。(一定要注意,不能再將主貼圖設定為True Color)
FairyGUI提供了特製的著色器處理兩張貼圖的合併。開發者並不需要編寫額外的程式碼就可以使用這項技術。
TIPS
你可以將本身就不含透明通道的點陣圖(例如一些大型的背景圖)安排到一張圖集上。如果一張圖集內的所有圖片都不包含透明通道,那麼最終輸出的圖集也不包含透明通道。不含透明通道的圖集可以選用Automatic 16bits格式。
6. 字型的處理
FairyGUI使用Unity的動態字型技術渲染文字。只需以下幾個簡單的步驟就可以完成設定:
1)拷貝一個字型檔案到專案的Resources目錄或Resources/Fonts目錄。字型檔案可以隨意使用一個,例如arial.ttf,或simhei.ttf等,這個ttf是什麼字型並不影響最終顯示字型的選擇。
2)在Unity中點選字型檔案,在inspector中修改字型屬性
注意在Font Names中填寫的是字型名稱,標準的字型名稱可以從這裡找到: 中文字型的英文名稱對照表。例如Droid Sans Fallback是Android平臺支援中文的內建預設字型之一。多個字型用逗號隔開,Unity會使用第一個在系統中能找到的字型。
3)設定UIConfig.defaultFont=”字型檔名稱”即可,注意這裡使用的是檔名稱,也就是說,如果放置在Resources目錄的是arial.ttf,則UIConfig.defaultFont=”arial”,不需要帶.ttf字尾。
因為我們沒有勾選Include Font Data,所以無論這個字型檔案有多大,最終並不會包含在我們的釋出包中,也就是說不會增大發行包的體積。Unity會在實際執行的系統中查詢與Font Names匹配的第一個字型,並使用此字型進行動態渲染。
如果你是在Unity5.x平臺開發,除了上述方式外,得益於Unity5.x引入的新的字型管理API,也可以不放置字型檔案到Resources目錄,你只需要直接設定UIConfig.defaultFont=”字型名稱“即可,同樣,多個字型名稱用逗號隔開。例如:UIConfig.defaultFont=”Droid Sans Fallback, LTHYSZK, Helvetica-Bold, Microsoft YaHei, SimHei”;
4)如果你的介面使用了多種字型,比如對單獨的文字設定了字型:
這裡用到了“黑體”這個名字的字型,這是與UIConfig.defaultFont裡設定的不同的字型,我們也需要註冊這種字型。方法是,首先做好上面1)和2),假如字型檔名稱是HeiTi.ttf,然後
FontManager.RegisterFont(“黑體”, FontManager.GetFont(“HeiTi”));
RegisterFont的第一個引數對應編輯器裡使用的字型名稱;第二個引數,是Unity中放入的字型檔案資源。如果檔案帶路徑,這也需要把路徑填上。
5)當你使用部分字型的粗體效果時,你會發現粗體的效果在Unity中的顯示不正確,這是因為有些字型不帶粗體效果的,這時候Unity就會用拉寬來實現,就像變扁了。fairygui可以用額外的mesh來解決粗體的顯示。方法是:
FontManager.GetFont(“字型路徑”).customFold = true;
這裡字型路徑與UIConfig.default裡設定的內容應該完全一樣。
6)某些字型,unity渲染有粗體效果,但當設定成斜體時,粗體效果又丟失(例如雅黑)。fairygui在這種情況取消unity預設渲染粗體的效果,改為增加額外的面渲染粗體。啟用這個功能的方法是
FontManager.GetFont(“字型路徑”).customBoldAndItalic = true;
如果已經設定了customBold,不需要再設定customBoldAndItalic。
備註1
動態字型要求玩家的執行系統環境中有你設定的字型,如果沒有,實際使用的字型可能並不是你想要的效果。因此,你可以選擇嵌入整個字型檔案。方法是把你要用的字型檔案放到Resources目錄,並把Include Font Data勾選,這樣整個字型檔案就會包含到最終的釋出包中,壞處就是會大大增加發行包的體積。
備註2
在實際遊戲製作過程中發現在桌面平臺下Unity對中文字型的渲染稍顯模糊和暗淡,因此FairyGUI使用了特製的著色器解決了這個問題。以下是Unity預設的字型渲染效果和FairyGUI的字型渲染效果的比較:
可以看見經過FairyGUI的特殊處理,中文文字更清晰更亮。
只有在桌面平臺下FairyGUI才會開啟這種技術,移動平臺永遠不會開啟,因為在高DPI情況下,字型預設的渲染效果已經非常漂亮。
另外,如果你不喜歡這種顯示效果,或者你使用的是全英文的文字,也可以手動把這種技術關閉:UIConfig.renderingTextBrighterOnDesktop = false;
7 在UI中穿插其他3D物件
FairyGUI底層使用Renderer.sortingOrder來決定物件的渲染順序,利用這個特性,我們可以很容易的將UI物件與其他3D物件(例如例子)安排在一起。FairyGUI提供了封裝類GoWrapper,可以直接包裝其他GameObject插入到UI層次中。如下圖,一個粒子特效被安排到了UI之間。
又例如,一個3D模型穿插在UI中間
詳細實現方法可以參考FairyGUI-unity包中的Assets/FairyGUI/Examples/Particles和Assets/FairyGUI/Examples/Model。
8. 與RenderTexture配合
在UI上展現3D內容的另一種方式是使用RenderTexture,特別是需要進行剪裁的情況下(如果不需要剪裁,推薦直接放置3D內容,免除RenderTexture效能消耗。參考6. 在UI中穿插其他3D物件)。在FairyGUI中,可以將Image.texture設定為一個RenderTexture,然後就可以像使用普通圖片一樣使用這個RenderTexture了,可以出現在任何地方,包括滾動容器。特別地,FairyGUI還能支援將RenderTexture所在位置的背景圖片影射到RenderTexture渲染相機的背景上,這樣就不用擔心透貼的問題了。如下圖,NPC使用RenderTexture渲染到UI視窗中。
詳細實現方法可以參考FairyGUI-unity包中的Assets/FairyGUI/Examples/RenderTexture。
9. Drawcall優化
在Unity中,每次引擎準備資料並通知GPU的過程稱為一次Draw Call(DC)。Draw Call次數是一項非常重要的效能指標。UI系統一般包含數量眾多的物體,有效控制DC是衡量一個UI系統是否實用的關鍵因素,特別是在移動裝置上。
我們先來看看NGUI是怎麼做的,NGUI把UIPanel中的Widget按depth排序,然後將相同材質的Widget進行Mesh合併,例如使用相同圖集的圖片,或者文字。Mesh合併的優點是合併後這些Widget就只產生一個DC。但這個合併過程需要計算所有Widget座標相對於Panel的變換,而且如果Widget行為改變,例如平移,縮放等,都會觸發Mesh重新合併,這會帶來一定的CPU消耗,這就需要開發者謹慎組織UI元素到各個UIPanel,並且對深度需要細緻安排,否則達不到減少DC效果的同時更可能帶來比較大的CPU消耗。
FairyGUI沒有采取合併Mesh的策略,原因有兩個:
● FairyGUI使用的是樹狀顯示物件結構,各個元件之間的層次關係非常複雜;
● FairyGUI編輯器給予使用者最大的設計自由度,加上動效的引入,各個元件的狀態都可能非常動態;
FairyGUI基於Unity的Dynamic Batching技術,提供了深度調整技術進行 drawcall優化 。FairyGUI能在不改變最終顯示效果的前提下,儘可能的把相同材質的物體調整到連續的renderingOrder值上,以促使他們能夠被Unity Dynamic Batching優化。Dynamic Batching是Unity提供的Draw Call Batching技術之一。如果動態物體共用著相同的材質,那麼Unity會自動對這些物體進行批處理。但Dynamic Batching的一個重要的前提是這些動態物體是連續渲染的。先來看看FairyGUI中物體的渲染順序,例如:
這裡有4個按鈕,每個按鈕都是一個元件,每個元件裡包含一個圖片和一個文字物件。FairyGUI是樹狀的顯示物件結構,那麼他們按深度排序應該是:
因為文字和圖片的材質並不相同,所以每次從文字到圖片都產生上下文切換,所以產生了6個DC。
FairyGUI的深度調整技術可以優化這種情況。觀察一下,其實四個按鈕之間並不相交,所以FairyGUI智慧地將渲染順序調整為:
因為FairyGUI使用了圖集,而且動態文字也使用了相同的貼圖,這樣,DC就降低到了2個,達到了優化的目的。實際情況會比這個複雜很多,但FairyGUI能在不改變最終顯示效果的前提下,儘可能的把相同材質的物體調整到連續的renderingOrder值上,以促使他們能夠被Unity Dynamic Batching優化。而對開發者來說,這些底層上的調整是透明的,也就是不會影響原來的顯示物件層次。從效率上考慮,這種技術僅比較物體之間的顯示矩形區域(一個Rect)是否相交,所以速度是非常快的,不會帶來過多的CPU負荷。
FairyGUI提供了一個開關控制組件是否啟用深度調整,API是:
GComponent someComponent;
someComponent.fairyBatching = true;
如果某個元件設定了fairyBatching,那麼無需在子元件和孫子元件再啟用fairyBatching。一般只在頂層元件開啟這個功能,例如主介面,載入介面等。注意,Window這個類已經自動打開了fairyBatching,這符合我們的使用習慣,因為一般我們都是以視窗為單位安排功能的。如果介面不復雜,Draw Call本來就不高的情況下,開發者也可以忽略這個功能,從10個DC優化到8個DC並沒有什麼意義。在實際使用過程中,
對於打開了fairyBatching的元件,當開發者動態改變子元件或者孫子元件的位置或大小時,並不會自動觸發深度調整,例如一個圖片原來顯示在一個窗口裡的頂層,你用Tween將它從原來的位置移到另外一個位置,這個圖片就有可能被窗口裡的其他元素遮擋。這時開發者可以手動觸發深度調整,API是
someObject.InvalidateBatchingState();
這個API並不需要由開啟了fairyBatching的元件呼叫,它可以用任何一個內含的元件發起。對於UI動效(Transitions),FairyGUI已經自動呼叫了這個API,所以開發者不必處理。
下載並執行Demo,可以觀察fairyBatching的實際效果。例如這個Demo的首頁:
設定了fairyBatching後由42個DC減少到了6個DC,另外,可以看到Saved by batching: 27的字樣。
- 與ULUA配合
一、安裝
1、將以下語句新增到Assets\uLua\Editor\WrapFile.cs適當的位置,然後呼叫Lua的選單Gen Lua Wrap Files,重新生成繫結檔案。
_GT(typeof(EventContext)),
_GT(typeof(EventDispatcher)),
_GT(typeof(EventListener)),
_GT(typeof(InputEvent)),
_GT(typeof(DisplayObject)),
_GT(typeof(Container)),
_GT(typeof(Stage)),
_GT(typeof(Controller)),
_GT(typeof(GObject)),
_GT(typeof(GGraph)),
_GT(typeof(GGroup)),
_GT(typeof(GImage)),
_GT(typeof(GLoader)),
_GT(typeof(PlayState)),
_GT(typeof(GMovieClip)),
_GT(typeof(TextFormat)),
_GT(typeof(GTextField)),
_GT(typeof(GRichTextField)),
_GT(typeof(GTextInput)),
_GT(typeof(GComponent)),
_GT(typeof(GList)),
_GT(typeof(GRoot)),
_GT(typeof(GLabel)),
_GT(typeof(GButton)),
_GT(typeof(GComboBox)),
_GT(typeof(GProgressBar)),
_GT(typeof(GSlider)),
_GT(typeof(PopupMenu)),
_GT(typeof(ScrollPane)),
_GT(typeof(Transition)),
_GT(typeof(UIPackage)),
_GT(typeof(Window)),
_GT(typeof(GObjectPool)),
_GT(typeof(Relations)),
_GT(typeof(RelationType)),
2、拷貝FairyGUI.lua到你的lua檔案存放目錄。
二、使用
1、普通方法的偵聽和刪除偵聽
require ‘FairyGUI’
function OnClick() –也可以帶上事件引數,OnClick(context)
print(‘you click’)
end
UIPackage.AddPackage(‘Demo’)
local view = UIPackage.CreateObject(‘Demo’, ‘DemoMain’)
GRoot.inst:AddChild(view)
view.onClick:Add(OnClick)
–view.onClick:Remove(OnClick)
2、類方法的偵聽和刪除偵聽
require ‘FairyGUI’
TestClass = class(‘TestClass’, {})
function TestClass:ctor()
UIPackage.AddPackage(‘Demo’)
self.view = UIPackage.CreateObject(‘Demo’, ‘DemoMain’)
GRoot.inst:AddChild(self.view)
self.view.onClick:Add(TestClass.OnClick, self)
–self.view.onClick:Remove(TestClass.OnClick, self)
end
function TestClass:OnClick() –也可以帶上事件引數,TestClass:OnClick(context)
print(‘you click’)
end
TestClass.New()
3、如果要使用Tween,你可以直接使用GObject.TweenXXXX系列函式,免除了Wrap DOTween的麻煩。