开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

开发者专栏

关注:1723

当前位置:游戏蛮牛 技术专区 开发者专栏

__________________________________________________________________________________
开发者干货区版块规则:

  1、文章必须是图文形式。(至少2幅图)
      2、文章字数必须保持在1500字节以上。(编辑器右下角有字数检查)
      3、本版块只支持在游戏蛮牛原创首发,不支持转载。
      4、本版块回复不得无意义,如:顶、呵呵、不错......【真的会扣分的哦】
      5、......
__________________________________________________________________________________
查看: 6000|回复: 68

[刘国柱] 游戏UI框架设计(二) 最简版本设计

  [复制链接]  [移动端链接]

7

主题

17

帖子

365

积分

Rank: 9Rank: 9Rank: 9

UID
34511
好友
10
蛮牛币
370
威望
0
注册时间
2014-7-15
在线时间
135 小时
最后登录
2017-6-23

专栏作家

发表于 2017-4-12 21:03:53 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
游戏UI框架设计(二)
最简版本设计

  
   为降低难度决定先讲解一个最简版本,阐述UI框架的核心设计理念。这里先定义三个核心功能:

   1:UI窗体的自动加载功能。
   2:缓存UI窗体。
   3:窗体生命周期(状态)管理。

   UI框架设计主要目的,就是尽可能的完成一些与具体游戏功能逻辑无关的一些底层事务性的功能实现。这些功能最好是自动或者是半自动的实现,无须客户程序(调用框架的程序)再去过多处理与关心。

   对于以上功能,笔者定义了UI框架的相关四个核心类:
  •   BaseUIForms    基础UI窗体脚本(父类,其他窗体都继承此脚本)
  •   UIManger.cs    UI窗体管理器脚本(框架核心脚本)
  •   UIType             窗体类型 (引用窗体的重要属性[枚举类型])
  •   SysDefine        系统定义类(包含框架中使用到的枚举类型、委托事件、系统常量、接口等)

在SysDefine 文件中,定义本框架三个核心枚举类型:

[C#] 纯文本查看 复制代码
    //UI窗体(位置)类型
    public enum UIFormType
    {
        //普通窗体
        Normal,   
        //固定窗体                              
        Fixed,
        //弹出窗体
        PopUp
    }
    
    //UI窗体的显示类型
    public enum UIFormShowMode
    {
        //普通
        Normal,
        //反向切换
        ReverseChange,
        //隐藏其他
        HideOther
    }

    //UI窗体透明度类型
    public enum UIFormLucenyType
    {
        //完全透明,不能穿透
        Lucency,
        //半透明,不能穿透
        Translucence,
        //低透明度,不能穿透
        ImPenetrable,
        //可以穿透
        Pentrate    
    }


上述三个核心枚举类型,解释如下:

  •      UIFormType 枚举类型,表示Unity层级视图中挂载不同类型窗体的空节点。这里Fixed 表示固定窗体,表示可以挂载"非全屏非弹出窗体",例如RPG游戏项目中的“英雄信息”窗体等。
  •      UIFormShowMode 枚举,表示窗体不同的显示方式。Normal 类型表示窗体与其他窗体可以并列显示; HideOther类型表示窗体显示的时候,需要隐藏所有其他窗体; ReverseChange 窗体主要应用与"弹出窗体",维护多个弹出窗体的层级关系。
  •      UIFormLucenyType 枚举,是定义弹出“模态窗体”不同透明度的类型。

Canvas根节点预设

Canvas根节点预设


    上图是我们定义的UGUI 中的“根窗体”预设 "Canvas",在Untiy的层级视图中,可以看到我们定义了若干空节点,用于不同类型的UI窗体加载到不同的“根窗体”预设中,实现不同显示效果。
    定义 UIType 类,主要是引用定义的三个核心枚举,方便使用 。代码如下:



[C#] 纯文本查看 复制代码
    /// <summary>
    /// UI(窗体)类型
    /// </summary>
    internal class UIType
    {
        //是否需要清空“反向切换”
        public bool IsClearReverseChange = false;
        //UI窗体类型
        public UIFormsType UIForms_Type = UIFormsType.Normal;
        //UI窗体显示类型
        public UIFormsShowMode UIForms_ShowMode = UIFormsShowMode.Normal;
        //UI窗体透明度类型
        public UIFormsLucencyType UIForms_LucencyType = UIFormsLucencyType.Lucency;
    }


定义基础UI窗体 BaseUIForms 脚本,代码如下:

[C#] 纯文本查看 复制代码
public class BaseUIForms : MonoBehaviour
    {
        /*  字段  */
        //当前(基类)窗口的类型
        private UIType _CurrentUIType=new UIType();

        /*  属性  */
        /// <summary>
        /// 属性_当前UI窗体类型
        /// </summary>
        internal UIType CurrentUIType
        {
            set
            {
                _CurrentUIType = value;
            }

            get
            {
                return _CurrentUIType;
            }
        }

        //页面显示
        public virtual void Display()
        {
            this.gameObject.SetActive(true);
        }
        
        //页面隐藏(不在“栈”集合中)
        public virtual void Hiding()
        {
            this.gameObject.SetActive(false);
        }
        //页面重新显示
        public virtual void Redisplay()
        {
            this.gameObject.SetActive(true);
        }
        //页面冻结(还在“栈”集合中)
        public virtual void Freeze()
        {
            this.gameObject.SetActive(true);
        } 

    }//Class_end



   上述代码中,主要定义了UI窗体基类的四个重要虚方法,分别对应窗体的打开显示、隐藏、重新显示、窗体冻结(即:窗体显示在其他窗体下面)。方便窗体在不同状态下,针对不同的行为进一步做处理操作。例如,当窗体为“隐藏”与“冻结”状态时,如果此窗体有针对远程服务的网络连接(Socket套接字)时,则需要关闭网络连接,以节省网络资源。
     定义“UI管理器”(UIManager.cs) 脚本,这是UI框架中的核心脚本,主要负责UI窗体的加载、缓存、以及对于“UI窗体基类”的各种生命周期的操作(显示、隐藏、重新显示、冻结)。



[C#] 纯文本查看 复制代码
public class UIManager : MonoBehaviour {
        /* 字段 */
        private static UIManager _Instance = null;
        //UI窗体预设路径(参数1:窗体预设名称,2:表示窗体预设路径)
        private Dictionary<string, string> _DicFormsPaths; 
        //缓存所有UI窗体
        private Dictionary<string, BaseUIForm> _DicALLUIForms;
        //当前显示的UI窗体
        private Dictionary<string, BaseUIForm> _DicCurrentShowUIForms;
        //UI根节点
        private Transform _TraCanvasTransfrom = null;
        //全屏幕显示的节点
        private Transform _TraNormal = null;
        //固定显示的节点
        private Transform _TraFixed = null;
        //弹出节点
        private Transform _TraPopUp = null;
        //UI管理脚本的节点
        private Transform _TraUIScripts = null;


        /// <summary>
        /// 得到实例
        /// </summary>
        /// <returns></returns>
        public static UIManager GetInstance()
        {
            if (_Instance==null)
            {
                _Instance = new GameObject("_UIManager").AddComponent<UIManager>();
            }
            return _Instance;
        }

        //初始化核心数据,加载“UI窗体路径”到集合中。
        public void Awake()
        {
            //字段初始化
            _DicALLUIForms=new Dictionary<string, BaseUIForm>();
            _DicCurrentShowUIForms=new Dictionary<string, BaseUIForm>();
            _DicFormsPaths=new Dictionary<string, string>();
            //初始化加载(根UI窗体)Canvas预设
            InitRootCanvasLoading();
            //得到UI根节点、全屏节点、固定节点、弹出节点
            _TraCanvasTransfrom = GameObject.FindGameObjectWithTag(SysDefine.SYS_TAG_CANVAS).transform;
            _TraNormal = _TraCanvasTransfrom.Find("Normal");
            _TraFixed = _TraCanvasTransfrom.Find("Fixed");
            _TraPopUp = _TraCanvasTransfrom.Find("PopUp");
            _TraUIScripts = _TraCanvasTransfrom.Find("_ScriptMgr");
            //把本脚本作为“根UI窗体”的子节点。
            this.gameObject.transform.SetParent(_TraUIScripts, false);
            //"根UI窗体"在场景转换的时候,不允许销毁
            DontDestroyOnLoad(_TraCanvasTransfrom);
            //初始化“UI窗体预设”路径数据
            //先写简单的,后面我们使用Json做配置文件,来完善。
            if (_DicFormsPaths!=null)
            {
                _DicFormsPaths.Add("LogonUIForm", @"UIPrefabs\LogonUIForm");
            }
        }

        /// <summary>
        /// 显示(打开)UI窗体
        /// 功能:
        /// 1: 根据UI窗体的名称,加载到“所有UI窗体”缓存集合中
        /// 2: 根据不同的UI窗体的“显示模式”,分别作不同的加载处理
        /// </summary>
        /// <param name="uiFormName">UI窗体预设的名称</param>
        public void ShowUIForms(string uiFormName)
        {
            BaseUIForm baseUIForms=null;                    //UI窗体基类

            //参数的检查
            if (string.IsNullOrEmpty(uiFormName)) return;
            //根据UI窗体的名称,加载到“所有UI窗体”缓存集合中
            baseUIForms = LoadFormsToAllUIFormsCatch(uiFormName);
            if (baseUIForms == null) return;
            //根据不同的UI窗体的显示模式,分别作不同的加载处理
            switch (baseUIForms.CurrentUIType.UIForms_ShowMode)
            {                    
                case UIFormShowMode.Normal:                 //“普通显示”窗口模式
                    //把当前窗体加载到“当前窗体”集合中。
                    LoadUIToCurrentCache(uiFormName);
                    break;
                case UIFormShowMode.ReverseChange:          //需要“反向切换”窗口模式
                    //更靠后课程进行讲解。
                    break;
                case UIFormShowMode.HideOther:              //“隐藏其他”窗口模式
                    //更靠后课程进行讲解。
                    break;
                default:
                    break;
            }
        }

        #region 私有方法
        //初始化加载(根UI窗体)Canvas预设
        private void InitRootCanvasLoading()
        {
            ResourcesMgr.GetInstance().LoadAsset(SysDefine.SYS_PATH_CANVAS, false);
        }

        /// <summary>
        /// 根据UI窗体的名称,加载到“所有UI窗体”缓存集合中
        /// 功能: 检查“所有UI窗体”集合中,是否已经加载过,否则才加载。
        /// </summary>
        /// <param name="uiFormsName">UI窗体(预设)的名称</param>
        /// <returns></returns>
        private BaseUIForm LoadFormsToAllUIFormsCatch(string uiFormsName)
        {
            BaseUIForm baseUIResult = null;                 //加载的返回UI窗体基类

            _DicALLUIForms.TryGetValue(uiFormsName, out baseUIResult);
            if (baseUIResult==null)
            {
                //加载指定名称的“UI窗体”
                baseUIResult = LoadUIForm(uiFormsName);
            }

            return baseUIResult;
        }

        /// <summary>
        /// 加载指定名称的“UI窗体”
        /// 功能:
        ///    1:根据“UI窗体名称”,加载预设克隆体。
        ///    2:根据不同预设克隆体中带的脚本中不同的“位置信息”,加载到“根窗体”下不同的节点。
        ///    3:隐藏刚创建的UI克隆体。
        ///    4:把克隆体,加入到“所有UI窗体”(缓存)集合中。
        /// 
        /// </summary>
        /// <param name="uiFormName">UI窗体名称</param>
        private BaseUIForm LoadUIForm(string uiFormName)
        {
            string strUIFormPaths = null;                   //UI窗体路径
            GameObject goCloneUIPrefabs = null;             //创建的UI克隆体预设
            BaseUIForm baseUiForm=null;                     //窗体基类


            //根据UI窗体名称,得到对应的加载路径
            _DicFormsPaths.TryGetValue(uiFormName, out strUIFormPaths);
            //根据“UI窗体名称”,加载“预设克隆体”
            if (!string.IsNullOrEmpty(strUIFormPaths))
            {
                goCloneUIPrefabs = ResourcesMgr.GetInstance().LoadAsset(strUIFormPaths, false);
            }
            //设置“UI克隆体”的父节点(根据克隆体中带的脚本中不同的“位置信息”)
            if (_TraCanvasTransfrom != null && goCloneUIPrefabs != null)
            {
                baseUiForm = goCloneUIPrefabs.GetComponent<BaseUIForm>();
                if (baseUiForm == null)
                {
                    Debug.Log("baseUiForm==null! ,请先确认窗体预设对象上是否加载了baseUIForm的子类脚本! 参数 uiFormName=" + uiFormName);
                    return null;
                }
                switch (baseUiForm.CurrentUIType.UIForms_Type)
                {
                    case UIFormType.Normal: //普通窗体节点
                        goCloneUIPrefabs.transform.SetParent(_TraNormal, false);
                        break;
                    case UIFormType.Fixed: //固定窗体节点
                        goCloneUIPrefabs.transform.SetParent(_TraFixed, false);
                        break;
                    case UIFormType.PopUp: //弹出窗体节点
                        goCloneUIPrefabs.transform.SetParent(_TraPopUp, false);
                        break;
                    default:
                        break;
                }

                //设置隐藏
                goCloneUIPrefabs.SetActive(false);
                //把克隆体,加入到“所有UI窗体”(缓存)集合中。
                _DicALLUIForms.Add(uiFormName, baseUiForm);
                return baseUiForm;
            }
            else
            {
                Debug.Log("_TraCanvasTransfrom==null Or goCloneUIPrefabs==null!! ,Plese Check!, 参数uiFormName="+uiFormName); 
            }

            Debug.Log("出现不可以预估的错误,请检查,参数 uiFormName="+uiFormName);
            return null;
        }//Mehtod_end

        /// <summary>
        /// 把当前窗体加载到“当前窗体”集合中
        /// </summary>
        /// <param name="uiFormName">窗体预设的名称</param>
        private void LoadUIToCurrentCache(string uiFormName)
        {
            BaseUIForm baseUiForm;                          //UI窗体基类
            BaseUIForm baseUIFormFromAllCache;              //从“所有窗体集合”中得到的窗体

            //如果“正在显示”的集合中,存在整个UI窗体,则直接返回
            _DicCurrentShowUIForms.TryGetValue(uiFormName, out baseUiForm);
            if (baseUiForm != null) return;
            //把当前窗体,加载到“正在显示”集合中
            _DicALLUIForms.TryGetValue(uiFormName, out baseUIFormFromAllCache);
            if (baseUIFormFromAllCache!=null)
            {
                _DicCurrentShowUIForms.Add(uiFormName, baseUIFormFromAllCache);
                baseUIFormFromAllCache.Display();           //显示当前窗体
            }
        }

        #endregion

    }//class_end



UI管理器脚本解释如下:

一:上述代码中重要字段的解释如下:
       1:  “_DicFormsPaths” 表示“UI窗体预设路径”集合,负责缓存所有UI窗体预设的名称与对应资源路径的关系。
  2: “ _DicALLUIForms” 表示“所有UI窗体”集合,负责缓存已经加载过的所有UI窗体名称以及与之对应的UI窗体。
  3: “_DicCurrentShowUIForms”表示“当前正在显示”集合,负责控制正在显示UI窗体的内部逻辑。
  4: UI管理器脚本中的“_TraCanvasTransfrom”、“_TraNormal”、“_TraFixed”、“_TraPopUp”、“_TraUIScripts”,分别表示Unity层级视图中的根结点、普通节点、固定节点、弹出节点、管理脚本节点,这些节点是加载UI窗体的不同类型的父节点,用于各种UI窗体的管理工作。

二:上述代码中重要方法的解释如下:

  1: ShowUIForms()  是外部程序调用本框架的对外公共方法,负责加载、缓存、打开与显示制定窗体名称的UI窗体预设。
  2: LoadFormsToAllUIFormsCatch() 是根据UI窗体的名称,加载到“所有UI窗体”缓存集合中。
  3: LoadUIToCurrentCache() 是把当前窗体加载到“当前窗体”集合中。

  上述(UI框架)脚本编写完毕,测试成功后效果如下图:


登陆界面

登陆界面



为广大读者进一步了解与熟悉本框架,特提供(部分)下载链接:http://pan.baidu.com/s/1skN2Njn 密码:2ey6  

教学视频链接:  http://edu.manew.com/course/228


本篇就先写到这,下篇 "游戏UI框架设计(3)_窗体的层级管理" 继续。







评分

参与人数 3鲜花 +6 收起 理由
袁晓保 + 2
sevinlin + 2 赞一个!
骨悚破 + 2 很给力!

查看全部评分


回复

使用道具 举报

排名
21518
昨日变化
17

0

主题

19

帖子

49

积分

Rank: 1

UID
90866
好友
0
蛮牛币
19
威望
0
注册时间
2015-4-10
在线时间
19 小时
最后登录
2017-6-16
发表于 2017-4-13 00:31:16 | 显示全部楼层
反反复复反反复复反反复复

回复 支持 反对

使用道具 举报

7日久生情
1783/5000
排名
3259
昨日变化
13

0

主题

1228

帖子

1783

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
94526
好友
0
蛮牛币
1585
威望
0
注册时间
2015-4-22
在线时间
231 小时
最后登录
2017-6-23
发表于 2017-4-13 08:11:29 | 显示全部楼层
感谢楼主分享

回复

使用道具 举报

7日久生情
1603/5000
排名
339
昨日变化
1

0

主题

37

帖子

1603

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
9580
好友
1
蛮牛币
4398
威望
0
注册时间
2013-12-4
在线时间
240 小时
最后登录
2017-6-23
发表于 2017-4-13 08:53:02 | 显示全部楼层
酷~學習了,很棒的分享,謝謝

回复 支持 反对

使用道具 举报

6蛮牛粉丝
1246/1500
排名
4648
昨日变化
5

0

主题

827

帖子

1246

积分

Rank: 6Rank: 6Rank: 6

UID
189581
好友
0
蛮牛币
1664
威望
0
注册时间
2016-12-5
在线时间
205 小时
最后登录
2017-6-14
发表于 2017-4-13 08:58:42 | 显示全部楼层
不错给力啊啊啊啊
[发帖际遇]: cs562981849 在论坛发帖时没有注意,被小偷偷去了 2 蛮牛币. 幸运榜 / 衰神榜

回复 支持 反对

使用道具 举报

5熟悉之中
569/1000
排名
6034
昨日变化
54

0

主题

306

帖子

569

积分

Rank: 5Rank: 5

UID
156480
好友
0
蛮牛币
885
威望
0
注册时间
2016-7-12
在线时间
111 小时
最后登录
2017-6-22
发表于 2017-4-13 09:40:33 | 显示全部楼层
6666666666666666

回复 支持 反对

使用道具 举报

3偶尔光临
198/300
排名
10960
昨日变化
154

2

主题

87

帖子

198

积分

Rank: 3Rank: 3Rank: 3

UID
207446
好友
0
蛮牛币
474
威望
0
注册时间
2017-2-20
在线时间
53 小时
最后登录
2017-6-22
发表于 2017-4-13 09:46:01 | 显示全部楼层

回复

使用道具 举报

3偶尔光临
230/300
排名
7998
昨日变化
6

1

主题

46

帖子

230

积分

Rank: 3Rank: 3Rank: 3

UID
126987
好友
1
蛮牛币
382
威望
0
注册时间
2015-10-27
在线时间
85 小时
最后登录
2017-6-19
发表于 2017-4-13 10:28:29 | 显示全部楼层
多谢老师

回复

使用道具 举报

3偶尔光临
180/300
排名
9205
昨日变化
7

3

主题

57

帖子

180

积分

Rank: 3Rank: 3Rank: 3

UID
194927
好友
0
蛮牛币
246
威望
0
注册时间
2016-12-21
在线时间
40 小时
最后登录
2017-5-19
发表于 2017-4-13 10:33:18 | 显示全部楼层
niubiadfads
[发帖际遇]: jojo19940911 在网吧通宵,花了 1 蛮牛币. 幸运榜 / 衰神榜

回复

使用道具 举报

3偶尔光临
258/300

1

主题

81

帖子

258

积分

Rank: 3Rank: 3Rank: 3

UID
50385
好友
1
蛮牛币
111
威望
0
注册时间
2014-10-22
在线时间
176 小时
最后登录
2017-6-16
发表于 2017-4-13 10:46:08 | 显示全部楼层
学习一下,谢谢分享

回复 支持 反对

使用道具 举报

4四处流浪
418/500
排名
6608
昨日变化
47

6

主题

178

帖子

418

积分

Rank: 4

UID
104368
好友
0
蛮牛币
285
威望
0
注册时间
2015-5-30
在线时间
96 小时
最后登录
2017-6-23
发表于 2017-4-13 10:48:50 | 显示全部楼层
这都是什么鬼?

回复

使用道具 举报

3偶尔光临
242/300
排名
6457
昨日变化
12

0

主题

32

帖子

242

积分

Rank: 3Rank: 3Rank: 3

UID
108282
好友
0
蛮牛币
347
威望
0
注册时间
2016-9-23
在线时间
72 小时
最后登录
2017-6-20
发表于 2017-4-13 14:05:33 | 显示全部楼层
框架是好东西
[发帖际遇]: 一个袋子砸在了 骨悚破 头上,骨悚破 赚了 1 蛮牛币. 幸运榜 / 衰神榜

回复

使用道具 举报

7日久生情
1547/5000
排名
852
昨日变化

0

主题

149

帖子

1547

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
122176
好友
9
蛮牛币
2361
威望
0
注册时间
2015-9-11
在线时间
502 小时
最后登录
2017-6-23
发表于 2017-4-13 14:51:46 | 显示全部楼层
一万个人一万个框架,unity应该集成一套简单通用易用灵活得框架,这样大大提高开发效率(大公司都有自己得套路)

回复 支持 反对

使用道具 举报

7日久生情
1744/5000
排名
733
昨日变化
1

1

主题

473

帖子

1744

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
111260
好友
0
蛮牛币
6523
威望
0
注册时间
2015-6-30
在线时间
294 小时
最后登录
2017-6-23
发表于 2017-4-13 15:32:29 | 显示全部楼层

感谢楼主分享

回复

使用道具 举报

4四处流浪
317/500
排名
5163
昨日变化
40

0

主题

45

帖子

317

积分

Rank: 4

UID
190644
好友
0
蛮牛币
568
威望
0
注册时间
2016-12-8
在线时间
82 小时
最后登录
2017-6-23
发表于 2017-4-13 21:15:29 | 显示全部楼层
感谢分享

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

快速回复 返回顶部 返回列表