unity遊戲框架學習-UI模塊

框架概述html

1.界面的加載、卸載緩存

2.打開、關閉、隱藏、顯示界面,這邊隱藏是指界面被遮擋的意思,通常來講,界面被遮住時,應該關閉界面的更新服務器

3.界面棧的管理,主要是用於場景切換時須要回到上一個場景打開的界面棧網絡

4.須要的功能:圖片鏡像(節省資源)、滑動列表(複用)、模糊背景等框架

 

注意點:ide

1.界面的生成:class的生成、預製體的實例化,類和實例的關聯。oop

業務打開一個界面須要傳入界面的標識(枚舉、或者字符串),如何經過這個標識找到預製體並實例化go、如何生成指定的界面view,如何綁定view和go佈局

如何銷燬一個界面,清除ab緩存、清除引用關係、destory go動畫

2.界面的層級關係:每次打開一個新的UI,都將它堆入棧,關閉時出棧。這個棧是一個特殊的棧,例如它能夠實現,某個不在棧頂的UI,能夠「TOP」到棧頂。ui

打開一個界面:1.從已打開界面搜索,避免重複打開界面。2.從緩存界面搜索,避免重複加載。3.隱藏棧頂界面。4.打開新界面

關閉一個界面:1.關閉界面並加入緩存。2.從已打開界面棧頂取出一個界面,顯示該界面。3.重複步驟2直到打開一個全屏界面。

退出場景:1.全部界面入棧,當再次回到場景時能夠恢復界面棧。2.關閉界面

進入場景:1.若是須要恢復界面棧,從界面棧取出界面並打開顯示該界面。2.重複1步驟直到打開一個全屏界面。3.不須要恢復:打開當前場景的界面。

3.界面間的通訊:最好不要有界面之間的通訊,界面的更新經過數據(邏輯)類發送消息通知給界面。例如使用道具後界面的更新,數據類接收到服務器道具使用成功後,發送道具更新消息BAG_DATA_UPDATE,須要更新的界面監聽BAG_DATA_UPDATE消息並刷新界面。

4.界面打開動畫控制。統一使用Animator(Animation),且每一個界面最多隻有一個Animator(Animation),界面獲取焦點時調用Animator的Play方法播放動畫。

5.界面特效控制:遊戲會有不少進入界面播放一次的特效,若是你的界面關閉不是使用SetActive處理的(例如設置layer、移到屏幕外等),那麼在你的界面再次打開時特效不會再次被播放。

6.界面內使用的對象怎麼獲取?(例如要修改界面的某個text)

每一個須要在腳本內加載的對象都掛載一個UIExportItem對象,在界面初始化時統一收集這些對象,並存儲在一個map裏給界面使用

7.界面的模糊效果怎麼作?能夠參考:https://blog.csdn.net/zhaoguanghui2012/article/details/51462284,原理就是使用一個shader均值周圍的像素。

8.彈窗適配,例如實現如下效果:彈窗顯示與item的某條邊對齊

如上所示,w爲target的包圍盒寬度,例如綠色框的長度,h爲包圍盒的高度,例如綠色框的高度。包圍盒大小能夠使用unity的RectTransformUtility.CalculateRelativeRectTransformBounds接口獲取。

那麼target的座標怎麼結算呢?

target.x = anchor.x + anchor.w/2 + offect.x + target.w/2

target.y - target.h/2 - offect.y = anchor.y - anchor.h/2 即 target.y = anchor.y - anchor.h/2 + target.h/2 + offect.y

固然對齊方式不同,計算方法也不同,例如左上、左下等,並且還須要考慮界面佈局是否會超框,超框如何處理等。

具體能夠參考:http://www.javashuo.com/article/p-palqpfue-et.html

 

UI模塊分如下幾部分:

1.界面類:負責界面的邏輯,提供生命週期方法供業務使用,如OnOpen、OnClose等。負責界面的生成、銷燬(以及界面ab包的加載、卸載)。子窗口、子item的管理(生成、緩存、銷燬)

2.管理類:提供界面的生命週期管理,如打開、關閉、顯示(得到焦點,顯示在最上層)、隱藏(失去焦點,能夠理解成被其餘界面擋住了)一個界面、打開、關閉一堆界面(場景切入、切出時)。緩存界面。維護窗體中間的層級關係。

3.配置類:負責配置界面的預製體路徑、類、界面類型等參數。

4.功能類:滑動列表、圖片鏡像、模糊、彈窗適配、通用的標題、通用的tab等。

5.item類,例如界面的滑動列表的子項。

導出的層級

 

 詳細代碼以下:

1、ViewDefine:定義類的配置:這邊主要是一些界面定義以及界面的配置,經過配置類名能夠用反射實例化界面類,經過配置的路徑能夠加載並實例化預製體。

using System.Collections.Generic;
using UnityEngine;

public class ViewDefine 
{
    public enum ViewType 
    {
        MAIN = 1,      // 主窗口(全屏)
        POPUP = 2,    // 彈窗
        FIXED = 3,    // 固化窗口
        SCENE = 4,    // 場景UI窗口
        GUIDE = 5,    // 引導UI窗口
    }

    public enum ViewPopModal
    {
        Blur = 1,                       // 帶有模糊效果的模態彈窗
        Lucency_ImPenetrable = 2,       // 無模糊,不可穿透
        Lucency_Penetrate = 3,          // 無模糊, 可穿透
    }

    public enum ViewLoadStateDefine
    {
        NONE = 0,
        LOADING = 1,
        LOADED = 2,
    }

    public enum ViewOnLoadDefine
    {
        Cache = 1,
        Destroy = 2,
    }

    public enum ViewAlignmentType
    {
        UpperLeft = 0,
        UpperCenter = 1,
        UpperRight = 2,
        MiddleLeft = 3,
        MiddleCenter = 4,
        MiddleRight = 5,
        LowerLeft = 6,
        LowerCenter = 7,
        LowerRight = 8,
    }

    public enum ViewID
    {
        TOAST,          // 吐司界面
        TOAST_BATTLE,   // 戰鬥中吐司界面
        NETWAIT,        // 網絡等待界面
        NETWORK_TIPS,   // 網絡異常提示
    }

    private static Dictionary<int, string> _viewConfig = new Dictionary<int, string>
    {
        { (int)ViewID.TOAST,            "ToastView,Prefab/Common/ToastPanel" },
        { (int)ViewID.TOAST_BATTLE,     "ToastBattleView,Prefab/Common/ToastBattlePanel" },
        { (int)ViewID.NETWAIT,          "NetwaitView,Prefab/Common/NetwaitPanel" }
    };

    public static string GetViewType(ViewID viewID)
    {
        string config = _viewConfig[(int)viewID];
        if (string.IsNullOrEmpty(config))
        {
            Debug.LogErrorFormat("未配置界面路徑 : {0}", viewID);
            return null;
        }

        string[] split = config.Split(',');
        return split[0];
    }

    public static string GetViewPath(ViewID viewID)
    {
        string config = _viewConfig[(int)viewID];
        if (string.IsNullOrEmpty(config))
        {
            Debug.LogErrorFormat("未配置界面路徑 : {0}", viewID);
            return null;
        }

        string[] split = config.Split(',');
        return split[1];
    }
}

2、UIBase ,ui元素基類,主要提供go的銷燬以及導入界面須要引用的對象並保存在_viewObj裏,業務能夠經過_viewObj["對象名"]訪問對象而不用去定義參數。

using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// item、view的基類,提供預製體的生成、卸載(ab包的維護),提供子節點的生成
/// </summary>
public class UIBase
{
    protected GameObject gameObject;
    protected Transform transform;

    public string Name { get; set; }

    protected Dictionary<string, Object> _viewObj = new Dictionary<string, Object>();//導出的界面對象

    public virtual void Ctor(GameObject obj, Transform parent)
    {
        if (obj != null)
        {
            gameObject = obj;
            transform = obj.transform;
            ExportHierarchy();

            if (parent != null) {
                transform.SetParent(parent);
                transform.localPosition = Vector3.zero;
                transform.localScale = Vector3.zero;
            }
        }
    }

    protected virtual void OnLoad() { }

    public void SetActive(bool isShow)
    {
        gameObject.SetActive(isShow);
    }

    public virtual void Dispose()
    {
        if (gameObject != null) {
            GameObject.Destroy(gameObject);
            transform = null;
            gameObject = null;           
        }
    }

    protected void ExportHierarchy()
    {
        if (gameObject)
        {
            UIHierarchy hierarchy = gameObject.GetComponent<UIHierarchy>();
            if (hierarchy)
            {
                foreach (var item in hierarchy.widgets)
                {
                    _viewObj.Add(item.name, item.item);
                }

                foreach (var item in hierarchy.externals)
                {
                    _viewObj.Add(item.name, item.item);
                }
            }
        }
    }
}

3、UIItemBase ,item類,主要是提供OnItemOpen、OnItemClose方法,方便item在界面打開和關閉時監聽(移除)事件

/// <summary>
/// 界面item
/// </summary>
public class UIItemBase:UIBase
{
    protected ViewBase parentView;
    protected bool isItemOpen = false;

    public virtual void OnItemOpen()
    {
        isItemOpen = true;
    }

    public virtual void OnItemClose()
    {
        isItemOpen = false;
    }

    public bool IsItemOpen()
    {
        return isItemOpen;
    }

    public void SetParentView(ViewBase parent)
    {
        parentView = parent;
    }
}

4、PanelBase ,子界面、界面的基類。主要是提供子item的生成、維護。

using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 全部界面的基類,包括子窗口、全部的界面
/// 這個類主要功能是:提供給子界面生個生成、維護item的接口
/// </summary>
public class PanelBase:UIBase
{
    protected bool isOpen = false;
    protected Dictionary<string, Queue<UIItemBase>> childItemPool = new Dictionary<string, Queue<UIItemBase>>(); //緩存item的池子
    protected List<UIBase> subItems = new List<UIBase>();//維護子對象,包括子窗口、子item

    /// <summary>
    /// UIModule調用,用於打開一個界面。若是該界面還未加載,會調用Load加載界面
    /// </summary>
    /// <param name="args"></param> 界面參數
    public virtual void Open(params object[] args)
    {
        
    }

    public virtual void Close()
    {
        
    }

    public bool IsOpen()
    {
        return isOpen;
    }

    /// <summary>
    /// 生成一個子item
    /// </summary>
    /// <param name="className"></param> 子item的類名
    /// <param name="prefabs"></param> 子item的預製體
    /// <param name="parent"></param> 子item的父節點
    /// <returns></returns>
    protected UIItemBase GenerateItem(string className, GameObject prefabs, Transform parent)
    {
        Queue<UIItemBase> pool = childItemPool[className];

        if (pool != null && pool.Count > 0)
        {
            return pool.Dequeue();
        }

        UIItemBase item = InstantiateItem(className, prefabs, parent);
        AddSubItem(item);
        return item;
    }

    protected void GenerateItemList(string className, GameObject prefabs, Transform parent, int count, ref List<UIItemBase> container)
    {
        while (container.Count < count)
        {
            UIItemBase item = GenerateItem(className, prefabs, transform);
            container.Add(item);
        }

        while (container.Count > count)
        {
            UIItemBase item = container[container.Count];
            container.Remove(item);
            RecyleItem(item);
        }
    }

    protected UIItemBase InstantiateItem(string className, GameObject prefabs, Transform parent)
    {
        GameObject obj = GameObject.Instantiate(prefabs, parent);
        UIItemBase item = (UIItemBase)UIModule.Instance.CreateUIClass(className);
        item.Ctor(obj, parent);
        item.Name = className;
        return item;
    }

    /// <summary>
    /// 回收子item
    /// </summary>
    /// <param name="item"></param>
    protected void RecyleItem(UIItemBase item)
    {
        RemoveSubItem(item);

        Queue<UIItemBase> cachePool = childItemPool[item.Name];
        if (cachePool == null)
        {
            cachePool = new Queue<UIItemBase>();
        }

        cachePool.Enqueue(item);
        item.SetActive(false);
        if (item.IsItemOpen())
        {
            item.OnItemClose();
        }
    }

    protected void RecyleItemList(int count, ref List<UIItemBase> container)
    {
        while (container.Count > count)
        {
            UIItemBase item = container[container.Count];
            RecyleItem(item);
            container.Remove(item);
        }
    }

    protected void ClearCache()
    {
        foreach (var pool in childItemPool.Values)
        {
            foreach (var item in pool)
            {
                item.Dispose();
            }
            pool.Clear();
        }

        childItemPool.Clear();
    }

    /// <summary>
    /// 添加子對象,包括item、childview
    /// </summary>
    /// <param name="item"></param>
    protected void AddSubItem(UIBase item)
    {
        if (subItems.Contains(item))
        {
            Debug.Log("item已存在");
            return;
        }

        subItems.Add(item);
    }

    protected void RemoveSubItem(UIBase item)
    {
        if (subItems.Contains(item))
        {
            subItems.Remove(item);
        }
    }

    protected void RemoveAllSubItem()
    {
        foreach (var item in subItems)
        {
            item.Dispose();
        }

        subItems.Clear();
    }

    protected void OnOpenSubItem()
    {
        foreach (var item in subItems)
        {
            if (item is UIItemBase)
            {
                (item as UIItemBase).OnItemOpen();
            }
        }
    }

    protected void OnCloseSubItem()
    {
        foreach (var item in subItems)
        {
            if (item is UIItemBase)
            {
                (item as UIItemBase).OnItemClose();
            }
        }
    }

    public override void Dispose()
    {
        base.Dispose();

        RemoveAllSubItem();
        ClearCache();
    }
}

5、ChildViewBase :重寫了Open、Close方法

public class ChildViewBase:PanelBase
{
    /// <summary>
    /// UIModule調用,用於打開一個界面。若是該界面還未加載,會調用Load加載界面
    /// </summary>
    /// <param name="args"></param> 界面參數
    public override void Open(params object[] args)
    {
        if (!isOpen)
        {
            SetActive(true);
            OnOpenSubItem();
            isOpen = true;
        }
    }

    public override void Close()
    {
        if (isOpen)
        {
            OnCloseSubItem();
            SetActive(false);
            isOpen = false;
        }
    }
}

6、ViewBase :全部界面基類,主要是提供了界面的加載、卸載(注意ab的加、卸載),子界面的添加、維護。

using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 界面基類,能夠生成ChildView
/// </summary>
public class ViewBase: PanelBase
{
    protected object[] openParam;
    protected ViewDefine.ViewType viewType;
    protected ViewDefine.ViewLoadStateDefine loadState = ViewDefine.ViewLoadStateDefine.NONE;
    protected bool isHide = false;
    protected float closeTime = 0;//用於回收計時

    public ViewDefine.ViewID ViewID { get; set; }

    /// <summary>
    /// 打開界面,一次OnOpen對應一次OnClose,子類實現
    /// </summary>
    /// <param name="args"></param>
    protected virtual void OnOpen(params object[] args){ }

    /// <summary>
    /// 用於刷新界面,每次調用OpenView都會調用,避免業務重複打開界面
    /// </summary>
    /// <param name="args"></param>
    protected virtual void OnRefreshView(params object[] args) { }

    /// <summary>
    /// 得到焦點。1.打開界面。2.上一層界面被關閉,從新得到焦點
    /// </summary>
    protected virtual void OnEnabled() { }

    public virtual void Update(float dt) { }

    /// <summary>
    /// 失去焦點。1.關閉界面。2.有新的界面打開。
    /// </summary>
    protected virtual void OnDisable() { }

    protected virtual void OnClose() { }


    /// <summary>
    /// UIModule調用,用於打開一個界面。若是該界面還未加載,會調用Load加載界面
    /// </summary>
    /// <param name="args"></param> 界面參數
    public override void Open(params object[] args)
    {
        openParam = args;
        if (loadState == ViewDefine.ViewLoadStateDefine.LOADED)
        {
            DoRealOpen();
        }
        else
        {
            Load();
        }
    }

    /// <summary>
    /// 加載界面,包括界面的ab包,ab的依賴包,最終返回一個Prefab用於實例化界面
    /// </summary>
    private void Load()
    {
        if (loadState != ViewDefine.ViewLoadStateDefine.NONE) return;

        loadState = ViewDefine.ViewLoadStateDefine.LOADING;
        ResManager.Instance.LoadPrefab(ViewDefine.GetViewPath(ViewID), "", LoadFinish);      
    }

    private void LoadFinish(object prefab)
    {
        loadState = ViewDefine.ViewLoadStateDefine.LOADED;
        gameObject = GameObject.Instantiate((GameObject)prefab, UIModule.Instance.GetViewRoot(viewType));
        transform = gameObject.transform;
        ExportHierarchy();

        OnLoad();
        DoRealOpen();
    }

    private void DoRealOpen()
    {
        if (!isOpen)
        {
            SetActiveEx(true);
            OnOpen(openParam);
            OnOpenSubItem();
        }

        OnRefreshView(openParam);
        ShowView();
    }

    /// <summary>
    /// 顯示、隱藏界面,這邊使用的方式是將界面移到屏幕外。
    /// 另外幾種作法是:1.SetActive直接隱藏go。2.設置Scale爲0。3.設置layer out
    /// </summary>
    /// <param name="isActive"></param> 是否顯示
    public void SetActiveEx(bool isActive)
    {
        if (transform) {
            if (isActive)
            {
                transform.localPosition = Vector3.zero;
            }
            else
            {
                transform.localPosition = new Vector3(10000, 10000, 0);
            }
        }
    }

    public virtual void ShowView()
    {
        SetActiveEx(true);
        transform.SetAsFirstSibling();

        if (!isHide)
        {
            isHide = true;
            OnEnabled();
        }       
    }

    /// <summary>
    /// 界面失去焦點,若是是打開彈窗,不隱藏該界面。
    /// </summary>
    /// <param name="keepShow"></param>
    public virtual void HideView(bool keepShow = false)
    {
        SetActiveEx(keepShow);

        if (isHide)
        {
            isHide = false;
            OnDisable();
        }
    }

    public override void Close()
    {
        if (isOpen)
        {
            UIModule.Instance.CloseView(ViewID);
        }
    }

    public virtual void CloseView()
    {
        HideView();
        if (isOpen)
        {
            OnClose();
            OnCloseSubItem();
            CloseSubPanel();
            closeTime = Time.realtimeSinceStartup;
        }
    }

    public float GetCloseTime()
    {
        return closeTime;
    }

    public bool IsPopView()
    {
        return viewType == ViewDefine.ViewType.POPUP;
    }

    public bool IsMainView()
    {
        return viewType == ViewDefine.ViewType.MAIN;
    }

    public bool IsFixedView()
    {
        return viewType == ViewDefine.ViewType.FIXED;
    }

    public bool IsShow()
    {
        return !isHide;
    }

    public bool IsOnLoadDestroy()
    {
        return loadState == ViewDefine.ViewLoadStateDefine.LOADING;
    }

    protected List<ChildViewBase> childView = new List<ChildViewBase>();
    protected ChildViewBase AddChildPanel(string className, GameObject obj, Transform parent)
    {
        ChildViewBase view = (ChildViewBase)UIModule.Instance.CreateUIClass(className);
        view.Ctor(obj, parent);
        childView.Add(view);
        AddSubItem(view);
        return view;
    }

    protected void CloseSubPanel()
    {
        foreach (var item in childView)
        {
            if (item.IsOpen())
            {
                item.Close();
            }
        }
    }
}

7、UIHierarchy:保存了業務導出的引用對象。ExportPanelHierarchy尋找UIExportItem 元素並保存到UIHierarchy

public class UIHierarchy : MonoBehaviour
{
    [System.Serializable]
    public class ItemInfo
    {
        public string name;
        public Object item;

        public ItemInfo() { }
        public ItemInfo(string _name, Object _item) { name = _name; item = _item; }
    }

    // 控件
    public List<ItemInfo> widgets;
    public void SetWidgets(List<ItemInfo> data)
    {
        if (data.Count == 0) return;
        if (widgets == null)
        {
            widgets = new List<ItemInfo>();
        }

        widgets.Clear();
        widgets.AddRange(data);
    }

    // 外部引用
    public List<ItemInfo> externals;
}

using UnityEngine;

[DisallowMultipleComponent]
public class UIExportItem : MonoBehaviour
{
    public string FieldName;
}
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
using System.Collections.Generic;

public class ExportPanelHierarchy
{
    /// <summary>
    /// 導出組件優先級
    /// </summary>
    private static System.Type[] ms_componentTypes = {
        typeof(Button),
        typeof(InputField),
        typeof(ScrollRect),
        typeof(Dropdown),
        typeof(Image),
        typeof(RawImage),
        typeof(Scrollbar),
        typeof(Slider),
        typeof(Text),
        typeof(Toggle),
        typeof(GridLayoutGroup),
        typeof(HorizontalOrVerticalLayoutGroup),
        typeof(LayoutElement),        
        typeof(CanvasGroup),
        typeof(ToggleGroup),
        typeof(TextMesh),
        typeof(Animation),
        typeof(Camera),
        typeof(SpriteRenderer),
    };

    static Object FindComponent(GameObject go)
    {
        Object component = null;
        for (int i = 0; i < ms_componentTypes.Length; ++i)
        {
            component = go.GetComponent(ms_componentTypes[i]);
            if (component != null)
            {
                break;
            }
        }
        return component;
    }

    //生成嵌套UI層級
    public static void ExportNested(Object obj)
    {
        GameObject root = obj as GameObject;
        if (root == null) return;

        UIHierarchy hierarchy = root.GetComponent<UIHierarchy> ();
        if(hierarchy==null)
        {
            hierarchy = root.AddComponent<UIHierarchy>();
        }

        //生成根節點層級
        List<UIHierarchy.ItemInfo> fields = new List<UIHierarchy.ItemInfo>();
        GetChildComponentUtilHierarchy (root.transform, fields);
        hierarchy.SetWidgets(fields);

        //生成子panel層級
        UIHierarchy[] childHierarchys = root.GetComponentsInChildren<UIHierarchy>(true);
        for(int i=1; i<childHierarchys.Length; i++)
        {
            UIHierarchy childHrcy = childHierarchys [i];

            List<UIHierarchy.ItemInfo> childUIItem = new List<UIHierarchy.ItemInfo>();
            GetChildComponentUtilHierarchy (childHrcy.transform, childUIItem);
            childHrcy.SetWidgets(childUIItem);
        }

        EditorUtility.SetDirty(root);
        AssetDatabase.SaveAssets();
    }
    
    //導出傳入節點的層級,直到某個子節點掛有UIHierarchy組件
    private static void GetChildComponentUtilHierarchy(Transform transRoot, List<UIHierarchy.ItemInfo> fields)
    {
        for(int i=0; i<transRoot.childCount; i++)
        {
            Transform trans = transRoot.GetChild (i);

            UIHierarchy hrchy = trans.GetComponent<UIHierarchy> ();
            if(hrchy!=null)
            {
                fields.Add (new UIHierarchy.ItemInfo(hrchy.name, hrchy));
                continue;
            }

            UIExportItem uiItem = trans.GetComponent<UIExportItem>();
            if (uiItem != null)
            {
                Object fieldItem = FindComponent(uiItem.gameObject);
                if (fieldItem == null)
                {
                    fieldItem = uiItem.transform;
                }
                fields.Add(new UIHierarchy.ItemInfo(uiItem.name, fieldItem));
            }

            GetChildComponentUtilHierarchy (trans, fields);
        }
    }
}

8、UIModule:核心管理類,提供給全局惟一的打開界面方法,維護層級棧。維護界面緩存。

using System.Collections.Generic;
using System.Reflection;
using UnityEngine;

public class UIModule
{
    #region Instance
    private static UIModule m_Instance;
    public static UIModule Instance
    {
        get
        {
            return m_Instance ?? (m_Instance = new UIModule());
        }
    }
    #endregion

    private const float CACHE_TIME = 30;//界面緩存時間
    private int curSceneID = 0;//當前打開的場景id,每一個場景都有本身的層級棧
    private int curViewID = 0;//當前打開的界面id
    private int lastViewID = 0;//上一個界面id

    private List<int> openViewList;//當前打開的界面列表,按順序
    private Dictionary<int, ViewBase> cacheView;//緩存區,等待銷燬,從緩存區取要移除
    private Dictionary<int, List<int>> naviStack;//場景的層級棧,key爲場景id,用於維護場景
    private Dictionary<int, ViewBase> viewPool;//保存全部ViewBase的引用,包括緩存區的,界面銷燬時須要移除。
    private Dictionary<ViewDefine.ViewType, Transform> viewRoot;//界面實例化出來的父節點,不一樣界面的父節點不同。
    private Transform uiRoot;//ui根節點
    private float lastCheckCacheTime;//上次檢查緩存時間,一秒檢查一次

    public void Init()
    {
        openViewList = new List<int>();
        cacheView = new Dictionary<int, ViewBase>();
        naviStack = new Dictionary<int, List<int>>();
        viewPool = new Dictionary<int, ViewBase>();

        uiRoot = GameObject.Find("UIRoot").transform;
        viewRoot = new Dictionary<ViewDefine.ViewType, Transform>
        {
            { ViewDefine.ViewType.MAIN, uiRoot.Find("main") },
            { ViewDefine.ViewType.POPUP, uiRoot.Find("main") },
            { ViewDefine.ViewType.FIXED, uiRoot.Find("fixed") },
            { ViewDefine.ViewType.GUIDE, uiRoot.Find("guide") }
        };

        lastCheckCacheTime = Time.realtimeSinceStartup;
    }

    public Transform GetViewRoot(ViewDefine.ViewType viewType)
    {
        if (viewType == ViewDefine.ViewType.SCENE)
        {
            return null;
        }
        else
        {
            return viewRoot[viewType];
        }
    }

    public void Update(float dt)
    {
        int openCount = openViewList.Count;
        ViewBase view;
        for (int i = 0; i < openCount; i++)
        {
            int viewId = openViewList[i];
            if (viewPool.TryGetValue(viewId, out view))
            {
                view.Update(dt);
            }
        }

        //清楚緩存時間到了的界面
        float curTime = Time.realtimeSinceStartup;
        if (curTime - lastCheckCacheTime > 1)
        {
            foreach (var item in cacheView)
            {
                if (curTime - item.Value.GetCloseTime() > CACHE_TIME)
                {
                    item.Value.Dispose();
                    cacheView.Remove(item.Key);
                    viewPool.Remove(item.Key);
                }
            }
        }

        lastCheckCacheTime = curTime;
    }

    /// <summary>
    /// 外部調用 打開一個窗口的惟一方式
    /// 若是上一個界面lastView存在,須要把lastView入棧
    /// </summary>
    /// <param name="viewID"></param>
    /// <param name="param"></param> 界面打開參數,給業務使用的
    /// <returns></returns>
    public ViewBase OpenView(ViewDefine.ViewID viewID, params object[] param)
    {
        int viewKey = (int)viewID;
        ViewBase view = FindOpenView(viewKey);

        if (view == null)
        {
            view = CreateView(viewID);

            if (view == null)
            {
                Debug.LogErrorFormat("OpenView CreateView Fail..viewID:", viewID);
                return null;
            }
            view.ViewID = viewID;
            AddOpenView(viewKey);
        }

        if (curViewID == viewKey)
        {
            view.Open(param);
            return view;
        }

        if (view.IsPopView() || view.IsMainView())
        {
            lastViewID = curViewID;
            curViewID = viewKey;

            if (lastViewID > 0)
            {
                OnBackstage(lastViewID);
            }
        }

        view.Open(param);
        return view;
    }

    private ViewBase FindOpenView(int viewKey)
    {
        ViewBase view = null;
        if (openViewList.Contains(viewKey))
        {            
            viewPool.TryGetValue(viewKey, out view);
        }

        return view;
    }

    private void AddOpenView(int viewKey)
    {
        if (openViewList.Contains(viewKey))
        {
            Debug.LogFormat("界面已打開:{0}", viewKey);
            return;
        }
        openViewList.Add(viewKey);
    }

    private void RemoveOpenView(int viewKey)
    {
        if (openViewList.Contains(viewKey))
        {
            openViewList.Remove(viewKey);
        }
    }

    /// <summary>
    /// 建立新的view,先從緩存裏面找
    /// </summary>
    /// <param name="viewID"></param>
    /// <returns></returns>
    private ViewBase CreateView(ViewDefine.ViewID viewID)
    {
        int viewKey = (int)viewID;
        ViewBase view = GetViewFromCache(viewKey);

        if (view == null)
        {
            string viewName = ViewDefine.GetViewType(viewID);
            //加載程序集,建立程序集裏面的 命名空間.類型名 實例
            object ect = CreateUIClass(viewName);

            view = (ViewBase)ect;//類型轉換並返回
            viewPool.Add((int)viewID, view);
        }
        
        return view;
    }

    public object CreateUIClass(string calssName)
    {
        return Assembly.GetExecutingAssembly().CreateInstance(calssName);
    }

    private ViewBase GetViewFromCache(int viewKey)
    {
        ViewBase view = null;
        if (cacheView.TryGetValue(viewKey, out view)) {
            cacheView.Remove(viewKey);
        }
        
        return view;
    }

    private void AddViewToCache(int viewKey, ViewBase view)
    {
        cacheView[viewKey] = view;
    }

    private int GetTopViewOfStack()
    {
        List<int> stack = naviStack[curSceneID];
        if (stack == null || stack.Count == 0)
        {
            return 0;
        }

        return stack[stack.Count];
    }

    private ViewBase PopViewFormStack()
    {
        List<int> stack = naviStack[curSceneID];
        if (stack == null || stack.Count == 0)
        {
            return null;
        }

        int index = stack.Count;
        int viewId = stack[index];
        ViewBase view = GetViewByKey(viewId);
        stack.RemoveAt(index);

        if (view.IsPopView())
        {
            for (int i = index-1; i > 0; i--)
            {
                ViewBase temp = GetViewByKey(stack[i]);
                temp.SetActiveEx(true);

                if (temp.IsMainView()) break;
            }
        }

        curViewID = viewId;
        view.ShowView();
        AddOpenView(viewId);
        return view;
    }

    /// <summary>
    /// 向棧裏添加元素
    /// </summary>
    /// <param name="viewKey"></param>
    /// <param name="isForce"></param>true時,棧裏存在會先移除在加入,不然棧裏存在就不處理了
    private void AddViewToStack(int viewKey, bool isForce = true)
    {
        List<int> stack = naviStack[curSceneID];
        if (stack == null)
        {
            stack = new List<int>();
        }

        if (stack.Contains(viewKey))
        {
            if (isForce)
            {
                stack.Remove(viewKey);
            }
            else
            {
                return;
            }            
        }

        stack.Add(viewKey);
    }

    private void RemoveFromStack(int viewKey)
    {
        List<int> stack = naviStack[curSceneID];
        if (stack == null || stack.Count == 0)
        {
            return;
        }

        if (stack.Contains(viewKey))
        {
            stack.Remove(viewKey);
        }
    }

    /// <summary>
    /// 進入後臺
    /// </summary>
    /// <param name="viewKey"></param>
    private void OnBackstage(int viewKey)
    {
        AddViewToStack(viewKey);
        ViewBase lastView = GetViewByKey(lastViewID);

        if (lastView.IsPopView())
        {
            lastView.HideView(true);
        }
        else
        {
            lastView.HideView(false);
        }        
    }

    /// <summary>
    /// 關閉界面,若是是當前打開界面,須要從棧頂彈出新的界面
    /// </summary>
    /// <param name="viewKey"></param>
    private void InsertClose(int viewKey)
    {
        RemoveView(viewKey);

        if (viewKey == curViewID)
        {
            curViewID = 0;
            PopViewFormStack();
        }
        else
        {
            RemoveFromStack(viewKey);
        }
    }

    /// <summary>
    /// 移除界面,從打開列表移除,添加到緩存
    /// </summary>
    /// <param name="viewKey"></param>
    private void RemoveView(int viewKey)
    {
        RemoveOpenView(viewKey);
        RemoveFromStack(viewKey);
        ViewBase view = GetViewByKey(viewKey);
        view.CloseView();
        AddViewToCache(viewKey, view);
    }

    private ViewBase GetViewByKey(int viewKey)
    {
        return viewPool[viewKey];
    }

    public void CloseView(ViewDefine.ViewID viewID)
    {
        int viewKey = (int)viewID;
        if (openViewList.Contains(viewKey))
        {
            InsertClose(viewKey);
        }
    }

    public void CloseCurView()
    {
        if (curViewID > 0)
        {
            CloseView((ViewDefine.ViewID)curViewID);
        }
    }

    /// <summary>
    /// 進入新的場景
    /// </summary>
    /// <param name="sceneID"></param> 場景id
    /// <param name="isNative"></param> 是否須要打開ui棧,isBack=true時,從當前場景的棧頂彈出界面
    public void EnterScene(int sceneID, bool isBack)
    {
        curSceneID = sceneID;
        if (naviStack[sceneID] == null)
        {
            naviStack[sceneID] = new List<int>();
        }

        if (isBack)
        {
            PopViewFormStack();
        }
    }

    /// <summary>
    /// 退出當前場景
    /// </summary>
    /// <param name="pushToStack"></param> 是否壓棧,用於場景返回時恢復ui層級
    public void ExitScene(bool pushToStack)
    {
        int count = openViewList.Count;
        ViewBase temp = null;
        int viewKey = 0;

        for (int i = count; i > 0; i--)
        {
            viewKey = openViewList[i];
            temp = GetViewByKey(viewKey);

            if (temp != null && (temp.IsMainView() || temp.IsPopView()))
            {
                if (pushToStack)
                {
                    RemoveOpenView(viewKey);
                    AddViewToStack(viewKey, false);
                }
                else
                {
                    RemoveView(viewKey);
                }
            }
        }

        List<int> stack = naviStack[curSceneID];
        if (stack != null)
        {
            for (int i = 0; i < stack.Count; i++)
            {
                ViewBase view = GetViewByKey(stack[i]);
                view.HideView();
            }
        }

        curViewID = 0;
        lastViewID = 0;
    }
}
相關文章
相關標籤/搜索