ui unity3d 框架

2017-04-19 11:02:55 u014659211 阅读数 13722
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

为什么要使用UI框架?直接使用NGUI或UGUI一拖一拉直接搭载出界面不就行了?

我相信很多小白,包括我在刚学习Unity3D UI的时候都这样想过。

我的第一款款Unity2D游戏《山地赛车》,使用的就是NGUI搭载界面。

弱联网手游一般都没什么复杂的界面,我也是很轻松花一天就把界面搭载好了,看起来好挺好看的,还花了不少时间做动态效果。

界面搭载好后,开始开发游戏内容,这下问题开始来了:

1.如何实现界面间的沟通?例如点击返回按钮,返回上一个界面,点击背包系统,弹出背包。

2.如何实现界面与游戏数据的沟通?例如点击排行榜,能列出最新的排名,点击购买车辆,能扣钱并买入新的车辆。

一开始我的做法(我相信也是大部分新手最喜欢的做法)就是为每个要触发功能的UI添加一个脚本,然后添加一个public gameobject,

然后拖入触发UI时要控制的object。在脚本的OnClick等函数里实现逻辑功能。

这样做是挺容易。很快我也花了半天时间拖来拖去,把UI要关联的各种物件绑定好。

但是接下来的各种问题让我非常头疼:

1.随着游戏系统的复杂,UI控件越来越多,各个UI直接的通讯,以及UI与GameObject之间的通讯形成一张复杂的蜘蛛网,

拖着拖着,有时候我都忘了哪个对象跟哪个对象关联了。如果是别人要看我的程序,我估计他找半天都找不到UI逻辑的入口。

2.耦合性非常严重,如果要改变需求,更改某个UI或者更改某个游戏对象,那么你需要再手动全部与该对象关联的物件重新更改一次。

3.作为强迫症的我,最受不了程序代码的混乱。这种组织方式十分“不优雅”,看着很乱。

 

鉴于以上各种情况,我开始寻找一种新的,科学的,高效的UI管理方式,

最开始想到的就是大名鼎鼎的MVC模式,我想过用它来管理我的UI,不过由于我对MVC模式不是很熟悉,尝试了下,效果并不是很好。

在网上搜到几个不错的UI框架,都写得很不错,各位可以参考参考:

1.Unity-UI-Framework的设计与实现 By:王选易 Github

2.简单、强大的TTUIFramework By:chiuan 游戏蛮牛 (这位的设计思路是参考上面那位作者的)

3.【设计和开发一套自动化UI框架】 By:NPC燕 游戏蛮牛

 

他们的设计思路都很清晰,做出来的效果也都很强大,都是些游戏界的大牛,但是都有点复杂。

作为一名菜鸟,我当然要设计出只有菜鸟能看得懂的东西啦。

好了,废话不多说,以下是我设计的两个主要方向:

1.设计一套用于管理UI的框架,各个UI的生成,销毁,切换,都是通过这个Manager单例来实现。各个UI间不直接联系。

2.所有UI都基于一个UIBase基类,每个UI的预设对应一个UIBase子类脚本,UI的基本逻辑在该类中实现,例如:_MenuView.prefab 对应 _MenuView.cs

3.重新实现Unity3D的消息通讯,原有SendMessage效率较低,利用订阅-发布(即观察者模式)重新设计一套通讯中心,所有UI间的通讯,

以及UI和游戏层的通讯,皆间接通过MessageCenter来管理。实现解耦。

4.以栈发方式管理UI,每次打开一个新的UI,都将它堆入栈,关闭时出栈。这个栈是一个特殊的栈,例如它可以实现,某个不在栈顶的UI,可以“TOP”到栈顶。

 

几个不错的UI框架

1.Unity-UI-Framework的设计与实现 By:王选易 Github

http://www.manew.com/thread-42748-1-1.html

https://github.com/MrNerverDie/Unity-UI-Framework

2.简单、强大的TTUIFramework By:chiuan 游戏蛮牛 (这位的设计思路是参考上面那位作者的)

http://www.manew.com/thread-42929-1-1.html?_dsign=8c9bee85

3.【设计和开发一套自动化UI框架】 By:NPC燕 游戏蛮牛

http://www.manew.com/thread-41889-1-1.html?_dsign=57fa0b1a

未完待续。。。

2018-04-11 13:06:58 qq_35361471 阅读数 4193
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

引言:为了小生的三个粉丝,坚持更新。今日分享的是如何搭建自己的UI框架,从而有效管理用户界面。这里主要讲解实现的思想及主要步骤。在此,感谢Siki老师。
开发版本:Unity 2017.1.1f1、VS 2017
适合人群:适合有一定基础的童鞋!

一、什么是UI框架

UI框架用于管理场景中的所有面板,控制面板之间的切换,可以加快开发进度、提高代码质量。

二、实现思路

根据用户界面调用情况,分析有如下四种状态:
  1. 进入状态:界面第一次被动态加载使用的时候
  2. 暂停状态:切换到其他界面的时候
  3. 继续状态:重新回到界面的时候
  4. 退出状态:界面不显示的时候
实现步骤:
  1. 使用JSON保存面板路径,枚举保存面板类型
  2. 根据界面共有的四种状态,创建UI基类BasePanel,场景中的界面继承该基类,并将四种状态写成虚方法,依次分别为OnEnter(),OnPause(),OnResume(),OnExit(),提供给子类重写。
  3. 通过管理类UIManager,解析JSON,管理UI界面的加载和切换。为了方便调用,做成单例模式。分别用两个字典保存从JSON读取的面板信息和已动态加载实例化的面板。通过栈来管理场景中所有面板之间的切换。
缺点也比较明显,因为用栈来存储场景中依次打开的界面,也只能依次从栈顶界面开始关闭。

页面状态流程图如下所示:

UI框架类图如下:

三、代码实现

UI界面基类
BasePanel.cs
public class BasePanel : MonoBehaviour
{
    /// <summary>
    /// 界面显示出来
    /// </summary>
    public virtual void OnEnter() { }

    /// <summary>
    /// 界面暂停(弹出了其他界面)
    /// </summary>
    public virtual void OnPause() { }

    /// <summary>
    /// 界面继续(其他界面移除,回复本来的界面交互)
    /// </summary>
    public virtual void OnResume() { }

    /// <summary>
    /// 界面不显示,退出这个界面,界面被关闭
    /// </summary>
    public virtual void OnExit() { }
}

UIManager.cs

public class UIManager
{
    // 单例模式:定义一个静态的对象,构造方法私有化,内部构造,用于外部访问
    private static UIManager _instance;
    public static UIManager Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new UIManager();
            }
            return _instance;
        }
    }

    //字典存储所有面板的Prefabs路径
    private Dictionary<UIPanelType, string> panelPathDict = new Dictionary<UIPanelType, string>();
    //保存所有已实例化面板的游戏物体身上的BasePanel组件
    private Dictionary<UIPanelType, BasePanel> panelDict = new Dictionary<UIPanelType, BasePanel>();
    //存储当前场景中的界面
    private Stack<BasePanel> panelStack = new Stack<BasePanel>();

    private Transform canvasTransform;
    private Transform CanvasTransform
    {
        get
        {
            if (canvasTransform == null)
            {
                canvasTransform = GameObject.Find("Canvas").transform;
            }
            return canvasTransform;
        }
    }

    private UIManager()
    {
        ParseUIPanelTypeJson();
    }

    /// <summary>
    /// 解析JSON,获取所有面板的路径信息
    /// </summary>
    private void ParseUIPanelTypeJson()
    {
        TextAsset ta = Resources.Load<TextAsset>("UIPanelType");
        JsonData jsonDataArray = JsonMapper.ToObject(ta.text);
        foreach (JsonData item in jsonDataArray)
        {
            UIPanelType panelType = (UIPanelType)Enum.Parse(typeof(UIPanelType), item["panelType"].ToString());
            string path = item["path"].ToString();
            panelPathDict.Add(panelType, path);
        }
    }

    /// <summary>
    /// 根据面板类型,返回对应的BasePanel组件
    /// </summary>
    /// <param name="panelType">需要返回的面板类型</param>
    /// <returns>返回该面板组件</returns>
    private BasePanel GetPanel(UIPanelType panelType)
    {
        BasePanel basePanel = panelDict.GetValue(panelType);
        //如果panel为空,根据该面板prefab的路径,实例化该面板
        if (basePanel == null)
        {
            string path = panelPathDict.GetValue(panelType);
            GameObject newPanel = GameObject.Instantiate(Resources.Load<GameObject>(path)) as GameObject;
            newPanel.transform.SetParent(CanvasTransform, false); 
            //第一次实例化的面板需要保存在字典中
            panelDict.Add(panelType, newPanel.GetComponent<BasePanel>());
            return newPanel.GetComponent<BasePanel>();
        }
        else
        {
            return basePanel;
        }
    }

    /// <summary>
    /// 设置默认的栈顶元素
    /// </summary>
    /// <param name="panelType">界面类型</param>
    /// <param name="basePanel">组件</param>
    public void SetDefaultPopPanel(UIPanelType panelType,BasePanel basePanel)  
    {
        panelDict.Add(panelType, basePanel);
        panelStack.Push(basePanel);
    }

    /// <summary>
    /// 把该页面显示在场景中
    /// </summary>
    /// <param name="panelType">需要显示界面的类型</param>
    public void PushPanel(UIPanelType panelType)
    {
        //判断一下栈里面是否有页面
        if (panelStack.Count > 0)
        {
            panelStack.Peek().OnPause();//原栈顶界面暂停
        }
        BasePanel panel = GetPanel(panelType);
        panel.OnEnter();//调用进入动作
        panelStack.Push(panel);//页面入栈
    }

    /// <summary>
    /// 关闭栈顶界面显示
    /// </summary>
    public void PopPanel()
    {
        //当前栈内为空,则直接返回
        if (panelStack.Count <= 0) return;
        panelStack.Pop().OnExit();//Pop删除栈顶元素,并关闭栈顶界面的显示,
        if (panelStack.Count <= 0) return;
        panelStack.Peek().OnResume();//获取现在栈顶界面,并调用界面恢复动作
    }
}

四、案例讲解

点击主菜单按钮,实现各个面板之间的切换效果,源文件在末尾。
效果展示如下:

将制作好的面板做成预制体放在Resources文件中,如下所示:

将状态面板类型记录在枚举中,如下所示:
public enum UIPanelType
{
    ItemMessagePanel, 
    CharacterPanel,
    KnapsackPanel,
    MainMenuPanel,
    ShopPanel,
    SkillPanel,
    SystemPanel,
    TaskPanel
}
根据面板和其路径,记录在JSON中,如下所示:
[
    {
        "panelType": "ItemMessagePanel",
        "path": "UIPanel/ItemMessagePanel"
    },
    {
        "panelType": "CharacterPanel",
        "path": "UIPanel/CharacterPanel"
    },

    {
        "panelType": "KnapsackPanel",
        "path": "UIPanel/KnapsackPanel"
    },

    {
        "panelType": "ShopPanel",
        "path": "UIPanel/ShopPanel"
    },

    {
        "panelType": "SkillPanel",
        "path": "UIPanel/SkillPanel"
    },

    {
        "panelType": "SystemPanel",
        "path": "UIPanel/SystemPanel"
    },
    {
        "panelType": "TaskPanel",
        "path": "UIPanel/TaskPanel"
    }
]

各个界面分别创建BasePanel的子类,用于实现界面的四种状态。为了方便实现界面的动画,使用DOTween插件,如果不会的话,可以看我之前的学习博客。 https://blog.csdn.net/qq_35361471/article/details/79353071
为各个界面添加CanvasGroup组件,如下:

Alpha可以方便实现隐藏和显示,取消勾选Blocks Raycasts,可以实现屏蔽UI射线检测
主菜单界面需要实现一个提供给各个界面按钮的方法接口,如下:
public void OnPushPanel(string panelTypeString)
 {
        UIPanelType panelType = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);
        UIManager.Instance.PushPanel(panelType);
}
给对应按钮添加监听方法的时候,需要注意,这里的字符串要和枚举UIPanelType里的值写的一致。

以CharacterPanel.cs为例,继承BasePanel,并重写四种方法,使用DOTween实现动画,代码如下:
using UnityEngine;
using DG.Tweening;

public class CharacterPanel : BasePanel
{
    private CanvasGroup canvasGroup;

    void Start()
    {
        if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>();
    }

    public override void OnEnter()
    {
        if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>();
        canvasGroup.alpha = 1;
        canvasGroup.blocksRaycasts = true;

        Vector3 temp = transform.localPosition;
        temp.x = -800;
        transform.localPosition = temp;
        transform.DOLocalMoveX(0, 0.5f);
    }

    public override void OnPause()
    {
        canvasGroup.blocksRaycasts = false;
    }

    public override void OnResume()
    {
        canvasGroup.blocksRaycasts = true;
    }

    public override void OnExit()
    {
        transform.DOLocalMoveX(-800, .5f).OnComplete(() => canvasGroup.alpha = 0);
    }

    public void OnClosePanel()
    {
        UIManager.Instance.PopPanel();
    }
}


其他界面代码实现类似,可以自己实现不同的切换效果。觉得我分享的学习博客有用的话,记得点赞关注我哦!

源文件百度云链接:链接:https://pan.baidu.com/s/1r-4AKfeO1WxUiENuo1lLLQ 密码:vrs1

 
2020-03-05 16:28:22 qq_33413868 阅读数 6543
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

最新:

参考公司lua的mvvm写了一个c#版的,https://github.com/so-sos-so/SoF,欢迎提建议

后续会新开一篇文章补充开发思路

------------------------------弃用---------------------------------

之前学了刘国柱老师的UI框架加上我自己的理解做了一个UI框架正好可以使用

这里附上刘国柱老师的博客 http://www.cnblogs.com/LiuGuozhu/ 受益颇深

基本UI框架的类图就是这样了

大体是根据MVC加上我的魔改而成的框架,至少我自己暂时觉得还是思路挺清晰的

Panel的切换是使用的Canvas Group的更改透明度,在加上使用

此方法来更改显示顺序和显隐的

其中的界面反向切换是使用的栈后入先出的特性

反向切换是指一个窗口弹出小窗口后,父窗口的Raycast需要取消,下面贴上核心代码

using System.Collections;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UIManager :MonoBehaviour {

    /// <summary>
    /// 单例模式:
    /// 1.定义一个静态的对象 在外界访问 内部构造
    /// 2.构造方法私有化
    /// </summary>
    #region 单例模式

    private static UIManager _instance;

    public static UIManager Instance
    {
        get
        {
            if (_instance == null)
            {
                UIManager[] instances = FindObjectsOfType<UIManager>();
                if (instances != null)
                {
                    _instance = instances[0];
                    for (var i = 1; i < instances.Length; i++)
                    {
                        Destroy(instances[i].gameObject);
                    }
                }
            }
            return _instance;
        }
    }

    #endregion


    private Transform _canvas;
    public Transform Canvas
    {
        get
        {
            if (_canvas == null)
                _canvas = GameObject.Find("Canvas").transform;
            return _canvas;
        }
    }
    //存储所有面板prefab的地址
    private Dictionary<UIPanelType, string> _DicPanelPath;
    //存储所有实例化的面板身上的BasePanel组件
    private Dictionary<UIPanelType, BasePanel> _DicPanel;
    //存储所有显示的面板上的BasePanel组件
    private Dictionary<UIPanelType, BasePanel> _DicShowPanel;
    //表示显示面板的栈
    private Stack<BasePanel> _StackPanel;
    //全屏显示的节点
    private Transform _TraFullScreen = null;
    //固定显示的节点
    private Transform _TraFixed = null;
    //弹出节点
    private Transform _TraPopUp = null;
    ////脚本节点
    //private Transform _TraScript = null;


    private void Awake()
    {
        ParseUIPanelPathJson();
        _StackPanel = new Stack<BasePanel>();
        _DicPanel = new Dictionary<UIPanelType, BasePanel>();
        _DicShowPanel = new Dictionary<UIPanelType, BasePanel>();
        _TraFullScreen = Canvas.Find("FullScreen");
        _TraFixed = Canvas.Find("Fixed");
        _TraPopUp = Canvas.Find("PopUp");
        DontDestroyOnLoad(Canvas.gameObject);
        //_TraScript = Canvas.Find("Script");
    }



    /// <summary>
    /// 把类型为反向切换的窗体入栈
    /// </summary>
    /// <param name="type"></param>
    private void PushPanel(UIPanelType type)
    {
        BasePanel basePanel = GetPanel(type);
        if (_StackPanel.Count >= 1)
        {
            BasePanel topPanel = _StackPanel.Peek();
            topPanel.OnPause();
        }
        _StackPanel.Push(basePanel);
        basePanel.OnEnter();
    }

    /// <summary>
    /// 把类型为反向切换的窗体出栈
    /// </summary>
    /// <param name="type"></param>
    private void PopPanel(UIPanelType type)
    {
        //如果栈里面为空,则返回
        if (_StackPanel.Count <= 0) return;
        BasePanel basePanel = GetPanel(type);
        BasePanel topPanel = _StackPanel.Peek();
        //如果当前要出栈的界面跟栈顶界面相同时 才会出栈
        if(basePanel.gameObject == topPanel.gameObject)
        {
            _StackPanel.Pop();
            topPanel.OnExit();
            //如果出栈后栈里面还有界面则恢复
            if(_StackPanel.Count >= 1)
            {
                _StackPanel.Peek().OnResume();
            }
        }
        else
        {
            Debug.LogError("栈顶界面不是此界面,请检查进出栈!!");
        }
        
    }

    /// <summary>
    /// 显示界面
    /// </summary>
    /// <param name="type"></param>
    public void ShowUIForms(UIPanelType type)
    {
        BasePanel basePanel = GetPanel(type);
        switch (basePanel.uiType.uiFormShowMode)
        {
            case UIFormShowMode.Normal:
                EnterUIFormsCache(type);
                break;
            //如果是反切换类型则入栈
            case UIFormShowMode.ReverseChange:
                PushPanel(type);
                break;
            //如果是隐藏其他的类型,清空显示的界面
            case UIFormShowMode.HideOther:
                HideOtherPanel(type);
                break;
        }
    }

    /// <summary>
    /// 隐藏界面 如果显示界面字典里面存在此界面
    /// </summary>
    /// <param name="type"></param>
    public void HideUIForms(UIPanelType type)
    {
        BasePanel basePanel = GetPanel(type);

        switch (basePanel.uiType.uiFormShowMode)
        {
            case UIFormShowMode.Normal:
                HideUIFormsCache(type);
                break;
            //如果是反切换类型则出栈
            case UIFormShowMode.ReverseChange:
                PopPanel(type);
                break;
            case UIFormShowMode.HideOther:
                HideUIFormsCache(type);
                break;
        }
    }

    /// <summary>
    /// 隐藏除此之外的Panel,如果不赋值则清空所有
    /// </summary>
    /// <param name="type"></param>
    public void HideOtherPanel(UIPanelType type = UIPanelType.Null)
    {
        foreach (KeyValuePair<UIPanelType,BasePanel> item in _DicShowPanel)
        {
            item.Value.OnExit();
        }
        _DicShowPanel.Clear();
        if (type != UIPanelType.Null)
        {
            BasePanel basePanel = GetPanel(type);
            basePanel.OnEnter();
            _DicShowPanel.Add(type, GetPanel(type));
        }
            
    }

    /// <summary>
    /// 将UI加入到已显示面板的字典中
    /// </summary>
    /// <param name="type"></param>
    private void EnterUIFormsCache(UIPanelType type)
    {
        //如果显示面板字典里面有当前面板则返回
        if (_DicShowPanel.ContainsKey(type)) return;
        BasePanel basePanel = GetPanel(type);
        _DicShowPanel.Add(type, basePanel);
        basePanel.OnEnter();
    }

    /// <summary>
    /// 将UI隐藏
    /// </summary>
    /// <param name="type"></param>
    private void HideUIFormsCache(UIPanelType type)
    {
        //如果显示面板字典里面没有当前面板则返回
        if (!_DicShowPanel.ContainsKey(type)) return;
        BasePanel basePanel = GetPanel(type);
        _DicShowPanel.Remove(type);
        basePanel.OnExit();
    }

    /// <summary>
    /// 根据面板类型得到实例化的面板
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private BasePanel GetPanel(UIPanelType type)
    {
        if (_DicPanel.ContainsKey(type))
        {
            return _DicPanel[type];
        }
        else
        {
            //如果找不到就找这个面板的prefab的路径,然后根据prefab去实例化面板
            BasePanel panel;
            string panelPath = _DicPanelPath.GetValue(type);
            GameObject insGo = GameObject.Instantiate(Resources.Load(panelPath),Canvas) as GameObject;
            insGo.name = type.ToString();
            panel = insGo.GetComponent<BasePanel>();
            switch (panel.uiType.uIFormParentType)
            {
                case UIFormParentType.FUllScreen:
                    insGo.transform.SetParent(_TraFullScreen);
                    break;
                case UIFormParentType.Fixed:
                    insGo.transform.SetParent(_TraFixed);
                    break;
                case UIFormParentType.PopUp:
                    insGo.transform.SetParent(_TraPopUp);
                    break;
            }
            _DicPanel.Add(type, panel);
            return panel;
        }
    }

    #region 解析地址json 并存储到字典中
    
    private void ParseUIPanelPathJson()
    {
        TextAsset text = Resources.Load<TextAsset>("UIPanelType");
        JSONObject jSONObject = new JSONObject(text.text);
        if (_DicPanelPath == null)
            _DicPanelPath = new Dictionary<UIPanelType, string>();
        foreach (JSONObject item in jSONObject.list)
        {
            string panelTypeString = item["panelTypeString"].str;
            string path = item["path"].str;
            UIPanelType uIPanelType = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);
            _DicPanelPath.Add(uIPanelType, path);
        }
    }

    #endregion

}
using System.Collections;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;


public class BasePanel : MonoBehaviour {


    protected CanvasGroup cg;
    public UIType uiType;


    private void Awake()
    {
        InitData();
    }


    public virtual void OnCloseBtnClick()
    {
        UIManager.Instance.HideUIForms(uiType.uiPanelType);
    }


    public virtual void InitData()
    {
        if (cg == null)
            cg = this.gameObject.GetAndAddComponent<CanvasGroup>();
        if (uiType == null)
            uiType = new UIType();
    }




    /// <summary>
    /// 界面被显示出来
    /// </summary>
    public virtual void OnEnter()
    {
        cg.alpha = 1;
        cg.blocksRaycasts = true;
        this.gameObject.transform.SetAsLastSibling();
    }


    /// <summary>
    /// 界面暂停 指的是当其他界面在此界面之上,此界面不是主界面的时候,此界面不接受鼠标检测,如果不需要此功能可以适当更改
    /// </summary>
    public virtual void OnPause()
    {
        cg.blocksRaycasts = false;
    }


    /// <summary>
    /// 界面继续
    /// </summary>
    public virtual void OnResume()
    {
        cg.blocksRaycasts = true;
    }


    /// <summary>
    /// 界面不显示,退出这个界面
    /// </summary>
    public virtual void OnExit()
    {
        cg.alpha = 0;
        cg.blocksRaycasts = false;
    }
	
}

 

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

public class UIType  {

    /// <summary>
    /// panel类别
    /// </summary>
    public UIPanelType uiPanelType;
    /// <summary>
    /// Panel的显示方式,有正常显示,隐藏其他和反向切换
    /// </summary>
    public UIFormShowMode uiFormShowMode = UIFormShowMode.Normal;
    /// <summary>
    /// 这个透明度暂时没用
    /// </summary>
    public UIFormLucencyType uIFormLucencyType = UIFormLucencyType.Lucency;
    /// <summary>
    /// Panel的父物体类型
    /// </summary>
    public UIFormParentType uIFormParentType = UIFormParentType.PopUp;

 

代码比较多,不过都有贴心的注释

 

-------------------------------加了一个代码自动生成的工具-----------------------------------

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading.Tasks;
using System;
using UnityEditor;
using System.IO;
using System.Text;
using UnityEngine.UI;

public class UIElement
{
    public string Name;
    public string Path;
    public string ComponentName;
    public UIElement(string name,string path,string componentName)
    {
        Name = name;
        Path = path;
        ComponentName = componentName;
    }

    public override string ToString()
    {
        string str = string.Format("Name={0} || Path={1} || ComponentName={2}", Name, Path, ComponentName);
        return str;
    }
}

public class UICodeGenerator
{
    [MenuItem ( "Assets/CreateCodeDeleteComponent" )]
    public static void CreateCodeDeleteComponent ()
    {
        GetPath ( true );
    }
    
    [MenuItem ( "Assets/OnlyCreateCode" )]
    public static void OnlyCreateCode ()
    {
        GetPath ( false );
    }
    
    public static void GetPath (bool isDeleteComponent)
    {
        var objs =
            Selection.GetFiltered ( typeof ( GameObject ), SelectionMode.Assets | SelectionMode.TopLevel );
        GameObject obj = objs[ 0 ] as GameObject;
        elements = new List<UIElement> ();
        GetPathAs ( obj.transform, isDeleteComponent );

        foreach ( var item in elements )
        {
            Debug.Log ( item );
        }

        GeneratePane ( "Assets/" + obj.name + ".cs", obj.name, elements );
        GenerateCtrl ( "Assets/" + obj.name + "Ctrl.cs", obj.name, elements );

    }

    public static List<UIElement> elements;

    static void GetPathAs ( Transform transform ,bool isDeleteComponent)
    {
        foreach ( Transform child in transform )
        {
            if ( child.gameObject.GetComponent<UIMark> () )
            {
                elements.Add ( new UIElement ( child.name, GetPath ( child ),
                                               child.gameObject.GetComponent<UIMark> ().ComponentName ) );
                if ( isDeleteComponent )
                    GameObject.DestroyImmediate ( child.gameObject.GetComponent<UIMark> (), true );
            }

            if ( child.childCount != 0 )
            {
                GetPathAs ( child, isDeleteComponent );
            }
        }
    }


    public static void GeneratePane ( string generateFilePath, string behaviourName, List<UIElement> elements )
    {
        var sw         = new StreamWriter ( generateFilePath, false, Encoding.UTF8 );
        var strBuilder = new StringBuilder ();

        strBuilder.AppendLine ( "using UnityEngine;" );
        strBuilder.AppendLine ( "using UnityEngine.UI;" );
        strBuilder.AppendLine ();
        strBuilder.AppendFormat ( "public class {0} : BasePanel ", behaviourName );
        strBuilder.AppendLine ();
        strBuilder.AppendLine ( "{" );
        foreach ( var item in elements )
        {
            strBuilder.AppendLine ( "\tpublic " + item.ComponentName + " " + item.Name + " { get; private set; }" );
        }

        strBuilder.AppendLine ();
        strBuilder.AppendLine ( "\tpublic override void InitData()" );
        strBuilder.AppendLine ( "\t{" );
        foreach ( var item in elements )
        {
            strBuilder.AppendFormat ( "\t\t{0} = transform.Find(\"{1}\").GetComponent<{2}>();", item.Name,
                                      item.Path.Replace ( behaviourName + "/", "" ), item.ComponentName );
            strBuilder.AppendLine ();
        }

        strBuilder.AppendLine ();
        strBuilder.AppendLine ( "\t\tuiType.uIFormParentType = UIFormParentType.PopUp;" );
        strBuilder.AppendLine ( "\t\tuiType.uiFormShowMode = UIFormShowMode.Normal;" );
        strBuilder.AppendLine ( "\t\tuiType.uiPanelType = UIPanelType.BoxPanel;" );
        strBuilder.AppendLine ( "\t}" );
        strBuilder.AppendLine ( "}" );
        sw.Write ( strBuilder );
        sw.Flush ();
        sw.Close ();

        AssetDatabase.SaveAssets ();
        AssetDatabase.Refresh ();
    }

    public static void GenerateCtrl ( string generateFilePath, string behaviourName , List<UIElement> elements)
    {
        var sw         = new StreamWriter ( generateFilePath, false, Encoding.UTF8 );
        var strBuilder = new StringBuilder ();

        List<UIElement> temp = new List<UIElement> ();
        
        foreach ( UIElement element in elements )
        {
            if(element.ComponentName.Equals("Button"))
                temp.Add(element);
        }
        
        strBuilder.AppendLine ( "using UnityEngine;" );
        strBuilder.AppendLine ( "using UnityEngine.UI;" );
        strBuilder.AppendLine ();
        strBuilder.AppendFormat ( "public class {0}Ctrl : BaseCtrl ", behaviourName );
        strBuilder.AppendLine ( "{" );
        strBuilder.AppendLine ();
        strBuilder.AppendFormat ( "\tprivate {0} panel;", behaviourName );
        strBuilder.AppendLine ();
        strBuilder.AppendLine ();
        strBuilder.AppendLine ( "\tpublic override void InitPanel()" );
        strBuilder.AppendLine ( "\t{" );
        strBuilder.AppendFormat ( "\t\tpanel = GetComponent<{0}>();", behaviourName );
        strBuilder.AppendLine ();
        foreach ( UIElement element in temp )
        {
            strBuilder.AppendFormat ( "\t\tpanel.{0}.AddListenerGracefully( {1}Click );", element.Name, element.Name );
            strBuilder.AppendLine ();
        }
        strBuilder.AppendLine ( "\t}" );
        strBuilder.AppendLine ();
        foreach ( UIElement element in temp )
        {
            strBuilder.AppendFormat ("\tvoid {0}Click()",element.Name);
            strBuilder.AppendLine ();
            strBuilder.AppendLine ( "\t{" );
            strBuilder.AppendLine ();
            strBuilder.AppendLine ( "\t}" );
            strBuilder.AppendLine ();
        }

        strBuilder.AppendLine ( "}" );
        sw.Write ( strBuilder );
        sw.Flush ();
        sw.Close ();

        AssetDatabase.SaveAssets ();
        AssetDatabase.Refresh ();
    }

    public static string GetPath ( Transform transform )
    {
        var sb = new System.Text.StringBuilder ();
        var t  = transform;
        while ( true )
        {
            sb.Insert ( 0, t.name );
            t = t.parent;
            if ( t )
            {
                sb.Insert ( 0, "/" );
            }
            else
            {
                return sb.ToString ();
            }
        }
    }
}

 

 

using UnityEngine.UI;
using UnityEngine;

/// <inheritdoc />
/// <summary>
/// UI的标记
/// </summary>
public class UIMark : MonoBehaviour
{
    //public UIMarkType MarkType = UIMarkType.DefaultUnityElement;

    public Transform Transform
    {
        get { return transform; }
    }

    public string CustomComponentName = "";
    
    public virtual string ComponentName
    {
        get
        {
            if ( !string.IsNullOrEmpty ( CustomComponentName ) )
                return CustomComponentName;
            if (null != GetComponent("SkeletonAnimation"))
                return "SkeletonAnimation";
            if (null != GetComponent<ScrollRect>())
                return "ScrollRect";
            if (null != GetComponent<InputField>())
                return "InputField";
            if (null != GetComponent<Text>())
                return "Text";
            if (null != GetComponent("TMP.TextMeshProUGUI"))
                return "TextMeshProUGUI";
            if (null != GetComponent<Button>())
                return "Button";
            if (null != GetComponent<RawImage>())
                return "RawImage";
            if (null != GetComponent<Toggle>())
                return "Toggle";
            if (null != GetComponent<Slider>())
                return "Slider";
            if (null != GetComponent<Scrollbar>())
                return "Scrollbar";
            if (null != GetComponent<Image>())
                return "Image";
            if (null != GetComponent<ToggleGroup>())
                return "ToggleGroup";
            if (null != GetComponent<Animator>())
                return "Animator";
            if (null != GetComponent<Canvas>())
                return "Canvas";
            if (null != GetComponent("Empty4Raycast"))
                return "Empty4Raycast";
            if (null != GetComponent<RectTransform>())
                return "RectTransform";

            return "Transform";


        }
    }
}

使用方法,把需要使用的组件加上UIMark标记,拖成预设体,右键点击自动生成代码,就会自动生成视图和控制层

-------------------------------弃用-------------------------------------

思想可以借鉴,但是使用起来不是很方便

最新版框架借鉴QFramework,喜欢的可以去看看

2019-04-03 01:34:20 Mr_Sun88 阅读数 833
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

                        Unity3D框架学习_基于FairyGUI的UI框架


目录

1、博客介绍

2、FGUI介绍

3、框架介绍

(1)Core

(2)结构

(3)FGUI工程目录

(4)命名规范

4、推送

5、结语


1、博客介绍

       最近一段时间一直都在使用FGUI,真心觉得非常好用,目前还处在学习阶段,在Github上扒了一些稍作整理,做了一个非常简单的框架,目前还处在初级阶段,后续发现问题了会持续更新,文章末尾会贴上github地址,有兴趣的关注,本篇文章只介绍相关结构和思路。


2、FGUI介绍

话不多少:http://www.fairygui.com/


3、框架介绍

(1)Core

        主要思想就是,FGUI的每个包作为UI的场景,在每个包内包含多个Window作为不同的UI页面,UI页面继承了FGUI的Window属性,本身作为一个窗口组件去拓展,方便展示关闭。

(2)结构

PanelManager用来管理FGUI包的加载和卸载,UIManager用来管理包内UI页面的调用

(3)FGUI工程目录

(4)命名规范

这里命名规范只是建议,唯一一点强制的是继承了window属性的UI页面脚本内的winName必须和当前类名保持一致


4、推送

工程github:https://github.com/KingSun5/FGUI_Framework  内含演示小例子

学习借鉴:https://github.com/YKPublicGame/LayaYKFamework


5、结语

        FGUI是个非常好用的UI编辑器,大大的提高了工作效率,学习成本低,在此感谢作者谷主大人的无私奉献,这里的小框架很简单,博主也还在学习阶段,希望对读者能有所帮助,另博主能力有限,若文中有出现什么错误的地方,欢迎各位评论指摘。

       QQ交流群:806091680(Chinar)

       该群为CSDN博主Chinar所创,推荐一下!我也在群里!

       本文属于原创文章,转载请著名作者出处并置顶!!!!!

2018-05-27 21:58:03 zcaixzy5211314 阅读数 6132
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

UGUI系列博文,可通过导航帖查看

前言

最近自己写了一套简单的UI框架,基本思想是基于mvc的,但是由于是给比较小型的项目设计的框架,整体解耦并不十分彻底,这样降低了调试难度,整体复杂度也相对较低,算是按自己的理解做了取舍。

1.接口部分

1)底层系统类,全局唯一

AUIRoot:是根节点抽象类,负责所有系统的初始化
IUIManager:是UI组件的管理类,提供UI界面显示,返回等统一接口
IUIDataHandlerManager :是数据处理器管理类,提供对数据处理器的缓存,添加,移除等接口
IUILayerManager :是UI层级管理类,根据UI预设的层级划分,系统自动设置UI的父物体,利用unity的自然层级管理UI的显示层级
IUIEffectManager :是UI动效管理类,架构内的动效类与UI系统完全解耦,此类负责管理UI动效的播放,提供UI物体Active状态及对象初始化状态的回调接口
AUIPathManager :UI路径管理接口,类似与配置文件,需要手动在类的字典UIPathDic里配置路径
####2)其他接口
AUIBase :UI基类,定义了处理UI的状态切换及回调事件等接口
AUIEffect :UI动效基类,定义了UI动效切换的接口及回调事件
IData :数据基类,用标记类为数据类
DataHandler :数据处理器接口,定义数据初始化,数据更新接口,此类进行数据的操作

2.结构构思

1)枚举

UI状态枚举
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mG9OgytF-1577879706134)(https://i.loli.net/2018/05/27/5b0aa498e8a67.jpg)]
NOTINIT:是一个UI的默认状态,也就是UI对象还未生成出来的状态
INIT:是UI对象生成出来时,第一个执行的状态,初始化完成会自动执行SHOW状态,类似unity的Awake只执行一次
SHOW:是当UI显示时执行的函数
HIDE:UI隐藏时执行的函数

UI层级枚举

2)基本构思

------1)UIRoot会自动初始化所有系统,其中UILayerManager会自动生成层级父物体
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NoYUUBgp-1577879706136)(https://i.loli.net/2018/05/27/5b0aa70515420.jpg)]
用这种方式来给UI分层

------2)通过UIManager来控制UI的状态切换,在调用UIManager的show方法时,会自动调用UIlayerManger来设置UI的层级,并生成对象到对应的父物体下

------3)通过事件绑定的方式,若是对象上有动效脚本就会自动执行,没有就正常执行逻辑,动效部分代码是完全和UI逻辑分离的,就算删除动效部分的代码,完全不影响UI系统的执行

------4)所有的UI类要继承UIBase类,所有的UI类只处理UI的显示,每一个UI类都有对应的数据类和数据处理类

3.使用说明

1)自定义枚举

需要自定义UI的ID枚举,如

实际在框架内部,我是使用的string来存放ID,所以这里有两种方式:
------1.像图中这样,定义枚举来定义ID,在使用UIManager的show方法时,传进去即可
------2.定义一个类,在类中定义ID的静态只读字符串

2)自定义管理类

-----(1)继承UIDataHandlerManager类,实现自定义数据处理器管理类,需要实现RegisterHandler方法,如:

需要在RegisterHandler方法内如图中的方式,初始化自定义的数据处理器类,并缓存到字典内
-----(2)继承AUIPathManager类,实现自定义路径管理类,需要实现InitPathDic方法,如:

图中value的字符串是路径,测试代码的预制体直接放到了Resources下,所以这里直接赋值的预制体名称
有两种应用方式:
-----------1.EUiId枚举直接定义成预制体名称,这样就不需要自己配置字符串了,但是这样预制体在Resources下必须都放到一个文件夹下,不能分类存放
-----------2.如图中这样,分别配置每个预制体的路径,可以在Resources下分类存放
-----(3)继承UIRoot类,实现自定义UI节点类,需要继承两个方法
-----------1.继承Start方法,如
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FQrPip1u-1577879706144)(https://i.loli.net/2018/05/27/5b0ab6162e29b.jpg)]
实现启动主界面的业务逻辑
------------2.继承InitUISystem方法,初始化需要自定义的系统,如

3)自定义UI显示层脚本

------1.需要根据UI的自身的需要选择继承基类,如主界面之类的这种全屏界面,一般是继承BasicUI类,像浮框这种界面,一般继承OverlayUI类,还有需要显示在这之上的UI就可以继承TopUI类
这些自定义类需要实现类的Init方法,并在Init方法中初始化调用InitUI方法来初始化UI数据,如:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eUaqPupQ-1577879706146)(https://i.loli.net/2018/05/27/5b0ab434d6c9c.jpg)]
需要赋值这个界面的ID还有自定义数据处理类的ID

------2.每个类需要定义它自己的UI数据类和数据处理器
每个数据处理器的NAME需要唯一,需要自己在创建类时定义,如
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gBNtO5Qa-1577879706147)(https://i.loli.net/2018/05/27/5b0ab4ce55460.jpg)]
数据的初始化是在InitData方法中,如

例子中就这样简单写,实际应该是从本地或者云端获取数据

4)自定义UI动效类

继承AUIEffect类实现自定义UI动效类,这个类需要继承两个方法
------1.Enter方法,在UI显示的时候执行,所以在这个方法里定义UI显示时的动效
------2.Exit方法,在UI隐藏的时候执行,所以在这个方法里定义UI隐藏时的动效
使用时需要把预制体需要的UI动效类挂载到UI预制体上即可,或者也可以自己写一个类进行管理,动态挂载

以上便是本人自己花了几天时间,写的UI框架,比较简单,希望路过的大牛能花点点时间,给与指证。
GitHub地址:https://github.com/BlueMonk1107/BlueUIFrame

我会在我的公众号上推送新的博文,也可以帮大家解答问题
微信公众号 Andy and Unity 搜索名称或扫描二维码
在这里插入图片描述
希望我们能共同成长,共同进步

Unity3D游戏UI框架

阅读数 69