轉載:在這篇文章中我將給讀者介紹Unity中的圖形用戶界面(GUI)編程.Unity有一個非常強大的GUI腳本API.它允許你使用腳本快速創建簡單的菜單和GUI.
簡介
Unity提供了使用腳本創建GUI界面的能力.在寫這篇文章的時候,Unity還沒有提供一套原生的可視化GUI開發工具,盡管你可能會在Unity Asset商店找到一些使用某種形式的圖形化腳本編程編寫GUI的工具.Autodesk Scaleform也提供了一個可以單獨購買並整合進Unity的插件但這超出了這篇文章的范圍.如果你對Scaleform插件的unity版本感興趣,我推薦你看看Scaleform Unity Plugin.
Unity提供了兩個主要的類來創建GUI.GUI類用於創建手動放置的GUI控件.GUILayout類用於創建自動放置的GUI控件.這兩個類之間的區別將在文章后面搞清楚.
Unity也提供了GUISkin資源(asset翻譯成套件?).它可以被應用於給你的GUI控件提供一種通用的的"外觀和感覺".一個GUISkin只是GUIStyle對象的集合.每個GUIStyle對象定義了單個GUI控件的樣式,比如按鈕,標簽或者文本域.
GUiText組件可被用於渲染單個的文本元素,GUITexture組件可以被用於渲染2D材質到屏幕.GUIText和GUITexture都適用於為你的游戲繪制GUI元素(就像HUD),但這些組件不適用於在游戲中繪制菜單.對於游戲中的菜單(像等級選擇和選項設置頁面)你應該使用GUI和GUILayout類.
這些不同的類,資源(Asset)和組件每一個本文中都會闡述
創建菜單
首先我講述一下如何在使用GUI和GUILayout在Unity中創建菜單.我也會向你展示如何使用GUISkin和GUIStyle來自定義GUI控件的外觀
OnGUI回調函數
GUI的渲染是通過創建腳本並定義OnGUI函數來執行的.所有的GUI渲染都應該在該函數中執行或者在一個被OnGUI調用的函數中執行
ButtonDemo.js
- function OnGUI()
- {
- var buttonWidth = 100;
- var buttonHeight = 50;
- var buttonX = (Screen.width - buttonWidth) / 2.0f;
- var buttonY = (Screen.height - buttonHeight) / 2.0f;
- //在屏幕中間繪制一個button組件
- if(GUI.Button(Rect(buttonX,buttonY,buttonWidth,buttonHeight),"Press Me!"))
- {
- //在調試控制台打印一些文字
- Debug.Log("Thanks!");
- }
- }
腳本ButtonDemo.js將會在屏幕中間繪制一個按鈕,松開按鈕文字"Thanks!"就會被打印到調試控制台.
GUIContent
大多數通用控件比如按鈕和標簽允許你指定控件上該呈現在控件上的的文本或者材質.如果你想在一個控件上指定文本與材質,那必須使用GUIContent結構.
CUIContent結構有幾個構造函數允許你創建一個帶有文本,材質和tooltip的CUIContent對象.
下面的例子擴展了之前的例子,但在這個例子中按鈕的內容由GUIContent結構指定.
ButtonDemo.js
- #pragma strict
- var texture:Texture;
- var text:String = "hello text";
- var toolTip:String = "hello toolTip";
- function OnGUI()
- {
- var buttonWidth = 100;
- var buttonHeight = 50;
- var buttonX = (Screen.width - buttonWidth) / 2.0f;
- var buttonY = (Screen.height - buttonHeight) / 2.0f;
- //在屏幕中心繪制一個帶有文本和圖像的按鈕
- if(GUI.Button(Rect(buttonX,buttonY,buttonWidth,buttonHeight),GUIContent(text,texture,toolTip)))
- {
- //在調試控制台打印一些文字
- Debug.Log("Thanks!");
- }
- }
改進后的ButtonDemo.js腳本將會在屏幕上顯示一個文本與材質.材質,文本和tooltip參數可以在檢視器(inspector)中指定.
ButtonDemo.js腳本會產生如下結果.
放置控件
使用GUI類你必須手動的擺放屏幕上的控件.控件使用GUI靜態函數的position參數來擺放.為了在屏幕上擺放控件,必須將一個Rect結構作為第一個參數傳遞給GUI控件函數.Rect結構的為控件定義了X,Y,Width,Height屬性.X,Y,Width和Height單位都是像素.
Screen.width和Screen.height屬性可以被用於檢視當前屏幕的范圍.
腳本ScreenDimension.js可以被用於渲染上面的截圖.
ScreenDimensions.js
- function OnGUI()
- {
- var width = Screen.width;
- var height = Screen.height;
- GUI.Label(Rect(0,0,100,20),"Top Left");
- GUI.Label(Rect(width - 100,0,100,20),"Top Right");
- GUI.Label(Rect(0,height - 20,100,20),"Bottom Left");
- GUI.Label(Rect(width - 100,height - 20,100,20),"Bottom Right");
- }
ScreenDimension.js腳本將繪制4個標簽.每個標簽都使用Screen.width和Screen.height屬性來決定其屏幕位置並對齊到屏幕角落里(測量單位為像素)
GUI類
GUI類是Unity用於將控件渲染到屏幕上的主要類.GUI類使用手動擺放來決定屏幕上控件的位置.這意味着在渲染控件時你必須顯式的指定控件在屏幕上的位置.使用這種方法手動擺放控件需要多做些工作但他可以讓你精確地控制屏幕上的控件位置.如果你不想手動的指定GUI控件的位置,那你應該使用GUILayout類.我們后面再詳細闡述GUILayout.
GUI控件
在下面的章節中,我將介紹在使用GUI和GUILayout時可利用的不同控件這些類提供的默認控件是box,button,label,window,texture,scroll bars,sliders,textfield,textarea,toggle和toolbar
GUI.BUTTON
可能你最常用的控件之一就是按鈕了.你可以使用GUI.Button()靜態函數來創建一個按鈕.這個函數用於將按鈕渲染到屏幕上,當松開按鈕時函數返回true.
這里值得一說的是GUI.Button函數只會在鼠標在按鈕上按下並且在按鈕上松開時才返回true,如果用戶按下按鈕移動鼠標在按鈕外面釋放鼠標,則函數不會返回TRUE,同樣的如果用戶按下了鼠標之后將光標移動到按鈕上然后釋放鼠標該函數也不會返回TRUE.要使該函數返回true,鼠標必須在按鈕上按下並釋放.
以下代碼可用於使用按鈕創建一個簡單的等級選擇屏幕(假定你在Build Settings對話框中有多個場景文件要設置)
LevelSelect.js
- function OnGUI()
- {
- var groundWidth = 120;
- var groundHeight = 150;
- var screenWidth = Screen.width;
- var screenHeight = Screen.height;
- var groupx = (screenWidth - groundWidth) / 2;
- var groupy = (screenHeight - groundHeight) / 2;
- GUI.BeginGroup(Rect(groupx,groupy,groundWidth,groundHeight));
- GUI.Box(Rect(0,0,groundWidth,groundHeight),"Level Select");
- if(GUI.Button(Rect(10,30,100,30),"Level 1"))
- {
- Application.LoadLevel(1);
- }
- if(GUI.Button(Rect(10,70,100,30),"Level 2"))
- {
- Application.LoadLevel(2);
- }
- if(GUI.Button(Rect(10,110,100,30),"Level 3"))
- {
- Application.LoadLevel(3);
- }
- GUI.EndGroup();
- }
將腳本添加到空的場景中會產生如下結果:
你也許會想看看Unity腳本參考里的GUI.Toggle和GUI.RepeatButton,這些與GUI.Button控件類似但略有不同的功能.
GUI.Label
GUI.Label()靜態函數用於繪制一個標簽.標簽通常實在屏幕上指定位置繪制的文字.標簽控件最常用的是在菜單屏幕中指定選項名稱(比如文本框和文本域).
標簽可包含文字,材質或者兩者兼有(使用之前講過的GUIContent結構)
下面的例子在屏幕上顯示繪制了兩個選項.選項名稱和滑塊的值使用標簽呈現
OptionsMenu.js
- #pragma strict
- private var masterVolume:float = 1.0;
- private var sfxVolume:float = 1.0;
- function OnGUI()
- {
- var groupWidth = 380;
- var groupHeight = 110;
- var screenWidth = Screen.width;
- var screenHeight = Screen.height;
- var groupX = (screenWidth - groupWidth) / 2;
- var groupY = (screenHeight - groupHeight) / 2;
- GUI.BeginGroup(Rect(groupX,groupY,groupWidth,groupHeight));
- GUI.Box(Rect(0,0,groupWidth,groupHeight),"Audio Settings");
- GUI.Label(Rect(10,30,100,30),"Master Volume");
- masterVolume = GUI.HorizontalSlider(Rect(120,35,200,30),masterVolume,0.0,1.0);
- GUI.Label(Rect(330,30,50,30),"(" + masterVolume.ToString("f2") + ")");
- GUI.Label(Rect(10,70,100,30),"Effect Volume");
- sfxVolume = GUI.HorizontalSlider(Rect(120,75,200,30),sfxVolume,0.0,1.0);
- GUI.Label(Rect(330,70,50,30),"(" + sfxVolume.ToString("f2") + ")");
- GUI.EndGroup();
- }
OptionsMenu.js腳本運行結果如下:
你也應該改看看GUI.TextField和GUI.TextArea這些控件允許你創建可編輯文字輸入控件.
GUI.HorizontalSlider和GUI.verticalSlider
GUI.HorizontalSlider和GUI.verticalslider靜態函數可相應的用於繪制水平和豎直滑塊.滑塊用於指定在一定范圍內的一個數值.在上面的例子中,使用了兩個水平滑塊來指定主音量和音效范圍為0到1
Slider函數接受當前滑塊值和滑塊最小值和滑塊最大值.上面的例子展示了如何使用水平滑塊,豎直滑塊使用與水平滑塊同樣的參數只是滑塊是豎直繪制而不是水平繪制.
下面的例子展示了使用豎直滑塊來創建一個音頻均衡器
- #pragma strict
- private var equalizerValues = new float[10];
- function OnGUI()
- {
- var groupWidth = 320;
- var groupHeight = 260;
- var screenWidth = Screen.width;
- var screenHeight = Screen.height;
- var groupX = (screenWidth - groupWidth) / 2;
- var groupY = (screenHeight - groupHeight) / 2;
- GUI.BeginGroup(Rect(groupX,groupY,groupWidth,groupHeight));
- GUI.Box(Rect(0,0,groupWidth,groupHeight),"Equalizer");
- for(var i = 0; i < equalizerValues.Length; i++)
- {
- equalizerValues[i] = GUI.VerticalSlider(Rect(i * 30 + 20,30,20,200),equalizerValues[i],0.0,1.0);
- }
- GUI.EndGroup();
- }
結果應類似下面的:
當使用水平滑塊時,最小值在滑塊的左邊最大值在滑塊的右邊.當使用豎直滑塊時,最小值在頂部,最大值在滑塊底部.
您可能也想看看GUI.HorizontalScrollbar和GUI.VerticalScrollbar它們類似於滑塊,但具有不同的功能。
GUI.WINDOW和GUI.DRAGWINDOW
GUI類提供了在屏幕上繪制窗口的函數,窗口可以使用外部函數(除了OnGUI)來渲染窗口的內容.
如果在窗口的回調函數中使用GUI.DragWindow函數,那窗口將會是可拖動的.
下面的代碼創建了一個簡單而可拖動的窗口
DraggableWindow.js
- #pragma strict
- //窗口的初始位置以及大小
- private var windowRect0 = Rect(20,20,150,0);
- function OnGUI()
- {
- //渲染窗口ID為0
- windowRect0 = GUILayout.Window(0,windowRect0,WindowFunction,"Draggable Window");
- }
- function WindowFunction()
- {
- GUILayout.Label("This is a draggable window!");
- //窗口的拖條(drag-strip),坐標相對於窗口的左上角
- GUI.DragWindow(Rect(0,0,150,20));
- }
如果在新場景中將腳本應用到GameObject上,你就會看到窗口,點擊並拖動窗口標題你就可以在屏幕上把窗口拖來拖去.
在窗口中可以放置任意數量的控件.如果你想要Unity在窗口中自動布局控件(就像例子中一樣)那你應該使用GUILayout.Window函數而不是GUI.Window函數.當使用GUILayout.Window函數時,unity會自動修改窗口的高度以適應內容.就像上面的例子展示的一樣.
自動布局
在這里展示的絕大數例子,我都是用GUI類來創建菜單.GUI類需要我們手動地在屏幕上放置空間,在某些情況下,手動擺放控件很有用,但如果你想要unity為你自動布局控件,那你需要使用GUILayout類.這個類提供了許多像GUI一樣的功能,但不要求你指定控件的大小
GUILayout.beginHorizontal和guilayout.beginVertical
默認情況下,當使用GUILayout函數時所有的視圖中的組件都會豎直排列.你可以使用guilayout.beginhorizontal和guilayout.endhorizontal靜態函數使控件相鄰排放.每出現一次guilayout.BeginVertical必須有對應的GUILayout.EndVertical與其對應,每出現一次GUILayout.BeginHorizontal則必須有對應的GUILayouyudHorizontal與其對應
下面的例子展示了如何使用豎直和水平布局來創建復雜的表格
UserForm.js
- #pragma strict
- private var firstName:String = "First Name";
- private var lastName:String = "Last Name";
- private var age:uint = 0;
- private var submitted:boolean = false;
- private var windowRect0:Rect;
- function Start()
- {
- }
- function OnGUI()
- {
- var screenWidth = Screen.width;
- var screenHeight = Screen.height;
- var windowWidth = 300;
- var windowHeight = 180;
- var windowX = (screenWidth - windowWidth) / 2;
- var windowY = (screenHeight - windowHeight) / 2;
- //將窗口放置到屏幕中間
- windowRect0 = Rect(windowX,windowY,windowWidth,windowHeight);
- GUILayout.Window(0,windowRect0,UserForm,"User information");
- }
- function UserForm()
- {
- GUILayout.BeginVertical();
- //first name
- GUILayout.BeginHorizontal();
- GUILayout.Label("First Name",GUILayout.Width(80));
- firstName = GUILayout.TextField(firstName);
- GUILayout.EndHorizontal();
- //last name
- GUILayout.BeginHorizontal();
- GUILayout.Label("Last Name",GUILayout.Width(80));
- lastName = GUILayout.TextField(lastName);
- GUILayout.EndHorizontal();
- //Age
- GUILayout.BeginHorizontal();
- GUILayout.Label("Age",GUILayout.Width(80));
- var ageText = GUILayout.TextField(age.ToString());
- var newAge = age;
- if(uint.TryParse(ageText,newAge))
- {
- age = newAge;
- }
- GUILayout.EndHorizontal();
- if(GUILayout.Button("Submit"))
- {
- submitted = true;
- }
- if(GUILayout.Button("Reset"))
- {
- firstName = "First Name";
- lastName = "Last Name";
- age = 0;
- submitted = false;
- }
- if(submitted)
- {
- GUILayout.Label("submitted!");
- }
- GUILayout.EndVertical();
- }
這段代碼看上去挺復雜的.這里面有些東西如果你不熟悉腳本編寫的話可能看起來不熟悉.需要強調的一點是我使用一個靜態窗口(不可拖動)的來把控件據合起來.窗口需要一個繪制其內部結構的函數.為此我是用了UseForm函數.
用戶表格開頭使用豎直布局.因為當使用自動布局時豎直布局是默認的,所以顯式的指定它是可有可無的.
之后每一個字段都是用水平布局創建,標簽旁邊跟着文本.標簽被顯式的使用GUILayout.Width()函數設置為80像素.我這樣做是為了確保所有的文本都整潔的對齊.文本域擴展填充窗口的剩余部分.
在兩個文本之后,添加了兩個豎直布局的按鈕.按鈕將擴展填充區域其高度由按鈕的內容決定.
不要忘了常使用GUILayout.EndVertical()或GUILayout.EndHorizontal()函數結果豎直或水平布局.
腳本UserForm.js將會產生如下結果:
你可能也想看看GUI.BeginGroup,GUILayout.BeginArea和GUI.BeginScrollView.所有的這些函數都可以被用於創建組(或者可以自動布局的區域),這些組或者區域可以用於保持控件在特定區域內聚合起來.
樣式和皮膚
unity為所有的GUI控件提供了默認的外觀.作為一個快速解決方案的話默認的樣式足夠用了,但你可能不想在你要面市的游戲中使用unity的默認GUI樣式.
GUISkin
使用自定義的GUISkin你可以更改按鈕,標簽,滑塊,和滾動條的外觀.
GUISkin是一個套件(asset),你可以從主菜單中選擇Assets>Create>GUISkin來創建它.
如果你在項目視圖(project view)里選擇了GUISkin,你可以為你可創建的多種控件編輯單獨的樣式設置.
要使用你自定義的皮膚將默認皮膚替換掉,在GUI腳本里面設置GUI.skin屬性為你自定義的皮膚.將GUI.skin屬性設置為null將還原回默認的GUISkin.
GUIStyle
GUISkin只是一系列GUIStyle樣式的集合.GUIStyle定義了一個控件所有可變狀態的樣式.一個控件有以下狀態:
- 正常狀態:控件的默認狀態.鼠標既沒有懸停到控件上控件也沒有獲得系統焦點.
- 懸停狀態:鼠標當前懸停在控件上
- 注視狀態:當前正選擇控件,選中的控件將會接受鍵盤輸入.這個狀態對於按鈕和可編輯文本控件.
- 活動狀態:控件被點擊,這個狀態對於按鈕,滑塊和滾動條都是合法的.
GUIStyle可以在沒有GUISkin的情況下使用以便改寫控件的樣式.要使用GUIStyle,簡單的在腳本中創建一個GUIStyle類型的公開變量並在檢視器(inspecot),當你想要為控件應用樣式.簡單把樣式作為控件函數的最后一個參數數傳進去就行.
作為練習,下載下面的Unity包並將其導入一個新的Unity工程中:
使用下面代碼,創建一個名稱為LevelSelect.js的腳本.
LevelSelect.js
- #pragma strict
- function OnGUI()
- {
- var groundWidth = 120;
- var groundHeight = 150;
- var screenWidth = Screen.width;
- var screenHeight = Screen.height;
- var groupx = (screenWidth - groundWidth) / 2;
- var groupy = (screenHeight - groundHeight) / 2;
- GUI.BeginGroup(Rect(groupx,groupy,groundWidth,groundHeight));
- GUI.Box(Rect(0,0,groundWidth,groundHeight),"Level Select");
- if(GUI.Button(Rect(10,30,100,30),"Level 1"))
- {
- Application.LoadLevel(1);
- }
- if(GUI.Button(Rect(10,70,100,30),"Level 2"))
- {
- Application.LoadLevel(2);
- }
- if(GUI.Button(Rect(10,110,100,30),"Level 3"))
- {
- Application.LoadLevel(3);
- }
- GUI.EndGroup();
- }
你可能注意到這就是上面按鈕例子里面的代碼,但是現在我們要為這些按鈕設置一套自定的皮膚.
在檢視器(inspector)中,在Button Style屬性中設置如下選項:
- Normal.Background: roundedButton_Normal.psd
- Normal.Text Color: (0, 0, 0, 255)
- Hover.Background: roundedButton_Hover.psd
- Hover.Text Color: (0, 0, 0, 255)
- Active.Background: roundedButton_Active.psd
- Active.Text Color: (80, 80, 255, 255)
- Border.Left: 6
- Border.Right: 6
- Border.Top: 6
- Border.Bottom: 6
- Padding.Left: 6
- Padding.Right: 6
- Padding.Top: 12
- Padding.Bottom: 6
- Font: SHOWG
- Alignment: Middle Center
你會得到類似於下面的展示的.
Demo
下面是一個交互demo,它展示了如何使用多種控件,demo包含如下場景:
- GUI基礎:描述了OnGUI函數並展示了一個使用按鈕和箱子(box)組件的例子
- 控件:這個場景展示了多種你能創建的組件
- 自定義:如何創建皮膚和樣式
- 布局:講述了固定和自動布局模式.也展示了一個使用豎直和水平布局的例子
- 擴展Unity編輯器:這個場景展示了一個如何在unity中創建自定義編輯器窗口.下載這個unity工程並將其加載到Unity來看自定義的編輯器窗口.
下載並解壓zip文件,在unity編輯器中打開Assets/Scenes/Introduction.unity場景文件