Search

㊗ 新的一年,祝福讀者們 2020 新年新希望!! ◆ Welcome to Laird Studio! 歡迎蒞臨萊爾德工作室! ◎ Android Studio 基礎教學籌備中,敬請期待! ☏ 對網站有任何問題或建議,都非常歡迎使用留言板或至 Facebook 粉絲團發訊息,讓我們知道您的想法 (੭ु´ ᐜ `)੭ु

2016年7月4日 星期一

[ 客座文章 ] [ Unity ] UGUI 原理篇




◆ 此篇客座文章由 ARKAI Studio 投稿,講述有關 Unity UGUI 的各種原理,幫助讀者們學習更多有關 Unity 技術上的知識,敬請大家多加參考利用!







Unity UGUI 原理篇 (一):Canvas 渲染模式

2016-03-25  k79k06k02k  UGUI, Unity

原文出處:http://wp.me/p7hYyg-34

目標:
了解各種不同 UI Render Mode


使用環境 與 版本

☻ Window 7
☻ Unity 5.2.4



Canvas

Canvas Component 是UI布局和渲染的抽象空間,所有的UI都必須在此元素之下(子物件),簡單來 Canvas 就是渲染 UI 的組件




Render Mode

UI渲染的方式,有以下三種

Screen Space – Overlay:螢幕空間 – 覆蓋

Screen Space – Camera:螢幕空間 – 攝影機

World Space:世界座標空間



Screen Space – Overlay



在此模式下不會參照到Camera,UI直接顯示在任何圖形之上

1.Pixel Perfect:可以使圖像更清晰,但是有額外的性能開銷,如果在有大量UI動畫時,動畫可能會不平順

2.Sort Order:深度,該越高顯示越前面



Screen Space – Camera



使用一個Camera作為參照,將UI平面放置在Camera前的一定距離,因為是參照Camera,如果螢幕大小、分辨率、Camera視錐改變時UI平面會自動調整大小。如果Scene中的物件(GameObject)比UI平面更靠近攝影機,就會遮擋到UI平面。

1.Render Camera:用於渲染的攝影機

2.Plane Distance:與Camera的距離

3.Sorting Layer:Canvas屬於的排序層,在 Edit->Project Setting->Tags and Layers->Sorting Layers 進行新增,越下方的層顯示越前面

4.Order in Layer:Canvas屬於的排序層下的順序,該越高顯示越前面



World Space



把物體當作世界座標中的平面(GameObject),也就是當作3D物件,顯示3D UI

1.Event Camera:處理UI事件(Click、Drag)的Camera,所設定的Camera才能觸發事件



參考資料

Unity – Manual: Canvas

http://docs.unity3d.com/Manual/class-Canvas.html







Unity UGUI 原理篇(二):Canvas Scaler 縮放核心

2016-03-28  k79k06k02k  UGUI, Unity

原文出處:http://wp.me/p7hYyg-o

目標:
了解各種不同 UI Scale Mode
Pixels Per Unit 每單位像素
Canvas Scale Factor 縮放因子
Reference Resolution(預設螢幕大小)、Screen Size、Canvas Size 之間的關係與算法


使用環境 與 版本

☻ Window 7
☻ Unity 5.2.4



Canvas Scaler

Canvas Scaler是Unity UI系統中,控制UI元素的總體大小和像素密度的Compoent,Canvas Scaler的縮放比例影響著Canvas下的元素,包含字體大小和圖像邊界。


Size

Reference Resolution:預設螢幕大小

Screen Size:目前螢幕大小



Canvas Size:Canvas Rect Transform 寬高



Scale Factor


用於縮放整個Canvas,而且調整Canvas Size與Screen Size一樣



先來看一段官方程式碼

CanvasScaler.cs   C#

protected void SetScaleFactor(float scaleFactor)
{
    if (scaleFactor == m_PrevScaleFactor)
        return;

    m_Canvas.scaleFactor = scaleFactor;
    m_PrevScaleFactor = scaleFactor;
}

程式碼可以看出,Canvas Scaler 透過設定Canvas下的Scale Factor,縮放所有在此Canvas下的元素



當Scale Factor為1時,Screen Size (800*600)、Canvas Size(800*600),圖片大小1倍




當Scale Factor為2時,Screen Size (800*600)、Canvas Size(400*300),圖片大小2倍




在當Scale Factor為2時,Scale Factor 會調整整個Canvas 的大小,並讓他的大小跟Screen Size一樣,運算後Canvas Size放大2倍,剛好等於Screen Size,而底下的圖片會放大2倍



UI Scale Mode

Constant Pixel Size

Canvas Size 始終等於 Screen Size,透過Scale Factor直接縮放所有UI元素



1. Scale Factor:透過此Factor縮放所有在此Canvas下的元素

2. Reference Pixels Per Unit:

先介紹圖片檔設定中的Pixels Per Unit,意思是在這張Sprite中,世界座標中的一單位由幾個Pixel組成



這邊使用的測試圖片為原始大小100*100 的圖檔,這邊統稱測試圖



舉例來,場景中有一個1*1 Cube ,與一個Sprite圖片指定為測試圖,兩者的Transform Scale 都為 1

當 Pixels Per Unit=100,每單位由 100 Pixel組成,Sprite 是100*100 Pixels,那 Sprite 在世界座標中大小就會變成 100/100 * 100/100 = 1*1 Unit

(左:Cube ,右:Sprite)



當 Pixels Per Unit=10,每單位由 10 Pixel組成,Sprite 是100*100 Pixels,那 Sprite 在世界座標中大小就會變成 100/10 * 100/10 = 10*10 Unit

Pixels Pre Unit-3
(左:Cube ,右:Sprite)



結論:

Unity中一單位等於 100 Pixels

由此可以推導出公式:

Sprite 在世界座標中大小 = 原圖大小(Pixels) / Pixels Per Unit



讓我們回到 Reference Pixels Per Unit,官方解釋是,如果圖片檔有設定Pixels Per Unit,則會將Sprite 的 1 pixel 轉換成 UI 中的 1 pixel


Image.cs   (C#)

public float pixelsPerUnit
{
    get
    {
        float spritePixelsPerUnit = 100;
        if (sprite)
            spritePixelsPerUnit = sprite.pixelsPerUnit;

        float referencePixelsPerUnit = 100;
        if (canvas)
            referencePixelsPerUnit = canvas.referencePixelsPerUnit;

        return spritePixelsPerUnit / referencePixelsPerUnit;
    }
}

上面官方程式碼,可以看出 Image 透過 spritePixelsPerUnit / referencePixelsPerUnit 方式算出新的 pixelsPerUnit


Image.cs   (C#)

public override void SetNativeSize()
{
    if (overrideSprite != null)
    {
        float w = overrideSprite.rect.width / pixelsPerUnit;
        float h = overrideSprite.rect.height / pixelsPerUnit;
        rectTransform.anchorMax = rectTransform.anchorMin;
        rectTransform.sizeDelta = new Vector2(w, h);
        SetAllDirty();
    }
}

在設定 Image 圖片大小時,是把 寬高 / pixelsPerUnit

實作一下,建立一個Canvas參數如下

Reference Pixels Per Unit-1






Canvas底下建立一個Image,Sprite設定為測試圖,參數如下

Reference Pixels Per Unit-2


這邊做4種不同的測試:測試方式是修改 Reference Pixels Per Unit 與 Pixels Per Unit 後,點下 Image Compoent 的 Set Native Size來設定圖片原始大小,藉此看到圖片變化



上表可以看出當數改變時,圖片預設大小也會改變

由此可以推導出公式

UI大小 = 原圖大小(Pixels)  /  (Pixels Per Unit / Reference Pixels Per Unit)



Scale With Screen Size

透過設定的Reference Resolution(預設螢幕大小)來縮放




1. Reference Resolution:預設螢幕大小

2. Screen Match Mode:縮放模式



先來看官方的算法

CanvasScaler.cs   C#

Vector2 screenSize = new Vector2(Screen.width, Screen.height);

float scaleFactor = 0;
switch (m_ScreenMatchMode)
{
    case ScreenMatchMode.MatchWidthOrHeight:
    {
        // We take the log of the relative width and height before taking the average.
        // Then we transform it back in the original space.
        // the reason to transform in and out of logarithmic space is to have better behavior.
        // If one axis has twice resolution and the other has half, it should even out if widthOrHeight value is at 0.5.
        // In normal space the average would be (0.5 + 2) / 2 = 1.25
        // In logarithmic space the average is (-1 + 1) / 2 = 0
        float logWidth = Mathf.Log(screenSize.x / m_ReferenceResolution.x, kLogBase);
        float logHeight = Mathf.Log(screenSize.y / m_ReferenceResolution.y, kLogBase);
        float logWeightedAverage = Mathf.Lerp(logWidth, logHeight, m_MatchWidthOrHeight);
        scaleFactor = Mathf.Pow(kLogBase, logWeightedAverage);
        break;
    }
    case ScreenMatchMode.Expand:
    {
        scaleFactor = Mathf.Min(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);
        break;
    }
    case ScreenMatchMode.Shrink:
    {
        scaleFactor = Mathf.Max(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);
        break;
    }
}



a. Expand(擴大):將Canvas Size進行寬或高擴大,讓他高於Reference Resolution,計算如下

scaleFactor = Mathf.Min(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);

意思是分別算出長寬 ,”Screen Size” 佔了 “Reference Resolution” 的比例,在求小的

舉例來,Reference Resolution為1280*720,Screen Size為800*600

ScaleFactor Width: 800/1280=0.625

ScaleFactor Height:600/720=0.83333



套用ScaleFactor公式:Canvas Size = Screen Size / Scale Factor

Canvas Width:800 / 0.625 = 1280

Canvas Height:600 / 0.625 = 960

Canvas Size 為 1280*960,高度從720變成了960,最大程度的放大(顯示所有元素)



b. Shrink(收縮):將Canvas Size進行寬或高收縮,讓他低於Reference Resolution,計算如下

scaleFactor = Mathf.Max(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);

意思是分別算出長寬 ,”Screen Size” 佔了 “Reference Resolution” 的比例,在求大的

舉例來,Reference Resolution為1280*720,Screen Size為800*600

ScaleFactor Width: 800/1280=0.625

ScaleFactor Height:600/720=0.83333



套用ScaleFactor公式:Canvas Size = Screen Size / Scale Factor

Canvas Width:800 / 0.83333 = 960

Canvas Height:600 / 0.83333 = 720

Canvas Size 為 960*720,寬度從1280變成了960,最大程度的縮小



c. Match Width or Height:根據Width或Height進行混合縮放,計算如下

float logWidth = Mathf.Log(screenSize.x / m_ReferenceResolution.x, kLogBase);
float logHeight = Mathf.Log(screenSize.y / m_ReferenceResolution.y, kLogBase);
float logWeightedAverage = Mathf.Lerp(logWidth, logHeight, m_MatchWidthOrHeight);
scaleFactor = Mathf.Pow(kLogBase, logWeightedAverage);

分別對ScaleFactor Width、Height取對數後,再進行平均混合,那為什麼不直接使用March對Width、Height進行混合呢??,讓我們來比較一下

假設Reference Resolution為400*300,Screen Size為200*600 大小關係是

Reference Resolution Width 是 Screen Size Width的2倍

Reference Resolution Height 是 Screen Size 的0.5倍

看起來會像下圖



當March為0.5時,ScaleFactor應該要是 1 (拉平)

ScaleFactor Width: 200/400=0.5

ScaleFactor Height:600/300=2


一般混合:

ScaleFactor = March * ScaleFactor Width + March * ScaleFactorHeight

ScaleFactor = 0.5 * 0.5 + 0.5 * 2 = 1.25


對數混合:

logWidth:log2(0.5) = -1

logHeight:log2(2) = 1

logWeightedAverage:0

ScaleFactor:2^0 = 1

scaleFactor一般混合為1.25,對數混合為1,結果很明顯,使用對數混合能更完美的修正大小



Constant Physical Size

透過硬體設備的Dpi(Dots Per Inch 每英吋點數),進行縮放



1. Physical Unit:使用的單位種類




2. Fallback Screen DPI:備用Dpi,當找不到設備Dpi時,使用此

3. Default Sprite DPI:預設的圖片Dpi


C#

float currentDpi = Screen.dpi;
float dpi = (currentDpi == 0 ? m_FallbackScreenDPI : currentDpi);
float targetDPI = 1;
switch (m_PhysicalUnit)
{
    case Unit.Centimeters: targetDPI = 2.54f; break;
    case Unit.Millimeters: targetDPI = 25.4f; break;
    case Unit.Inches:      targetDPI =     1; break;
    case Unit.Points:      targetDPI =    72; break;
    case Unit.Picas:       targetDPI =     6; break;
}

SetScaleFactor(dpi / targetDPI);
SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit * targetDPI / m_DefaultSpriteDPI);



結論:

ScaleFactor 為 “目前硬體dpi” 佔了 “目標單位” 的比例

ReferencePixelsPerUnit 要與目前的Dpi在運算求出新的,再傳入Canvas中求出大小,公式如下:


新的 Reference Pixels Per Unit = Reference Pixels Per Unit * Physical Unit / Default Sprite DPI


UI大小 = 原圖大小(Pixels)  /  (Pixels Per Unit / 新的 Reference Pixels Per Unit)



參考資料

Unity – Manual: Canvas

http://docs.unity3d.com/Manual/class-Canvas.html







Unity UGUI 原理篇(三):RectTransform

2016-05-02  k79k06k02k  UGUI, Unity

原文出處:http://wp.me/p7hYyg-5o

目標:
理解 RectTransform Component
Anchor (錨點)
Pivot (支點)
Blue Print Mode 與 Raw Edit Mode


使用環境 與 版本

☻ Window 7
☻ Unity 5.2.4



RectTransform



RectTransform 是 Transform 的 2D 對應 Component,Transform 表示單個點,RectTransform 表示一個2D矩形(UI空間),如果父子物體都有RectTransform,那麼子物體可以指定在父物體矩形中的位置與大小,簡單來RectTransform 就是定義UI元素的位置、旋轉、大小



Anchor (錨點)



物體的錨點(對齊點),如果父子都有 RectTransform 情況下,子物體可以依據 Anchor 對齊到父物體,又分為 Min 與 Max 位置,如下圖物體四周有4個三角形





Anchor位置座標與關係

當我們使用滑鼠點選4個三角形調整Anchor時,會貼心的出現比例訊息,此比例是子物體在父物體中的縮放比例




當Canvas 下有1張圖 Anchor Min 與 Anchor Max 皆為 (0.5 , 0.5),如下左圖部分

如果將Anchor Min調整為(0.3, 0.5) ,Anchor Max調整為 (0.5, 0.7),如下右圖部分

注意看 左圖 Pos X、Pos Y、Width、Height ,會改變為 右圖 Left、Top、Right、Buttom

Anchor_15


因為當 Anchor 在同一點時,顯示的是物體的座標與大小,當 Anchor 不在同一點時(此時會形成矩形),顯示的會是 Anchor 矩形填充空間,如下圖,(P.S.在我們移動物體時會貼心的顯示目前與 Anchor 距離關係)



上面看完一定還是不了解怎麼運作,讓我們來透過實例了解一下

Canvas 下有5張圖,Anchor Min 與 Anchor Max 皆為 (0.5 , 0.5),物體的位置會對齊到父物體的中心,當父物體大小改變時,情形如下
 


Canvas 下有1張圖,Anchor Min 與 Anchor Max 皆為 (0.0, 1.0),物體的位置會對齊到父物體的左上角,當父物體大小改變時,情形如下,物體會固定在左上角

Anchor_09


Canvas 下有1張圖,Anchor Min 為 (0.0, 0.0), Anchor Max 為 (1.0, 0.0),物體的位置會對齊到父物體的左下角與右下角,當父物體大小改變時,情形如下,物體寬度會隨著父物體改變

Anchor_10


由上面的幾個實例可以知道,子物體會依據所設定 Anchor 對齊到父物體,當父物體大小改變時,透過 Anchor 更新子物體,上面有提到當我們點選4個三角形調整Anchor時,畫面會貼心的出現比例訊息,相信有經驗的人一定知道該比例的用意,此比例就是子物體在父物體中的縮放比例,以下舉例



原來數

Parent Size (400, 350)

Image Size (120, 105)

Anchor Min 為 (0.2, 0.5), Anchor Max 為 (0.5, 0.8)



Parent Size 一半時數

Parent Size (200, 175)

Image Size (60, 52.5)

Image Size Width  = 400 * 50% * 30% = 60

Image Size Height = 350 * 50% * 30% = 52.5

Anchor Min 為 (0.2, 0.5), Anchor Max 為 (0.5, 0.8)

經由上面可以得知父物體在縮小2倍後,父物體透過子物體的 Anchor 比例更新子物體,透過這種方式,我們可以達到不同螢幕解析度自動改變UI大小與位置



Anchor Presets



點選 RectTransform 左上角,可以開Anchor Presets 工具,這邊列出了常用的 Anchor ,可以快速套用,按住Shift 可以連同 Pivot 一起改變,按住 Alt 可以連同位置一起改變



Pivot (支點)

物體自身的支點,影響物體的旋轉、縮放、位置,改變 UI Pivot 必須先開控制面板的 Pivot 按鈕,如下圖



當 Pivot (0.5, 0.5)

Pivot_02


當 Pivot (0, 1)

Pivot_03


Blue Print Mode(藍圖模式) 、 Raw Edit Mode(原始编辑模式)



Blue Print Mode (藍圖模式)

忽略了物體的 Local Rotation 和 Local Scale,方便以原來的旋轉與大小調整物體 



Raw Edit Mode (原始編輯模式)

在 Inspector 中調整 Pivot 和 Anchor 時,物體會維持目前的位置與大小(Inspector 中數部分),調整情形如下,請注意數部分

Inspector 中調整  Pivot

RawEditMode_02


Inspector 中調整  Anchor

RawEditMode_01



參考資料

Unity – Manual: Basic Layout

UnityのuGUIのレイアウト調整機能について解説してみる(RectTransform入門)
http://tsubakit1.hateblo.jp/entry/2014/12/19/033946








Unity UGUI 原理篇()Event System Manager 事件與觸發

2016-05-19  k79k06k02k  UGUI, Unity

原文出處:http://wp.me/p7hYyg-76

目標:
Event System 事件系統
Input Module 輸入控制
Graphic Raycaster
Physics Raycaster Physics 2D Raycaster


使用環境 版本

☻ Window 7
☻ Unity 5.2.4



Event System

在建立出UI時,Unity會自動幫我們建立Event System物件,此物件是基於滑鼠、觸摸、鍵盤的輸入方式,傳送 Event Object 上,物件下有3個組件,分別為Event System ManagerStandalone Input ModuleTouch Input Module





控管所有Event,負責將滑鼠、觸摸、鍵盤輸入方式(Input Module) 被選中的 Object 互相協調,每個 “Update” Event System 都會接收所有呼叫,並判斷出這一瞬間要使用哪種Input Modules


Event System Info

當按下Play後,點選Event System物件,會在inspector顯示 選中物件、位置、接收事件的Camera等等資訊



First Selected

執行時第一次要選擇的Object,例如:選擇為 InputField (輸入框) ,按下Play後就會將游標 Force InputField



Send Navigation Events

是否開UI導航功能,導航功能是可以用鍵盤的上”、”下”、”左”、”右”、”Cancel(Esc)”、”Sumit(Enter)” 控制選擇的UI

舉例:如果畫面上有多個選單按鈕,我們可以設定按鈕上的 Navigation Options 這裡使用Explicit方式,來指定按下鍵盤上”、”下”、”左”、”右” 要選取哪一個物件

Select On Up :當鍵盤按下上” 鍵後要選擇的物件,DownLeftRight 不多做贅述




Visualize Buttin 按下Visualize可以看到物件指向的黃線



Drag Threshold

Drag Event靈敏度,越低越靈敏




電腦輸入控制模組,主要影響著滑鼠與鍵盤的輸入,使用 Scene 中的 Raycasters 計算哪個元素被點中,並傳遞 Event

Horizontal Axis

代表 Input Module 中的 Horizontal Axis,可以被設定為 Input Manager 中的Vertical AxisSubmit ButtonCancel Button 不多做贅述



Input Actions Per Second

每秒能輸入的最大按鈕與滑鼠次數



Repeat Delay

重複輸入的延遲



事件執行完整流程

鍵盤輸入

1.Move Event:透過 input manager 驗證輸入 axisleftrightupdown 按鈕,傳遞給 selected object

2.Submit Cancel Button:物件已經 Preesed (按下)時,透過 input manager 驗證輸入  submit cancel 按鈕,傳遞給 selected object



滑鼠輸入

1.如果是新的按下

a.傳送 PointerEnter event

b.傳送 PointerPress event

c. drag 相關暫存

e.傳送 BeginDrag event

f.設定Event system中的 selected object 為按下的Object

2.如果是持續按下(Drag)

a.處理移動相關

b.傳送 Drag event

c.處理 Drag 時跨到其他物體的 PointerEnter eventPointerExit event

3.如果是釋放(滑鼠放開)

a.傳送 PointerUp event

b.如果滑鼠放開與按下時的物件一樣,傳送 PointerClick event

c.如果有 drag 相關暫存,傳送 Drop event

d.傳送EndDrag event

4.滑鼠中鍵滾輪傳送scroll event





觸摸輸入模組,主要用於移動設備上,可以透過TouchDrag的方式響應,使用 Scene 中的 Raycasters 計算哪個元素被點中,並傳遞 Event

事件執行完整流程

Standalone Input Module 的滑鼠輸入一樣,滑鼠點下想成觸摸即可



4.Event System 觸發流程

1.使用者輸入(滑鼠、觸摸、鍵盤)

2.透過 Event System Manager 決定使用 Standalone 還是 Touch Input Module

3.決定使用的 Input Module 後,透過 Scene 中的 Raycasters 計算哪個元素被點中

4.傳送Event



組件位置:Unity Menu Item → Component → Event → Graphic Raycaster



建立 Canvas 物件時下的其中一個 ComponentRaycaster 會觀察 Canvas下所有圖形,並檢測是否被擊中,射線檢測其實就是指定位置與方向後,投射一條隱形線並判斷是否有碰撞體在線上,射線檢測這點官方已經有詳細,這裡用於判斷是否點選到UI圖形



Ignore Reversed Graphics

背對著畫面的圖形,射線檢測是否要忽略此圖形

舉例:當圖形Y軸進行旋轉180後,此時是背對著畫面,這時是如果有打勾,就會忽略不檢測此圖形



Blocked Objects Blocking Mask

主要用於當Canvas Component Render Mode 使用 World Space 或是 Camera Space 時,UI 前有 3D 或是 2D Object 時,將會阻礙射線傳遞到 UI 圖形

Blocked Objects 阻礙射線的 Object 類型

Blocking Mask 勾選的 Layer 將會阻礙射線

舉例:如果畫面上有一個 Button Cube 位置故意重疊,現在點擊重疊之處會發現 Button 還是會被觸發




如果將Cube Layer 改為 Test01 Blocked Objects 設定為 Three DBlocking Mask 只勾選 Test01,再次點選重疊區域,會發現 Cube 會阻礙射線檢測,此時按鈕會接收不到射線,當然也不會有反應



Physics Raycaster (物理物件 射線檢測員)

組件位置:Unity Menu Item → Component → Event → Physics Raycaster

透過 Camera 檢測 Scene 中的 3D GameObject(必須有 Collider Component),有實現 Event Interfaces 接口的物件將會接收到 Message 通知,例如能讓 3D GameObject 能接收 點下Event 或是 拖拉Event 等等…..看更多Event 請點我

接下來讓我們透過實例理解

1.建立 EventSystem,進行 Event 處理

物件位置:Unity Menu Item → GameObject → UI → EventSystem



2.Camera下增加 Physics Raycaster Component,用來觀察射線




3.實現 Event Interfaces 接口,這裡有兩種方式,一種是建立 Script 直接實作 Interfaces ,一種是使用Event Trigger Component



第一種 建立 Script 直接實作 Interfaces

a.建立一個 Script,實作 Event Interfaces

EventTest.cs   (C#)

using UnityEngine;
using UnityEngine.EventSystems;

public class EventTest : MonoBehaviour, IPointerDownHandler
{
    public void OnPointerDown(PointerEventData eventData)
    {
        print(gameObject.name);
    }
}


Line. 2using  UnityEngine.EventSystems 匯入命名空間

Line. 4:繼承 Event Interfaces,這裡是IPointerDownHandler(點下事件)看更多Event 請點我

Line. 6~8:實作方法,傳入 PointerEventData 為事件資料

b.建立一個3D物件(此稱為Cube),並增加 BoxCollider Component



c. Script 放至 Cube 下,Inspector 中會出現 Intercepted Events 資訊,顯示出正在監聽的 Event



d.此時點擊 Cube 就會通知 OnPointerDown 方法並傳入事件資訊



第二種 使用Event Trigger Component 實作 Interfaces

a.建立一個 Script,實作方法,用於接收 Event Trigger 通知

EventTriggerTest.cs   (C#)

using UnityEngine;
using UnityEngine.EventSystems;

public class EventTriggerTest : MonoBehaviour
{
    //BaseEventData 動態傳入事件資訊
    public void OnPointerDown(BaseEventData eventData)
    {
        print("OnPointerDown--BaseEventData");
    }

    //純呼叫
    public void OnPointerDown()
    {
        print("OnPointerDown--non");
    }

    //傳入int
    public void OnPointerDown(int i)
    {
        print("OnPointerDown--int");
    }
}


Line. 2using  UnityEngine.EventSystems 匯入命名空間

Line. 6~8:實作方法,這邊實作3

b.建立一個3D物件(此稱為Cube),並增加 BoxCollider Component



c. Script 放至 Cube

d.Cube 下加入 Event Trigger Component,主要接收來至 Event System Event ,並呼叫有實作的 Event

組件位置:Unity Menu Item → Component → Event → Event Trigger

e.點選 Add New Event Type 選擇要實作的 Event 類型 ,這裡使用PointerDown(點下事件)舉例




f.此時會新增一個UnityEvents,是一種透過編輯器設定的方式,設定 Event 觸發時要通知的方法與屬性,詳細可以參考以下,這邊簡單




點下 “+” 按鈕後,拖入要通知的Scene GameObjectUnity Event 就會尋找此 GameObject 上所有 Public 的方法與屬性 ,就可以新增 Event 觸發時通知的方法” 預修改屬性”

g.GameObject 拖入 Cube,通知方法設定 Script 中的3個方法




h.此時點擊 Cube 就會觸發 PointerDown ,通知 Script 中的3個方法



4.實作注意點:

Scene 必需有 EventSystem GameObject

Camera 必需有 Physics Raycaster Component

3D GameObject 必須有 Collider Component

實作 Event Interfaces 的方式,一種是建立 Script 直接實作 Interfaces ,一種是使用 Event Trigger Component,由上面實作可以知道,使用 Event Trigger 的方式可以使用編輯器設定,設定觸發時的通知方法” 修改屬性”,且更為彈性



Physics 2D Raycaster
組件位置:Unity Menu Item → Component → Event → Physics 2D Raycaster

Physics Raycaster 只差在於,Physics 2D Raycaster 是檢測 Scene 中的 2D GameObject,當然 GameObject 上必須有 Collider2D Component,這邊不再贅述



後記
我們透過輸入的方式不同與 Raycaster 的關係,理解了整個 Event System 觸發流程,而且也知道怎麼實作 Event 與應用 Event,不管是3D2DUI物件都可以方便的套用,大大提升開發速度、簡化語法,可是非常方便的功能



參考資料

Unity – Manual: Event System
Unity – Manual: UnityEvents

Unity – Raycasting

胡亂隨便寫 – Unity:使用 UnityEngine.Events 讓程式更靈活、穩定
http://godstamps.blogspot.tw/2015/10/unity-unityengineevents.html







Unity UGUI 原理篇(五):Auto Layout 自動佈局

2016-06-19  k79k06k02k  UGUI, Unity

原文出處:http://wp.me/p7hYyg-8K

目標:
 Auto Layout System 架構
 Layout Element 元素大小
 Horizontal、Vertical 、Grid Layout Group 元素排列
 Content Size 與 Aspect Ratio Fitter 大小控制


使用環境 與 版本

☻ Window 7
☻ Unity 5.2.4



Auto Layout System

Auto Layout System 是基於 Rect Transform Layout System 之上的系統,自動調整一個或多個的元素大小、位置、間格,又分為 Layout Controllers(父物件) 與 Layout Elements(子物件) 兩部分,一個簡單的 Auto Layout 架構如下 (此處介紹理論,實作留到後面)

AutoLayoutSystem_01


Layout Element (子物件)


代表著每個元素的大小,擁有資訊如下:



點選UI後,可以在 Inspector 最下方切換為 Layout Properties 看到資訊




Layout Controllers 透過不同的佈局方式,取得 Layout Element size 分配子物件,基本原則如下

☻ 首先分配 Minimum Size

☻ 如果還有足夠空間,分配 Preferred Size

☻ 如果還有額外空間,分配 Flexible Size



從以下圖片可以看出圖片寬度的增長方式 (此處介紹理論,實作留到後面)



☻ 首先分配 Minimum Size (300,紅色部分)

☻ 如果還有足夠空間,分配 Preferred Size (300~500,綠色部分)

☻ 如果還有額外空間,分配 Flexible Size:1 (500~700,藍色部分)



比較特別的是 Flexible,他是代表著整個大小的比例,如果 Layout 下有2個物體,分別給 Flexible 設定為 0.3 與 0.7,那比例就會變成下圖 (3:7)



另外要注意的是,TextImage Component 會根據內容大小自動分配 Preferred Size



Layout Controllers (父物件)

Layout Group

不會控制 Layout Controllers (父物件)自身大小,而是控制子物件大小與位置,在大多數情況下,根據每個元素的 minimum、preferred、flexible 大小分配適當的空間,layout group 之間也可以嵌套,又分為 Horizontal(水平)Vertical(垂直)Grid(格狀) 3種



Horizontal Layout Group



水平方向(Width) 排列子物件

組件位置:Unity Menu Item → Component → Layout → Horizontal Layout Group



Padding:填充內部空間

Spacing:每個元素間格

Child Alignment:當沒有填滿全部空間時,子物件對齊位置

Child Force Expand:強制控制子物件填滿空間



透過實例理解各參數:

A.開新 Scene
Unity Menu Item → File → New Scene

B.新增一個 Canvas
Unity Menu Item → GameObject → UI → Canvas

C.Canvas 下新增空物件,做為 Layout Controllers (以下簡稱父物件)

D.父物件增加 Horizontal Layout Group Component
Unity Menu Item → Component → Layout → Horizontal Layout Group

E.父物件下建立5個 Button(子物件),完成後如下,當大小改變時會自動分配子物件大小




F.此時在 Button 的 Rect Transform Component 就不能進行調整,因為我們已經透過 Horizontal Layout Group 進行分配空間,在 Rect Transform 會顯示目前被哪個 Layout Group 控制



G.將 Padding 數值調整如圖,可以看出填充區域



H.將 Spacing 數值調整如圖,可以看出元素區間



I.接下來我們將5個 Button 增加 Layout Element Component 覆蓋預設大小,用於手動設定每個元素的大小
組件位置:Unity Menu Item → Component → Layout → Layout Element

J.此時將 Horizontal Layout Group 的 Child Force Expand Width 取消勾選,不強制子物件填滿額外空間,而是透過 Layout Element 手動設定

K.這裡使用幾種不同的設定,來理解 Horizontal Layout Group 是怎麼取得 Layout Element size 分配子物件

 複習一下子物件大小分配方式,如果不清楚請回去上面 Layout Elements 部分

☻ 首先分配 Minimum Size

☻ 如果還有足夠空間,分配 Preferred Size

☻ 如果還有額外空間,分配 Flexible Size



 將5個 Button 的 Layout Element Min Width 分別改為 20、30、40、50、60,此時可以看出每個 Button 寬度分佈,改變父物件大小時子物件大小並不會改變,因為只有分配 Min Width,並不會分配額外有效空間



此時改變 Horizontal Layout Group 的 Child Alignment,可以看出元素對齊



父物件 Layout Properties Min Width = 5個按鈕寬(20+30+40+50+60=200) + Spacing(40) + Padding Left、Right(20) = 260



 現在將第1個 Button 的 Layout Element 數值調整如圖



這邊設定 Preferred Width 為 100

1.首先分配 Minimum Size(20)

2.空間足夠的情況下,將會分配剩下的 Preferred Size (20~100 空間),如下所示




 現在將第1個 Button 的 Layout Element 數值調整如圖



這邊設定 Flexible Width 為 1

1.首先分配 Minimum Size(20)

2.如果還有足夠空間,將會分配剩下的 Preferred Size (20~100 空間)

3.如果還有額外空間,分配剩下 Flexible Size,如下所示



 現在將 Horizontal Layout Group 的 Child Force Expand Width 勾選,讓子物件強制填滿

1.首先分配 Minimum Size(20)

2.如果還有足夠空間,將會分配剩下的 Preferred Size (20~100 空間)

3.如果還有額外空間,分配剩下元素 Flexible Size 與 Child Force Expand Width





結論:
上面我們看到,所有元素會先被分配 Minimum Size,接下來還有足夠空間,將會分配剩下的 Preferred Size,最後才是 Flexible Size 與 Child Force Expand Width

至此我們了解到 Horizontal Layout Group 是怎麼取得 Layout Element size 分配子物件



Vertical Layout Group



垂直方向(Height) 排列子物件,與 Horizontal Layout Group 只差在水平或是垂直,這邊不在贅述

組件位置:Unity Menu Item → Component → Layout → Vertical Layout Group



Grid Layout Group



網格方式排列子物件

組件位置:Unity Menu Item → Component → Layout → Grid Layout Group



Padding:填充內部空間

Cell Size:每個元素的寬高



Spacing:每個元素間格

Start Corner:開始排列的角落(位置),又分為 “左上”、”右上”、”左下”、”右下”,請仔細看元素數字



Start Axis:”水平” 或是 “垂直” 排列,請仔細看元素數字



Child Alignment:當沒有填滿全部空間時,子物件對齊位置

Constraint:排列限制

Flexible:自動根據大小彈性排列

Fixed Column Count:限制排列 “行數(直)”

Fixed Row Count:限制排列 “列數(橫)”



Layout Fitter

控制著 Layout Controllers 自身大小,大小取決於子物件,或是設定的大小比例,又分為 Content Size FitterAspect Ratio Fitter



Content Size Fitter



控制著 Layout Controllers (父物件)自身大小,大小取決於子物件的 Minimum 或是 Preferred 大小,能透過 Pivot 改變縮放方向

組件位置:Unity Menu Item → Component → Layout → Content Size Fitter



Horizontal、Vertical Fit:水平、垂直 適應調整

None 不調整

Min Size 根據子物件的 Minimum 大小進行調整

Preferred Size 根據子物件的 Preferred 大小進行調整



透過實例理解:

如果我們現在有一個需求,必需要讓 “父物件大小” 根據 “子物件大小” 進行縮放,完成如下 (方便明顯看出父物件大小,增加黑色外框)



A.開新 Scene
Unity Menu Item → File → New Scene

B.新增一個 Canvas
Unity Menu Item → GameObject → UI → Canvas

C.Canvas 下新增空物件,做為 Layout Controllers (以下簡稱父物件)

D.父物件增加 Horizontal Layout Group Component
Unity Menu Item → Component → Layout → Horizontal Layout Group

這時如果增加 Button(子物件),上面有提到,Horizontal Layout Group 會根據子物件的 Layout Element 進行分配子物件大小,而不會修改父物件本身的大小,如下所示 (方便明顯看出父物件大小,增加黑色外框)



E.父物件下增加 Button(子物件),並增加 Layout Element Component 覆蓋預設大小,Minimum Width 調整為 100
組件位置:Unity Menu Item → Component → Layout → Layout Element



F.父物件增加 Content Size Fitter Component,Horizontal Fit 調整為 Min Size,透過子物件 Minimum Width 調整父物件本身大小 (Horizontal 方向其實就是取得子物件 Width)



G.此時如果 Button 複製增加,父物件本身的大小也會跟著改變,如下所示



H.調整父物件的 pivot,可以控制縮放方向,如下所示



I.通過上面實例,我們首先使用 Horizontal Layout Group 排列子物件,並在子物件增加 Layout Element 覆蓋預設大小,最後透過 Content Size Fitter 取得子物件 Layout Element 設定父物件大小,至此父物件大小就會根據子物件大小進行縮放



Aspect Ratio Fitter



控制著 Layout Controllers 自身大小,按照物件寬高比調整大小,能透過 pivot 改變縮放方向

組件位置:Unity Menu Item → Component → Layout → Aspect Ratio Fitter



Aspect Mode:調整模式

None:不調整

Width Controls Height:

基於 Width 為基準,依據比例改變 Height



當 Width 改變時,Height 會依比例改變



Height Controls Width:

基於 Height 為基準,依據比例改變 Width



當 Height 改變時,Width 會依比例改變



Fit In Parent:依據比例將 寬高、位置、anchors自動調整,使此圖形大小在父物件中完全貼齊,此模式可能不會包覆所有空間

調整比例 (方便明顯看出父物件增加黑底)



調整父物件大小,物體會依據比例貼齊父物件



Envelope Parent:依據比例將 寬高、位置、anchors自動調整,使此圖形大小完全包覆父物件,此模式可能會超出空間

調整比例 (方便明顯看出父物件增加黑框)



調整父物件大小,物體會依據比例包覆父物件



Aspect Ratio:比例,此數值為 寬/高



差別:

Content Size Fitter 是透過子物件自動進行調整大小

Aspect Ratio Fitter 是透過數值(寬高比)進行調整



後記
Auto Layout System 可以快速、方便的排列多個 UI,當大小改變時會自動調整內容,也能應用在多層崁套下,在日後調整與修改上也是非常方便與直覺,是 UI 系統中必學的功能之一 !!



參考資料

■ Unity – Manual- Auto Layout
http://docs.unity3d.com/Manual/UIAutoLayout.html

■ Unity – Manual- Auto Layout_UI Reference
http://docs.unity3d.com/Manual/comp-UIAutoLayout.html