Unity - A计划(永久有效期) 扫二维码继续学习 二维码时效为半小时

(197评价)
价格: 4431.00元
关于UI框架的改进思路以及一个问题。
sdhexu发起了话题2017-05-30
5
回复
453
浏览

看了siki老师的UI框架,受益匪浅,非常感谢siki老师。。

在此提出几点改进建议,请老师和同学们批评指正:

1、教程中使用json去加载所有UI面板的信息,事实上我认为是没有必要的。因为所有的面板肯定具有一个窗口基类(视频教程中是“BasePanel"),因此,只要这么做就可以了:

    public void LoadUI()
    {
        _uiResources = new Dictionary<string, HXUIWindowBase>();

        HXUIWindowBase[] objs = Resources.FindObjectsOfTypeAll<HXUIWindowBase>();
        foreach( HXUIWindowBase obj in objs)
        {
            _uiResources.Add(obj.ID, obj);
        }
    }

2、使用栈来管理所有的面板,的确具很方便,但是有一个坏处,就是同一时刻只能有一个面板(位于栈顶的那个)处于可交互状态,其他的都不可以交互。但实际开发中,各种面板不能一概而论。比如:背包面板应该可以跟商人出售东西的面板同时可以交互,人物穿装备的面板应该可以和背包、商人面板等同时交互.....但,还有一些面板必须是模态的,就是说一旦弹出所有其他面板都不能再交互,比如一个MessageBox确定提示:”该物品是贵重装备,您确定要出售该物品吗?“、”您确定要放弃这个任务吗“等等此类的提示。因此,用栈来管理就不太方便不太好了。

我的改进方案是:

首先。将面板分为三类:

1、一般窗口(可以与其他窗口共存);

2、模态窗口,只能独占交互。

3、Tips窗口,仅仅是弹出物品信息等,不需要交互。

这样,增加一个窗口类型的枚举,给每个面板分配一个类型。

public enum HXUIType
{
    NormalWindow,
    ModalWindow,
    TipsWindow
}

然后,仍然使用字典而不是栈来存储窗口信息。当一个面板被激活时(调用PushWindow),先判断这个窗口是什么类型的窗口,如果是Normal或者tips,不需要做额外操作,如果是modalWindow,则首先禁用字典中所有的其他窗口;然后再显示该窗口。显示的步骤是:先检查字典中是否存在,如果不存在,就实例化,如果已经存在,直接修改它的z值(或者深度之类的),使它位于顶层。

以上是我的建议,欢迎老师和同学们品评斧正。

另外,有一个问题还是百思不得其解,请siki老师和同学们指教:

就是,当在某处时,可能需要弹出一个模态信息框,让玩家选择”是“或者”否“,比如,当玩家点了放弃任务的按钮时,我弹出一个对话框问他是否确定。然后我怎么才能知道他点了是还是否呢??

比如:

public SomeWindow : public BasePanel {

     // Other code .........

     public void OnCancelQuest() // 放弃任务按钮的处理
     {

         // Pop a Message Box With Information "Are you soure ? "

            PanelManager.Instance.Push( PanelType.areYouSoureWindow );


         if( // 这里我怎么知道用户点了 确定 还是 取消 ??? )
         {
             // 用户点了确定。确实要放弃任务了。
         }
         else {
             // 只是虚晃一下而已。。并没有真放弃。。
         }

         // 或者,如何实现下面的功能:
         PanelManager.Hinstance.Push( PanelType.areYouSoure ).OnClickOK( CancelTask());
         
     }
}

 

所有回复
  • sdhexu 2017-05-30

    贴出改进后的完整代码:

    // 基础信息类及管理类
    using System;
    using System.Collections.Generic;
    using UnityEngine;
    
    public enum HXUIType
    {
        NormalWindow,
        ModalWindow,
        TipsWindow
    }
    
    public class HXUIManager
    {
        private static HXUIManager _Instance = null;
        private static Dictionary<string, HXUIWindowBase> _uiResources = null;
        private static Dictionary<string, HXUIWindowBase> _uiInstance = null;
        private static Dictionary<string, HXUIWindowBase> _uiShowing;
    
        /// <summary>
        /// 只读唯一实例
        /// </summary>
        public static HXUIManager hInstance
        {
            get
            {
                if (_Instance == null)
                    _Instance = new HXUIManager();
                return _Instance;
            }
        }
    
        private HXUIManager()
        {
            _uiInstance = new Dictionary<string, HXUIWindowBase>();
            _uiShowing = new Dictionary<string, HXUIWindowBase>();
        }
    
        /// <summary>
        /// 开启窗口并入栈
        /// </summary>
        /// <param name="win">指定要开启的窗口</param>
        public void ShowWindow( HXUIWindowBase win )
        {
            if (win != null)
            {
                bool bNeedProcess = true;
                if(( win.Type == HXUIType.ModalWindow ) && ( _uiShowing.Count > 0 ))
                {
                    foreach( var wb in _uiShowing )
                    {
                        if (wb.Key == win.ID)
                        {
                            win.OnShowWindow();
                            bNeedProcess = false;
                        }
                        else
                            wb.Value.OnPauseWindow();
                    }
                }
                if( bNeedProcess )
                {
                    if (_uiShowing.TryGet(win.ID) == null)
                        _uiShowing.Add(win.ID, win);
    
                    win.OnShowWindow();
                }
            }
        }
    
        /// <summary>
        /// 开启窗口并入栈
        /// </summary>
        /// <param name="id">要开启窗口的ID</param>
        public void ShowWindow( string id)
        {
            ShowWindow(GetWindow(id));
        }
    
    
        public void HideWindow(HXUIWindowBase win )
        {
            if( win  != null )
            {
                if (_uiShowing.TryGet(win.ID) != null)
                {
                    _uiShowing.Remove(win.ID);
                    win.OnHideWindow();
                    if( win.Type == HXUIType.ModalWindow )
                    {
                        int i = win.transform.GetSiblingIndex();
                        while (i > 0)
                        {
                            --i;
                            HXUIWindowBase brother = win.transform.parent.GetChild(i).GetComponent<HXUIWindowBase>();
                            if (brother.bIsShow )
                            {
                                brother.OnResume();
                                if (brother.Type == HXUIType.ModalWindow)
                                    break;
                            }
                        }
                    }
                }
            }
        }
    
        public void HideWindow( string id  )
        {
            HideWindow(_uiShowing.TryGet(id));
        }
    
        public HXUIWindowBase GetWindow( string id)
        {
            HXUIWindowBase wb = _uiInstance.TryGet(id);
            if( wb == null )
            {
                wb = _uiResources.TryGet(id);
                if (wb != null)
                {
                    GameObject ob = GameObject.Instantiate(wb.gameObject, HXUIRoot.RootCanvas, false);
                    wb = ob.GetComponent<HXUIWindowBase>();
                    _uiInstance.Add(id, wb);
                }
            }
            return wb;
        }
    
        public void LoadUI()
        {
            _uiResources = new Dictionary<string, HXUIWindowBase>();
    
            HXUIWindowBase[] objs = Resources.FindObjectsOfTypeAll<HXUIWindowBase>();
            foreach( HXUIWindowBase obj in objs)
            {
                _uiResources.Add(obj.ID, obj);
            }
        }
    }
    
    // 窗口面板基类
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class HXUIWindowBase : MonoBehaviour {
    
        [SerializeField] protected string m_id;
        [SerializeField] protected HXUIType m_type;
    
        protected CanvasGroup m_canvasGroup = null;
    
        public HXUIType Type
        {
            get
            {
                return m_type;
            }
        }
    
        public string ID
        {
            get
            {
                return m_id;
            }
        }
    
        public bool bIsShow
        {
            get
            {
                return gameObject.activeInHierarchy;
            }
        }
    
        public bool CanInteractive
        {
            get
            {
                return m_canvasGroup.blocksRaycasts;
            }
        }
    
        protected void Awake()
        {
            m_canvasGroup = GetComponent<CanvasGroup>();
            if( m_canvasGroup == null)
            {
                Debug.LogError("Window Must Have A CanvasGroup Component");
            }
        }
    
        public void HideWindow()
        {
            HXUIManager.hInstance.HideWindow(this.ID);
        }
    
        public void OnShowWindow()
        {
            if ( ! bIsShow )
                gameObject.SetActive(true);
            m_canvasGroup.blocksRaycasts = true;
            
            // 将窗口移动到最顶端
            transform.SetSiblingIndex(transform.parent.childCount);
        }
    
        public void OnPauseWindow()
        {
            m_canvasGroup.blocksRaycasts = false;
        }
    
        public void OnResume()
        {
            m_canvasGroup.blocksRaycasts = true;
        }
    
        public void OnHideWindow()
        {
            if ( bIsShow )
            {
                gameObject.SetActive(false);
            }
        }
    }
    

     

    还有-5条回复,点击查看
    你还没有登录,请先登录注册
  • siki 2017-05-30

    非常好 棒

    还有-5条回复,点击查看
    你还没有登录,请先登录注册
  • siki 2017-05-30
    PanelManager.Instance.Push( PanelType.areYouSoureWindow );

    在弹出 选择是和否的框的时候,注册一下是和否的点击事件,把是和否的处理代码放在是和否的点击处理方法里面

    • sdhexu 2017-05-31

      首先感谢siki老师在百忙之中的答复。

      把用户选择是和否的代码放在激发模态对话框的代码文件中处理。如果是用手工指定,那模态对话框就不能通用了。经过了我无数次的尝试。终于实现这个功能,共享出来,跟大家一起研究,或许还有更好的方法,如果谁有更好的办法,麻烦不吝赐教,一起进步。

      首先,写一个类,集成自 BasePanel,我这里是HXUIWindowBase...

      写到这里发现,居然这里的回复不能帖代码,没有帖代码的那个工具,我就发到楼下吧。。

      (0) 回复
    还有-4条回复,点击查看
    你还没有登录,请先登录注册
  • sdhexu 2017-05-31
    public class HXUIConfirmBox : HXUIWindowBase
    {
        [SerializeField]
        protected Text m_text;  // 确认对话框上的文字。
        [SerializeField]
        protected Button m_okButton;   // 确认按钮
        [SerializeField]
        protected Button m_cancelButton;  // 取消按钮
    
        protected Text m_btOKText;      //确认按钮上的文字
        protected Text m_btCancelText;  // 取消按钮上的文字
    
        protected  override void Awake()
        {
            base.Awake();
    
            // 下面是一系列的获取,组建内的子物体。便于显示信息。
            // 子物体包括一个文本,两个按钮,用户可以手工指定,也可以保持默认,系统会自动检索。
    
            if (m_text == null)
            {
                // 如果用户没有指定哪个组建代表主文字,那么就默认孩子中的第一个Text组件。
                m_text = transform.Find("Text").GetComponent<Text>();
            }
    
            if (m_okButton == null)
            {
                m_okButton = transform.Find("OK").GetComponent<Button>();
            }
    
            if (m_cancelButton == null)
            {
                m_cancelButton = transform.Find("Cancel").GetComponent<Button>();
            }
    
            if (m_okButton != null)
            {
                m_btOKText = m_okButton.GetComponentInChildren<Text>();
            }
            if (m_cancelButton != null)
            {
                m_btCancelText = m_cancelButton.GetComponentInChildren<Text>();
            }
        }
    
        // 这个函数用来修改确认框的内容。
        // 参数意义依次是:确认框上要显示的文本信息、当用户点ok的时候的回调函数、当用户点取消按钮时的回调函数、OK按钮上的文本、Cancel按钮上的文本
        public void ConfirmBox( string text = null, UnityAction okCall = null, UnityAction cancelCall = null, string okText = null, string cancelText = null )
        {
            m_text.text = (text == null) ? "您确定吗?" : text;
            
            if((m_btOKText != null) && ( okText != null ))
                m_btOKText.text = okText;
            
            if(( m_btCancelText != null) && ( cancelText != null ))
                m_btCancelText.text = cancelText;
     
            // 清理OK按钮上的监听事件,如果不清理,上次调用时的回调这次还会去调用,所以必须清理。
            m_okButton.onClick.RemoveAllListeners();
    
            // 如果用户指定了OK按钮的回调,那么就监听他,否则给定一个关闭本对话框的默认功能。
            if (okCall != null)
                m_okButton.onClick.AddListener(okCall);
            else
                m_okButton.onClick.AddListener(new UnityAction(ButtonClickDefault));
            m_cancelButton.onClick.RemoveAllListeners();
    
            // 如果用户指定了OK按钮的回调,那么就监听他,否则给定一个关闭本对话框的默认功能。
            if (cancelCall != null)
                m_cancelButton.onClick.AddListener(cancelCall);
            else
                m_cancelButton.onClick.AddListener(new UnityAction(ButtonClickDefault));
        }
    
        // 这里是默认回调
        public void ButtonClickDefault()
        {
            HideWindow();
        }

    在BasePanel中添加一个函数如下:

        public void doConfirmBox( string id, string text = null, UnityAction okCall = null, UnityAction cancelCall = null, string okText = null, string cancelText = null )
        {
            // 获取模态确认框的实例,如果没有实例化,会自动实例化。
            HXUIConfirmBox wb = GetWindow(id) as HXUIConfirmBox;
    
            // 设置提示信息
            wb.ConfirmBox(text, okCall, cancelCall, okText, cancelText);
    
            // 显示确认框
            ShowWindow(wb);
        }

    然后,在任意地方,只要这么调用就行了:

        public void onClose()
        {
            // 如果用户点了关闭按钮,弹出是否关闭的提示。
            HXUIManager.hInstance.doConfirmBox("confirmBox", "确定要关闭吗?", new UnityEngine.Events.UnityAction(OnCloseClick));
        }
    
        public void OnCloseClick()
        {
            // 如果用户点了关闭按钮,并且在弹出的对话框中点击了确定按钮,那么这个函数将会被调用
            HXUIManager.hInstance.HideWindow("confirmBox");
            HideWindow();
        }

     

    还有-5条回复,点击查看
    你还没有登录,请先登录注册
发表回复
你还没有登录,请先 登录或 注册!