国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
Unity3D-深入剖析NGUI的游戲UI架構(gòu)
   unity3d-NGUI分析,使用NGUI做UI需要注意的幾個(gè)要點(diǎn)在此我想羅列一下,對(duì)我在U3D上做UI的一些總結(jié),最后解剖一下NGUI的源代碼,它是如果架構(gòu)和運(yùn)作的。

    在此前我介紹了自己項(xiàng)目的架構(gòu)方式,所以在NGUI的利用上也是同樣的做法,UI邏輯的程序不被綁定在物體上。那么如何做到GUI輸入消息的傳遞呢,答案是:我封裝了一個(gè)關(guān)于NGUI輸入消息的類,由于NGUI的輸入消息傳遞方式是U3D中的SendMessage方式,所以在每個(gè)需要接入輸入的物體上動(dòng)態(tài)的綁定該封裝腳本。在這個(gè)消息封裝類中,加入消息傳遞的委托方法后,所有關(guān)于該物體的輸入消息將通過封裝類直接傳遞到方法上,再通過消息類型的識(shí)別就可以脫離傳統(tǒng)腳本綁定的束縛了。源碼地址:GUIComponentEvent

    在用NGUI制作UI時(shí)需要注意的幾點(diǎn):

1.每個(gè)GUI以1各UIPanel為標(biāo)準(zhǔn),過多的UIPanel首先會(huì)導(dǎo)致DrawCall的增多,其次是導(dǎo)致UI邏輯的混亂。

2.UITexture不能使用的過于平凡,因?yàn)槊總€(gè)UITexture都會(huì)增加1各DrawCall,所以一般會(huì)作為背景圖出現(xiàn)在UI上,小背景,大背景都可以,unity3d ngui教程。

3.圖集不宜過大,過大的圖集,不要把很多個(gè)GUI都放在一個(gè)圖集里,在UI顯示時(shí)加載資源IO速度會(huì)非常慢。我嘗試了各種方式來管理圖集,例如每個(gè)GUI一個(gè)圖集,大雨300*100寬度的圖不做圖集,抑或一個(gè)系統(tǒng)模塊2個(gè)圖集,甚至我有嘗試過以整個(gè)游戲?yàn)閱挝粍澐止矆D集,按鈕圖集,頭像圖集,問題圖集,但這種方式最終以圖集過大IO過慢而放棄,這些圖集的管理方式都是應(yīng)項(xiàng)目而適應(yīng)的,并沒有固定的方式,最主要是你怎么理解程序讀取資源時(shí)的IO操作時(shí)間。

4.能不用自帶的UIDraggablePanel就不用,自己寫才是最適合自己項(xiàng)目的。

5.在開發(fā)中,盡量用Free分辨率來測(cè)試項(xiàng)目的適配效果,不要到上線才發(fā)現(xiàn)適配問題。

適配源碼:

        float defaultWHRate = 800f / 480f;
        float ScreenWHRate = (float)Screen.width / (float)Screen.height;
        bool isUseHResize = defaultWHRate >= ScreenWHRate ? false : true;        
        UIRoot root = GameObject.Find("ROOT").GetComponent<UIRoot>();
        if (!isUseHResize)
        {
            float curScreenH = (float)Screen.width / defaultWHRate;
            float Hrate = curScreenH / Screen.height;
            root.manualHeight =(int)(480f / Hrate);
        }
        else
        {
            root.manualHeight = 480;
        }

6.拆分以及固定各個(gè)錨點(diǎn),上,左上,右上,中,左中,右中,下,左下,右下

7.拆分GUI層級(jí),層級(jí)越高,顯示越靠前。層級(jí)的正確拆分能有效管理GUI的顯示方式。

/// <summary>
/// GUI層級(jí)
/// </summary>
public enum GUILAYER
{
    GUI_BACKGROUND = 0, //背景層
    GUI_MENU,           //菜單層0
    GUI_MENU1,           //菜單層1
    GUI_PANEL,          //面板層
    GUI_PANEL1,         //面板1層
    GUI_PANEL2,         //面板2層
    GUI_PANEL3,         //面板3層
    GUI_FULL,           //滿屏層
    GUI_MESSAGE,        //消息層
    GUI_MESSAGE1,        //消息層
    GUI_GUIDE,           //引導(dǎo)層
    GUI_LOADING,        //加載層
}

8.要充分的管理GUI,不然過多的GUI會(huì)導(dǎo)致內(nèi)存加速增長,而每次都銷毀不用的GUI則會(huì)讓IO過于頻繁降低運(yùn)行速度。我的方法是找到兩者間的中間態(tài),給予隱藏的GUI一個(gè)緩沖帶,當(dāng)每次某各GUI進(jìn)行隱藏時(shí)判斷是否有需要銷毀的GUI。或者也可以這么做,每時(shí)每刻去監(jiān)控隱藏的GUI,哪些GUI內(nèi)存時(shí)間駐留過長就銷毀。

9.另外關(guān)于圖標(biāo),像頭像,物品,數(shù)量過多的,可以用打成幾個(gè)圖集,按一定規(guī)則進(jìn)行排列,減小文件大小減少一次性讀取的IO時(shí)間。

10.盡量減少不必要的UI更改,NGUI一旦有UI進(jìn)行更改,它就得重新繪制MESH和貼圖,比起cocos2d耗得CPU大的多。

11.如果可以不用動(dòng)態(tài)字體就不要用動(dòng)態(tài)字體,因?yàn)閯?dòng)態(tài)字體每次都會(huì)做IO操作讀取相應(yīng)的圖片,這個(gè)是NGUI一個(gè)問題,特別費(fèi)CPU。

12.設(shè)置腳本執(zhí)行次序,在U3D的Project setting->Script Execution Order 中。由于NGUI以UIPanel為主要渲染入口,所以,所有關(guān)于游戲渲染處理的程序最好放在渲染之后,也就是UIPanel之后。UIPanel以LateUpdate為接口入口,所以關(guān)于渲染方面的程序還得斟酌是否方在LateUpdate里。

13.NGUI對(duì)于動(dòng)態(tài)的移動(dòng)旋轉(zhuǎn)等的UI操作支持性很差,當(dāng)有這種操作過多的時(shí)候,會(huì)使得屏幕很卡。解決辦法就是,自己用程序生成面片,面片的渲染不再受到NGUI的控制。

 

    以上是我能想起來的注意點(diǎn),若有沒想起來的,在以后的時(shí)間想到的也將補(bǔ)充進(jìn)去??跓o遮攔的說了這么多,不剖析一下源碼怎么說的過去,之前對(duì)NGUI輸入消息進(jìn)行了封裝,對(duì)2D動(dòng)畫序列幀進(jìn)行了封裝,卻一直沒能完整剖析它的底層源碼,著實(shí)遺憾。

   NGUI中UIPanel是渲染的關(guān)鍵,他承載了在他下面的子物體的所有渲染工作,每個(gè)渲染元素都是由UIWidget繼承而來,每個(gè)UI物體的渲染都是由面片、材質(zhì)球、UV點(diǎn)組成,每個(gè)種材質(zhì)由一個(gè)UIDrawCall完成渲染工作,UIDrawCall中自己創(chuàng)建Mesh和MeshRender來進(jìn)行統(tǒng)一的渲染工作。這些都是對(duì)NGUI底層的簡單的介紹,下面將進(jìn)行更加細(xì)致的分析。

   首先我們來看UIWidget這個(gè)組件基類,從它擁有的類內(nèi)部變量就能知道它承擔(dān)得怎樣的責(zé)任:

    // Cached and saved values
    [HideInInspector][SerializeField] protected Material mMat;//材質(zhì)
    [HideInInspector][SerializeField] protected Texture mTex;//貼圖
    [HideInInspector][SerializeField] Color mColor = Color.white;//顏色
    [HideInInspector][SerializeField] Pivot mPivot = Pivot.Center;//對(duì)齊位置
    [HideInInspector][SerializeField] int mDepth = 0;//深度
    protected Transform mTrans;//坐標(biāo)轉(zhuǎn)換
    protected UIPanel mPanel;//相應(yīng)的UIPanel

    protected bool mChanged = true;//是否更改
    protected bool mPlayMode = true;//模式

    Vector3 mDiffPos;//位置差異
    Quaternion mDiffRot;//旋轉(zhuǎn)差異
    Vector3 mDiffScale;//縮放差異
    int mVisibleFlag = -1;//可見標(biāo)志

    // Widget's generated geometry
    UIGeometry mGeom = new UIGeometry();//多變形實(shí)例

    UIWidget承擔(dān)了存儲(chǔ)顯示內(nèi)容,顏色調(diào)配,顯示深度,顯示位置,顯示大小,顯示角度,顯示的多邊形形狀,歸屬哪個(gè)UIPanel。這就是UIWidget所要承擔(dān)的內(nèi)容,在UIWidget的所有子類中都具有以上相同的屬性和任務(wù)。UIWidget和UIPanel的關(guān)系非常密切,因?yàn)閁IPanel承擔(dān)了UIWidget的所有渲染工作,而UIWidget只是承擔(dān)了存儲(chǔ)需要渲染數(shù)據(jù)。所以,在UIWidget在更換貼圖,材質(zhì)球,甚至更換UIPanel父節(jié)點(diǎn)時(shí)它會(huì)及時(shí)通知UIPanel說:"我更變配置了,你得重新獲取我的渲染數(shù)據(jù)"。

    UIWidget中最重要的虛方法為 virtual public void OnFill(BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols) { } 它是區(qū)分子類的顯示內(nèi)容的重要方法。它的工作就是填寫如何顯示,顯示什么。

    UIWidget中在使用OnFill方法的重要的方法是 更新渲染多邊型方法:

    public bool UpdateGeometry (ref Matrix4x4 worldToPanel, bool parentMoved, bool generateNormals)
    {
        if (material == null) return false;

        if (OnUpdate() || mChanged)
        {
            mChanged = false;
            mGeom.Clear();
            OnFill(mGeom.verts, mGeom.uvs, mGeom.cols);

            if (mGeom.hasVertices)
            {
                Vector3 offset = pivotOffset;
                Vector2 scale = relativeSize;
                offset.x *= scale.x;
                offset.y *= scale.y;

                mGeom.ApplyOffset(offset);
                mGeom.ApplyTransform(worldToPanel * cachedTransform.localToWorldMatrix, generateNormals);
            }
            return true;
        }
        else if (mGeom.hasVertices && parentMoved)
        {
            mGeom.ApplyTransform(worldToPanel * cachedTransform.localToWorldMatrix, generateNormals);
        }
        return false;
    }

    它的作用就是,當(dāng)需要重新組織多邊型展示內(nèi)容時(shí),進(jìn)行多邊型的重新規(guī)劃。

 

    接著,我們來看看UINode,這個(gè)類很容易被人忽視,而他的作用也很重要。它是在UIPanel被告知有新的UIWidget顯示元素時(shí)被創(chuàng)建的,它的創(chuàng)建主要是為了監(jiān)視被創(chuàng)建的UIWidget的位置,旋轉(zhuǎn),大小是否被更改,若被更改,將由UIPanel進(jìn)行重新的渲染工作。

    HasChanged這是UINode唯一重要的方法之一,它的作用就是被UIPanel用來監(jiān)視每個(gè)元素是否改變了進(jìn)而進(jìn)行重新渲染。

    public bool HasChanged ()
    {
#if UNITY_3 || UNITY_4_0
        bool isActive = NGUITools.GetActive(mGo) && (widget == null || (widget.enabled && widget.isVisible));

        if (lastActive != isActive || (isActive &&
            (lastPos != trans.localPosition ||
             lastRot != trans.localRotation ||
             lastScale != trans.localScale)))
        {
            lastActive = isActive;
            lastPos = trans.localPosition;
            lastRot = trans.localRotation;
            lastScale = trans.localScale;
            return true;
        }
#else
        if (widget != null && widget.finalAlpha != mLastAlpha)
        {
            mLastAlpha = widget.finalAlpha;
            trans.hasChanged = false;
            return true;
        }
        else if (trans.hasChanged)
        {
            trans.hasChanged = false;
            return true;
        }
#endif
        return false;
    }

   

    接著,來看UIDrawCall,它是被NGUI隱藏起來的類。他的內(nèi)部變量來看看:

    Transform        mTrans;            //坐標(biāo)轉(zhuǎn)換類
    Material        mSharedMat;        // 渲染材質(zhì)
    Mesh            mMesh0;            //首個(gè)MESH
    Mesh            mMesh1;            //用于更換的Mesh
    MeshFilter        mFilter;        //繪制的MeshFilter
    MeshRenderer    mRen;            //渲染MeshRender組件
    Clipping        mClipping;        //裁剪類型
    Vector4            mClipRange;        //裁剪范圍
    Vector2            mClipSoft;        //裁剪緩沖方位
    Material        mMat;            //實(shí)例化材質(zhì)
    int[]            mIndices;        //做為Mesh三角型索引點(diǎn)

    由這些內(nèi)部變量可知,UIDrawCall是負(fù)責(zé)NGUI的最重要的渲染類。他制造Mesh制造Material,設(shè)置裁剪范圍,為NGUI提供渲染底層。

    他最重要的方法是:

    public void Set (BetterList<Vector3> verts, BetterList<Vector3> norms, BetterList<Vector4> tans, BetterList<Vector2> uvs, BetterList<Color32> cols)
    {
        int count = verts.size;

        // Safety check to ensure we get valid values
        if (count > 0 && (count == uvs.size && count == cols.size) && (count % 4) == 0)
        {
            // Cache all components
            if (mFilter == null) mFilter = gameObject.GetComponent<MeshFilter>();
            if (mFilter == null) mFilter = gameObject.AddComponent<MeshFilter>();
            if (mRen == null) mRen = gameObject.GetComponent<MeshRenderer>();

            if (mRen == null)
            {
                mRen = gameObject.AddComponent<MeshRenderer>();
#if UNITY_EDITOR
                mRen.enabled = isActive;
#endif
                UpdateMaterials();
            }
            else if (mMat != null && mMat.mainTexture != mSharedMat.mainTexture)
            {
                UpdateMaterials();
            }

            if (verts.size < 65000)
            {
                int indexCount = (count >> 1) * 3;
                bool rebuildIndices = (mIndices == null || mIndices.Length != indexCount);

                // Populate the index buffer
                if (rebuildIndices)
                {
                    // It takes 6 indices to draw a quad of 4 vertices
                    mIndices = new int[indexCount];
                    int index = 0;

                    for (int i = 0; i < count; i += 4)
                    {
                        mIndices[index++] = i;
                        mIndices[index++] = i + 1;
                        mIndices[index++] = i + 2;

                        mIndices[index++] = i + 2;
                        mIndices[index++] = i + 3;
                        mIndices[index++] = i;
                    }
                }

                // Set the mesh values
                Mesh mesh = GetMesh(ref rebuildIndices, verts.size);
                mesh.vertices = verts.ToArray();
                if (norms != null) mesh.normals = norms.ToArray();
                if (tans != null) mesh.tangents = tans.ToArray();
                mesh.uv = uvs.ToArray();
                mesh.colors32 = cols.ToArray();
                if (rebuildIndices) mesh.triangles = mIndices;
                mesh.RecalculateBounds();
                mFilter.mesh = mesh;
            }
            else
            {
                if (mFilter.mesh != null) mFilter.mesh.Clear();
                Debug.LogError("Too many vertices on one panel: " + verts.size);
            }
        }
        else
        {
            if (mFilter.mesh != null) mFilter.mesh.Clear();
            Debug.LogError("UIWidgets must fill the buffer with 4 vertices per quad. Found " + count);
        }
    }

    在這個(gè)方法里,它制造Mesh,MeshFilter,MeshRender,Materials。

 

    最后,我們來說說最重要的UI渲染入口UIPanel。

    UIPanel的渲染步驟:

    1.當(dāng)有任何形式的UI組件啟動(dòng)渲染時(shí)加入U(xiǎn)IPanel的渲染隊(duì)列,當(dāng)有新的渲染組件需要有新的UIDrawCall時(shí),進(jìn)行生成新的UIDrawCall.

    2.對(duì)所有UIPanel的渲染隊(duì)列進(jìn)行檢查,是否隊(duì)列中渲染組件需要重新渲染,包括位移,縮放,更改圖片,啟用,關(guān)閉.

    3.獲取渲染組件對(duì)應(yīng)的UIDrawCall,更新Mesh,貼圖,UV,位置,大小

    4.對(duì)需要更新的UIDrawCall進(jìn)行重新渲染

    5.最后標(biāo)記已經(jīng)渲染的渲染組件,告訴他們已經(jīng)渲染,為下次判斷更新做好準(zhǔn)備。刪除不再需要渲染的UIDrawCall,銷毀渲染冗余。

    注意:所有的渲染都是在LateUpdate下進(jìn)行,也就是它是進(jìn)行的延遲渲染。

    接口源碼:

    void LateUpdate ()
    {
        // Only the very first panel should be doing the update logic
        if (list[0] != this) return;

        // Update all panels
        for (int i = 0; i < list.size; ++i)
        {
            UIPanel panel = list[i];
            panel.mUpdateTime = RealTime.time;
            panel.UpdateTransformMatrix();
            panel.UpdateLayers();
            panel.UpdateWidgets();
        }

        // Fill the draw calls for all of the changed materials
        if (mFullRebuild)
        {
            UIWidget.list.Sort(UIWidget.CompareFunc);
            Fill();
        }
        else
        {
            for (int i = 0; i < UIDrawCall.list.size; )
            {
                UIDrawCall dc = UIDrawCall.list[i];

                if (dc.isDirty)
                {
                    if (!Fill(dc))
                    {
                        DestroyDrawCall(dc, i);
                        continue;
                    }
                }
                ++i;
            }
        }

        // Update the clipping rects
        for (int i = 0; i < list.size; ++i)
        {
            UIPanel panel = list[i];
            panel.UpdateDrawcalls();
        }
        mFullRebuild = false;
    }

    Fill()接口源碼:

   

    /// <summary>
    /// Fill the geometry fully, processing all widgets and re-creating all draw calls.
    /// </summary>

    static void Fill ()
    {
        for (int i = UIDrawCall.list.size; i > 0; )
            DestroyDrawCall(UIDrawCall.list[--i], i);

        int index = 0;
        UIPanel pan = null;
        Material mat = null;
        UIDrawCall dc = null;

        for (int i = 0; i < UIWidget.list.size; )
        {
            UIWidget w = UIWidget.list[i];

            if (w == null)
            {
                UIWidget.list.RemoveAt(i);
                continue;
            }

            if (w.isVisible && w.hasVertices)
            {
                if (pan != w.panel || mat != w.material)
                {
                    if (pan != null && mat != null && mVerts.size != 0)
                    {
                        pan.SubmitDrawCall(dc);
                        dc = null;
                    }

                    pan = w.panel;
                    mat = w.material;
                }

                if (pan != null && mat != null)
                {
                    if (dc == null) dc = pan.GetDrawCall(index++, mat);
                    w.drawCall = dc;
                    if (pan.generateNormals) w.WriteToBuffers(mVerts, mUvs, mCols, mNorms, mTans);
                    else w.WriteToBuffers(mVerts, mUvs, mCols, null, null);
                }
            }
            else w.drawCall = null;
            ++i;
        }

        if (mVerts.size != 0)
            pan.SubmitDrawCall(dc);
    }
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
NGUI 渲染流程深入研究 (UIDrawCall UIGeometry UIPanel UIWidget)
NGUI三大基礎(chǔ)機(jī)制
NGUI 減少drawcall
CPU卡頓怎么解?13步教你輕松搞定UI優(yōu)化
Unity屏幕適配終極方案
性能優(yōu)化,進(jìn)無止境---CPU篇
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服