NGUI開發優化技巧(上)
UGUI和NGUI的區別:
使用率:NGUI佔大多數,NGUI和UGUI的使用佔比為81% : 19%;
易用性:NGUI佔一定優勢,UGUI仍在慢慢改善;
效能:如果都合理搭配,UGUI在效能上還是有一點優勢的,在Unity5.2、5.3之後UGUI有一部分網格合併的操作是放在多執行緒中進行,相對來講,效能有一定的提升;
但NGUI的優化相對更簡單一些,主要是因為NGUI的Drawcall相對容易估計,可以知道Drawcall從哪裡來的,也更容易定位網格的重新整理是由什麼元素引起的,
但UGUI的Drawcall難以估計,且網格重建的開銷因為引入了多執行緒,也會被隱藏起來,所以優化起來,
螢幕自適應
1:UIRoot/Scaling Style
— Flexible(PixelPerfect)
— Constrained(FixedSize)
可以讓使用者指定一個區間,當真實螢幕的畫素高度介於最大、最小高度之間,那麼UI元素將保持原有的解析度,也就是說,在這個範圍之間,UI元素的畫素和裝置的畫素是可以匹配的;在解析度高的設配上UI會相對小一些,在解析度低的裝置上這些UI圖片會相對大一些;這種模式較為複雜,使用不多;
不同的裝置上看到的畫面只是等比的縮放,佈局更加容易,使用較多;
2:UIAnchor 在舊的NGUI
3:UIRect/AnchorPoint:在較新的NGUI中,Anchor放在了UIRect這個類中;
Execute的模式會影響效能,對於靜態的UI,不要使用On Update模式;
如果設定為On Update,則會在Profiler中看到UIRect.Update會比較高;
事件處理
1:UIButton
2:UIEventListener
NGUI和UGUI相差很大,UGUI中的事件處理沒有使用到物理中的Physics.Raycast,而是通過
射線跟四邊形去做一些檢查,而NGUI會使用物理系統Physics
在Unity4.X上UGUI事件檢測的開銷有時候可能會有比較高的持續開銷,因為在Unity4.X中預設所有Graphics的元素(包含Image和Texture)都會作為事件檢查的目標,除非把Camera給禁用掉,在Unity5.2中才出現對每一個Graphics可以設定RaycastTarget這個屬性,去掉這個屬性之後,可以不參與檢測;
在NGUI中就不會出現這個問題,因為NGUI只會在需要檢測的地方(Button、Toggle)掛上碰撞體,所以只有在掛了碰撞體的元素才會參與事件的檢測;
可以藉助UIEventListener元件,在按鈕或UI元素上掛上這個元件,然後在一個統一的地方去檢查,去處理所有Button;
如下程式碼所示:
- using UnityEngine;
- using System.Collections;
- publicclass UIEventManager : MonoBehaviour {
- private UIButton[] buttons;
- // Use this for initialization
- void Start () {
- buttons = GetComponentsInChildren<UIButton>();
- foreach (var button in buttons)
- {
- UIEventListener.Get(button.gameObject).onClick += OnButtonClick;
- }
- }
- // Update is called once per frame
- void Update () {
- }
- void OnButtonClick(GameObject go)
- {
- Debug.Log(go.name);
- }
- }
自定義材質
1:Gray
2:ETC Split
比如:做一個會變灰的按鈕,自定義的Shader放到UI元素上可以正常顯示,但是把它放到ScrollView裡面的滾動區域時,材質丟失或完全變黑,這是因為NGUI在處理滾動框裡面的內容時,為了實現遮罩效果,會去動態替換裡面元素的Shader;
把Texturefen採用ETC1分離為RGB和A圖片後,替換原有的Shader,當放入ScrollView的裁剪區域後,圖片變黑,想要恢復原狀,需要把原有的Shader替換成NGUI可以識別的Shader;
比如:把Shader “UI/UI_ETC”替換為:Shader “Hidden/UI/UI_ETC 1”(只用替換Shader名字,1代表被1個Panel做了clipping,以此類推)
當我們需要提供一個自定義的材質時,需要附帶3個配套的版本,以保證UI元素放到滾動區域後依然能夠正常顯示;
ParticleSystem互動
1:前後遮擋
2:ScrollView裁剪
如何把一個ParticleSystem放在兩個UI元素之間?
通過指令碼把ParticleSystem的Renderer的RenderQueue控制在兩個UI元素之間;
當ParticleSystem放到了滾動區域之後(比如揹包),有一些圖示需要高亮或者特殊效果,當滾動揹包時,希望揹包區域把這些粒子裁減掉,但預設情況下,並不會被裁剪,因為粒子系統的Shader是另一種渲染方式,跟NGUI的不一樣,NGUI不僅自身有一種UI的Shader,而且還需要去替換做clip裁剪的Shader版本;
一般可以考慮的會有以下幾種方式:
1:轉成序列幀,做成UI的方式去做,但是表現力會大打折扣,並且圖片量會有所提升;
2:設計自定義的ParticleSystem的Shader,並且NGUI中裁剪的機制放到自定義的Shader中去,相對複雜一些,畢竟ParticleSystem的座標系和產生的Mesh時候的座標系的對應不一樣;
3:通過額外的Camera去做,就是說Particle的裁剪是通過Camera的顯示區域去做,這種限制比較大,多加的Camera使UI層級的管理會比較複雜,所以還是使用第二種方式;
程式碼如下:
- using UnityEngine;
- using System.Collections;
- [ExecuteInEditMode]
- [RequireComponent(typeof(ParticleSystemRenderer))]
- publicclass UIParticle : MonoBehaviour {
- private UIPanel panel;
- private ParticleSystemRenderer pRenderer;
- private Material dyMaterial;
- private UIWidget cover;
- // Use this for initialization
- void Start () {
- panel = GetComponentInParent<UIPanel>();
- pRenderer = GetComponent<ParticleSystemRenderer>();
- // dyMaterial = new Material(Shader.Find("Hidden/Unlit/Transparent Colored 1"))
- dyMaterial = new Material(Shader.Find("Unlit/Transparent Colored"))
- {
- renderQueue = 4000,
- mainTexture = pRenderer.sharedMaterial.mainTexture
- };
- pRenderer.material = dyMaterial;
- }
- // Update is called once per frame
- void OnWillRenderObject () {
- if (cover!=null && cover.isActiveAndEnabled && cover.drawCall!=null)
- {
- dyMaterial.renderQueue = cover.drawCall.renderQueue;
- }
- Vector4 cr = panel.drawCallClipRange;
- Vector2 soft = panel.clipSoftness;
- Vector2 sharpness = new Vector2(1000.0f, 1000.0f);
- if (soft.x > 0f) sharpness.x = cr.z / soft.x;
- if (soft.y > 0f) sharpness.y = cr.w / soft.y;
- float scale = 1.0f / transform.lossyScale.x;
- Vector3 position = -panel.transform.position * scale;
- dyMaterial.SetVector(Shader.PropertyToID("_ClipRange0"),
- new Vector4(-cr.x/cr.z + position.x/cr.z, -cr.y/cr.w + position.y/cr.w, 1f/cr.z*scale, 1f/cr.w*scale));
- dyMaterial.SetVector(Shader.PropertyToID("_ClipArgs0"), new Vector4(sharpness.x, sharpness.y, 0, 1));
- }
- void OnDestroy()
- {
- DestroyImmediate(dyMaterial);
- dyMaterial = null;
- }
- }
UIDrawCall:管理UI的顯示,任何材質的替換都是在這個類裡面做的,包括在裁剪前替換材質,並把裁剪區域做一個限定,也都是在這個腳本里面進行的;
DrawCall優化:
1:Panel Tool
2:Draw Call Tool
優化時,可以看到每個Panel產生了多少個Drawcall;
如果Drawcall使用UISprite,材質一樣,並且相鄰,則會自動合併成一個Drawcall,如果沒有合併,說明使用了UITexture,因為每個UITexture都會產生一個Drawcall;
多個Drawcall合成一個時,並不會增大記憶體,因為多個UI元素,每個UI元素都對應一個Mesh,對應的Mesh頂點固定,如果不合並Drawcall,則他們的Mesh是分開的,合併後Mesh是合併的,但定點數和頂點屬性是固定的;