尊重他人的勞動,支持原創(chuàng),轉(zhuǎn)載請注明出處:http.dsqiu.iteye.com
你是不是對 UIGrid 和 UITable 定位計算方法還模糊不清,那么這篇文章就是你需要的。
NGUI 提供了 Grid 和 Table 組件,支持的參數(shù)很少,功能也很雞肋,完全不能像 CSS 的 Box 模型那樣隨心所欲的布局,而且使用 UIGrid 和 UITable 的時候經(jīng)常會相對 UIGrid 和 UITable 掛載的 Transform 出現(xiàn)偏移,之前使用的時候,都是根據(jù)經(jīng)驗來規(guī)避的。今天打算把UIGrid 和 UITable 的排列規(guī)則看下, 才形成此文。
UIGrid 和 UITable 的原理很簡單,對子 Transform 的 List 進行排序,然后更加不同的規(guī)則進行定位排列(UIGrid 和 UITable 還是有很大不同的)。
排序(Sort)
UIGrid 和 UITable 定義了5種排列方式(其實是3種,None默認不排序即Transform的默認排序,Custom雖然提供virtual 可以重載):
C#代碼
public enum Sorting { None, Alphabetic, Horizontal, Vertical, Custom, } 對應(yīng)的三種排序方法:集 Alphabetic 按照名字字符串排序,Horizontal 和 Vertical 按照localPosition 進行的排序
C#代碼
static public int SortByName (Transform a, Transform b) { return string.Compare(a.name, b.name); } static public int SortHorizontal (Transform a, Transform b) { return a.localPosition.x.CompareTo(b.localPosition.x); } static public int SortVertical (Transform a, Transform b) { return b.localPosition.y.CompareTo(a.localPosition.y); } 這里說下,雖然提供了Custom方式,第一感覺NGUI的developer考慮很周到,但是提供的確實重載 virtual 函數(shù)的方式,D.S.Qiu 覺得這種方式太不好了,為了一個方法就要寫一個 子類去重載,個人覺得指定一個委托,擴展起來會更直觀,但這一要求開發(fā)者一開始就得指定這個 Custom Sort Delegate。
UIGrid定位原理
下面這段代碼是 Reposition() 的一部分,原理很簡單:根據(jù)定義的cellHeight 和cellWidth 來調(diào)整子 Transform 的 localPosition。這里還是吐槽下:Reposition() 的代碼太多容易了,至少我看到了這段代碼在 Reposition 中出現(xiàn)了兩次,完全多余,其實就是處理流程就應(yīng)該是: 先獲取所有子 Transform List ,然后對List 排序,最后就是下面這段定位代碼了。
C#代碼
for (int i = 0, imax = list.Count; i < imax; ++i) { Transform t = list[i]; if (!NGUITools.GetActive(t.gameObject) && hideInactive) continue; float depth = t.localPosition.z; Vector3 pos = (arrangement == Arrangement.Horizontal) ? new Vector3(cellWidth * x, -cellHeight * y, depth) : new Vector3(cellWidth * y, -cellHeight * x, depth); if (animateSmoothly && application.isPlaying) { SPRingPosition.Begin(t.gameObject, pos, 15f).updateScrollView = true; } else t.localPosition = pos; if (++x >= maxPerLine && maxPerLine > 0) { x = 0; ++y; } }
UITable 定位原理
UITable 的定位方法在 ReositionVaribleSize 中,跟UIGrid 最大不同點是:UIGrid 只根據(jù)定義的cellHeight 和 cellWidth 來計算位置,UITable 根據(jù)“內(nèi)容“(UIWidget)來計算位置的。
C#代碼
protected void RepositionVariableSize (List<Transform> children) { float xOffset = 0; float yOffset = 0; int cols = columns > 0 ? children.Count / columns + 1 : 1; int rows = columns > 0 ? columns : children.Count; Bounds[,] bounds = new Bounds[cols, rows]; Bounds[] boundsRows = new Bounds[rows]; Bounds[] boundsCols = new Bounds[cols]; int x = 0; int y = 0; //這個循環(huán)計算每行,每列,每個cell 的內(nèi)容的編輯 Bounds for (int i = 0, imax = children.Count; i < imax; ++i) { Transform t = children[i]; Bounds b = NGUIMath.CalculateRelativeWidgetBounds(t, !hideInactive); Vector3 scale = t.localScale; b.min = Vector3.Scale(b.min, scale); b.max = Vector3.Scale(b.max, scale); bounds[y, x] = b; boundsRows[x].Encapsulate(b); boundsCols[y].Encapsulate(b); if (++x >= columns && columns > 0) { x = 0; ++y; } } x = 0; y = 0; //計算位置 for (int i = 0, imax = children.Count; i < imax; ++i) { Transform t = children[i]; Bounds b = bounds[y, x]; Bounds br = boundsRows[x]; Bounds bc = boundsCols[y]; Vector3 pos = t.localPosition; pos.x = xOffset + b.extents.x - b.center.x; pos.x += b.min.x - br.min.x + padding.x; //以每列最右邊的x值為標準,即保證每列最左邊的基準點 if (direction == Direction.Down) { pos.y = -yOffset - b.extents.y - b.center.y; pos.y += (b.max.y - b.min.y - bc.max.y + bc.min.y) * 0.5f - padding.y; } else { pos.y = yOffset + (b.extents.y - b.center.y); pos.y -= (b.max.y - b.min.y - bc.max.y + bc.min.y) * 0.5f - padding.y; } xOffset += br.max.x - br.min.x + padding.x * 2f; t.localPosition = pos; if (++x >= columns && columns > 0) { x = 0; ++y; xOffset = 0f; yOffset += bc.size.y + padding.y * 2f; } } } 這里還是有吐槽:根據(jù) Bounds 的定義 b.extens.x - b.center.x + b.min.x == 0 ,也就是這部完全是沒有必要的,看來UIGrid 的 developer,不光代碼邏輯不清晰,難道連大腦都是豆腐花做的么。
C#代碼
pos.x = xOffset + b.extents.x - b.center.x; pos.x += b.min.x - br.min.x + padding.x; 類似計算 y 的值也其他更直接方法:
C#代碼
(b.max.y - b.min.y - bc.max.y + bc.min.y) * 0.5f
使用經(jīng)驗
1.UIGrid 沒有考慮Bounds ,根據(jù)UIGrid 的計算公式可以知道:UIGrid 的第一個元素的 localPosition 的 x 和 y 一定都是 0 ,所以要位置,必須調(diào)整UIGrid 的localPosition ,但是實際在有可能調(diào)整的是 子對象,然后再 Scene 窗口看是沒問題的(注意此時還沒有重排),一運行就會出現(xiàn)位置的偏移。
2.UITable 的子組件的 x 總是以 每列最左為 起始基準點的, y 則是每行居中對齊 :(b.max.y - b.min.y - bc.max.y + bc.min.y) * 0.5f 這行代碼起始就是計算當前組件和所在行中心點的偏移。
下圖是將NGUI 其中一個組件的 sprite 左移了,就出現(xiàn)下面的排列:
雖然 NGUI 提供了 UIGrid 和 UITable ,起始是非常之不完善,幾乎做不了什么功能。這里分享兩條經(jīng)驗:
1.使用UIGrid時,調(diào)整界面的時候讓 UIGrid 的 transform 和 其第一個子組件的 transform 相同,這樣經(jīng)過計算之后,位置就是之前調(diào)整想要的。或者將 第一個子組件 transform 重置,這樣調(diào)整UIGrid 的 transform 位置看到的效果就是真實的。
2.使用UITable 讓每列元素的左邊界都相同,即左對齊。可以看到 NGUI Example 的 Question Log 的 Table 的所有組件(UILable UISprite)的 pivot 都調(diào)整為 left 。
總之,就是根據(jù)UIGrid 和UITable 的排列原理做相應(yīng)的調(diào)整。
小結(jié):
這篇文章相對于NGUI所見即所得系列其他文章來說,簡單很多。最近要做一個界面根據(jù)內(nèi)容自適應(yīng),挺復(fù)雜的,一堆莫名其妙的問題。之前一直覺得 Unity 的 UI 沒有Window MFC 等開發(fā)直接拖拽方式那么直觀。NGUI 雖然很龐大,但NGUI越來越容易讓我吐槽了,可能是對NGUI的家底多少掌握的緣故吧。
很久就聽說Unity要出自己的 UI 了,其實D.S.Qiu 也一直有想嘗試自己寫一點UI的可視化編輯工具(Visual Editor)。昨天不經(jīng)意看到 Cocos2D 的 UI 編輯器 CocoStudio 感覺很強大,然后順手google Unity有沒有這方面的工具,果然還是發(fā)現(xiàn)了 UIToolkit , bitverse RagePixel 和 EWS editor ,也就說 Unity其實也有些 UI 可視化工具的。尤其 bitverse 支持的組件特別豐富,很強大,只可惜沒有集成 Batch DrawCall 的功能。
如果您對D.S.Qiu有任何建議或意見可以在文章后面評論,或者發(fā)郵件(gd.s.qiu@Gmail.com)交流,您的鼓勵和支持是我前進的動力,希望能有更多更好的分享。
轉(zhuǎn)載請在文首注明出處:http://dsqiu.iteye.com/blog/2034883
新聞熱點
疑難解答