背景

曾几何时,在Winform中,使用MessageBox对话框是如此happy,后来还有人封装了可以选择各种图标和带隐藏详情的MessageBox,现在Unity3d UGui就没有了这样的好事情了,所有的UI都需要自己来搞定了,幸好还有各种插件,Inventory Pro中的对话框方案不失一种通用,可复用的方案。

YY(自己的想法)

所谓通用对话框,如果是自己实现的话有以下几点需要解决,窗体显示控制,窗体UI布局,窗体文字显示,窗体事件回调,窗体显示动画控制,窗体显示声音控制,窗体与其他窗体的关系,功能虽然小涉及的方面和知识却不少,自己做真的很不容易,所以别再自己造轮子了。

插件实现的效果

简单的确认对话框提示

当扔物品的时候会提示是否确认对话框。

稍微复杂一些的购买物品对话框

当购买物品时会显示出一个购买的物品,物品数量金额的对话框

简单确认对话框的使用

1、使用UGUI来设计一个自己使用的对话框,基本几个元素Title,description ,two buttons;

2、给对话框绑定Draggable Window(Script)使其具有拖拽功能

3、添加Animator,定义对话框显示的时候具有动画效果

4、添加UI Windows(script)使其具有打开关闭,声音,动画的效果

5、Confirmation Dialog(script)使其具有事件回调,model对话框的属性,文字绑定等对话框固有的属性

至此简单的对话框就做好了,这里我们充分见识了绑定技术、组件技术、UI解耦和框架的强大威力

复杂对话框的使用

这里只要知道Item Int Val Dialog(scirpt)其实是ConfirmDialog类的一个子类,剩下的东西就很自然了,这里不详细展开了。

分析

功能需求确定了,如何实现这些功能可能就需要用到一些模式,以及一些经验了,先看一下类图

根据前一节的脑图,类图我们逐个分析,InventoryUIDialogBase 是一个抽象类,也是与UI进行绑定的主体,其没有一个无用的属性,这里重点关注几个字段和属性,UIWindow类是通用的窗口显示和动画控制组件,InventoryMessage是字符串Message的封装类。

1)窗体UI布局

UI布局是通过Unity3d UGUI拖拽的方式设计上去的,这个很简单,首先做到了UI分离

2)窗体文字显示

窗体文字的显示首先是通过后台与UI做的绑定,这里使用Unity3d的组件设计时绑定技术(这里做过WPF的同学有是否有印象MVVM中的绑定),这里关键是文字信息,实际发现其实Dialog类并不关心显示的什么string,而是Inventory Pro提供的(类图中的Message类)一层封装后得到的结果,这里为什么要单独拿出来实际是为了做国际化以及一些文字性的扩展,比如颜色,字体显示的方案。

InventoryLangDataBase类对于所有的消息体文字进行了集中处理,而且本身也是Asset,这里有两种好处一种就是可以集中管理,一种就是为国际化文字。

因为Unity3d UGUI可以做文字颜色和字体的格式化操作,这里完全可以扩展添加有颜色和字体大小的文字重载

3)窗体显示控制,窗体显示动画控制,窗体显示声音控制

窗体显示的控制,完全利用Unity3d平台的组件化功能,通过UIWindow专门拿出来控制,这里看到UIWinow类是必须加载Animator动画类的

窗体的动画控制,由主体DialogBase进行设计时的动画效果绑定,由UIWindow类在控制显示和关闭时进行动画的Play,这里还用到了协程

窗体显示声音控制,由全局类静态方法 InventoryUIUtility.AudioPlayOneShot 来播放即可

3)窗体与其他窗体的关系

这个功能类似于网页中的遮罩或者winform里的模态(ModelDialog)对话框,这里没有现成的东西可以使用只能自己写了,这里如何关闭UGUI的事件处理主要是通过CanvasGroup这个插件来控制

4) 窗体事件回调

窗体中的事件回调交给了Dialog子类来处理,具体是在重载的ShowDialog方法中添加了委托的事件回调函数,然后通过代码绑定的方式(这里是onClick.AddListener,而不是UI手动可视化绑定)进行了按钮事件的绑定,这里有很大的灵活性。我比较喜欢这种通过代码定义显示委托的方式,来完成事件的回调(c++系可能叫做函数指针),同比匿名委托,泛型委托(Action或者Func),Lambda表达式,代码可读性更强

其它

这里留了一个小疑问,对话框的触发显示是如何实现的,我们的(MessageBox.Show)在哪里呢?

看过前面的文章的同学应该知道,Inevntory Pro有一个全局setting类,需要进行一些配置,其中就需要窗体元素与SettingManger脚本进行绑定,而SettingManger是一个单列全局类

最后是如何显示对话框的代码了,看到ShowDialog方法了吗,两个按钮的事件回调函数 Lambda表达式特别显眼

写在最后

分析总结完毕后有一些想法

1、好的框架使开发变得的easy,扩展很方便,通过以上的分析和例子看的出来很容易就能扩展出来一些简单的类似Confirm对话框,而且是对修改封闭,对新增开放的;

2、一个司空见惯的小功能,如果做好了完全可以覆盖到Unity3d的许多知识,剩下的只是不断进行这样的重复,重建你的神经网络即可,总有一天Unity3d的技术就这样印在你的大脑之中;

3、如果你真的看懂了本文,分析一下其实所有的UI系统都是相通的只是API和使用的技术不同而已,只是有些API封装的死,有些封装的松散一些。换句话说如果你自己在某种UI体系中完成一种自己的实现,换到另一个UI体系一样可以实现的;

4、微软体系如Winform过渡的封装是否是好事情?有些时候是好事情,有些时候就未必。根据手上的资源合理的选择技术才是根本;

5、关于使用轮子和造轮子的纠结,这也是一组矛盾,不造轮子就不能深刻的体会技术,造轮子需要大量的时间可造出来未必有已经造好的轮子设计的好,你会选择哪一种呢?

本文首发于蛮牛,次发于博客园,特此说明

蛮牛地址

核心代码

UIWindow

using System;
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
using System.Collections.Generic;namespace Devdog.InventorySystem
{/// <summary>/// Any window that you want to hide or show through key combination or a helper (UIShowWindow for example)/// </summary>[RequireComponent(typeof(Animator))][AddComponentMenu("InventorySystem/UI Helpers/UIWindow")]public partial class UIWindow : MonoBehaviour{public delegate void WindowShow();public delegate void WindowHide();#region Variables /// <summary>/// Should the window be hidden when the game starts?/// </summary>[Header("Behavior")]public bool hideOnStart = true;/// <summary>/// Keys to toggle this window/// </summary>public KeyCode[] keyCombination;/// <summary>/// The animation played when showing the window, if null the item will be shown without animation./// </summary>[Header("Audio & Visuals")]public AnimationClip showAnimation;/// <summary>/// The animation played when hiding the window, if null the item will be hidden without animation. /// </summary>public AnimationClip hideAnimation;public AudioClip showAudioClip;public AudioClip hideAudioClip;/// <summary>/// The animator in case the user wants to play an animation./// </summary>public Animator animator { get; set; }protected RectTransform rectTransform { get; set; }[NonSerialized]private bool _isVisible = false;/// <summary>/// Is the window visible or not? Used for toggling./// </summary>public bool isVisible{get{return _isVisible;}protected set{_isVisible = value;}}private IEnumerator showCoroutine;private IEnumerator hideCoroutine;/// <summary>/// All the pages of this window/// </summary>
        [HideInInspector]private List<UIWindowPage> pages = new List<UIWindowPage>();public UIWindowPage defaultPage{get;private set;}#endregion#region Events/// <summary>/// Event is fired when the window is hidden./// </summary>public event WindowHide OnHide;/// <summary>/// Event is fired when the window becomes visible./// </summary>public event WindowShow OnShow;#endregionpublic void AddPage(UIWindowPage page){pages.Add(page);if (page.isDefaultPage)defaultPage = page;}public void RemovePage(UIWindowPage page){pages.Remove(page);}public virtual void Awake(){animator = GetComponent<Animator>();if (animator == null)animator = gameObject.AddComponent<Animator>();rectTransform = GetComponent<RectTransform>();if (hideOnStart)HideFirst();else{isVisible = true;}}public virtual void Update(){if (keyCombination.Length == 0)return;bool allDown = true;foreach (var key in keyCombination){if (Input.GetKeyDown(key) == false){allDown = false;}}if (allDown)Toggle();}#region Usefull UI reflection functions /// <summary>/// One of our children pages has been shown/// </summary>public void NotifyPageShown(UIWindowPage page){foreach (var item in pages){if (item.isVisible && item != page)item.Hide();}}protected virtual void SetChildrenActive(bool active){foreach (Transform t in transform){t.gameObject.SetActive(active);}var img = gameObject.GetComponent<UnityEngine.UI.Image>();if(img != null)img.enabled = active;}public virtual void Toggle(){if (isVisible)Hide();elseShow();}public virtual void Show(){if (isVisible)return;isVisible = true;animator.enabled = true;SetChildrenActive(true);if (showAnimation != null){animator.Play(showAnimation.name);if (showCoroutine != null){StopCoroutine(showCoroutine);}showCoroutine = _Show(showAnimation); StartCoroutine(showCoroutine);}// Show pagesforeach (var page in pages){if (page.isDefaultPage)page.Show();else if (page.isVisible)page.Hide();}if (showAudioClip != null)InventoryUIUtility.AudioPlayOneShot(showAudioClip);if (OnShow != null)OnShow();}public virtual void HideFirst(){isVisible = false;animator.enabled = false;SetChildrenActive(false);rectTransform.anchoredPosition = Vector2.zero;}public virtual void Hide(){if (isVisible == false)return;isVisible = false;if (OnHide != null)OnHide();if (hideAudioClip != null)InventoryUIUtility.AudioPlayOneShot(hideAudioClip);if (hideAnimation != null){animator.enabled = true;animator.Play(hideAnimation.name);if (hideCoroutine != null){StopCoroutine(hideCoroutine);                    }hideCoroutine = _Hide(hideAnimation);StartCoroutine(hideCoroutine);}else{animator.enabled = false;SetChildrenActive(false);}}/// <summary>/// Hides object after animation is completed./// </summary>/// <param name="animation"></param>/// <returns></returns>protected virtual IEnumerator _Hide(AnimationClip animation){yield return new WaitForSeconds(animation.length + 0.1f);// Maybe it got visible in the time we played the animation?if (isVisible == false){SetChildrenActive(false);animator.enabled = false;}}/// <summary>/// Hides object after animation is completed./// </summary>/// <param name="animation"></param>/// <returns></returns>protected virtual IEnumerator _Show(AnimationClip animation){yield return new WaitForSeconds(animation.length + 0.1f);if (isVisible)animator.enabled = false;}#endregion}
}

InventoryUIDialogBase

using UnityEngine;
using System.Collections;
using Devdog.InventorySystem.Dialogs;
using UnityEngine.UI;namespace Devdog.InventorySystem.Dialogs
{public delegate void InventoryUIDialogCallback(InventoryUIDialogBase dialog);/// <summary>/// The abstract base class used to create all dialogs. If you want to create your own dialog, extend from this class./// </summary>[RequireComponent(typeof(Animator))][RequireComponent(typeof(UIWindow))]public abstract partial class InventoryUIDialogBase : MonoBehaviour{[Header("UI")]public Text titleText;public Text descriptionText;public UnityEngine.UI.Button yesButton;public UnityEngine.UI.Button noButton;/// <summary>/// The item that should be selected by default when the dialog opens./// </summary>[Header("Behavior")]public Selectable selectOnOpenDialog;/// <summary>/// Disables the items defined in InventorySettingsManager.disabledWhileDialogActive if set to true./// </summary>public bool disableElementsWhileActive = true;protected CanvasGroup canvasGroup { get; set; }protected Animator animator { get; set; }public UIWindow window { get; protected set; }public virtual void Awake(){canvasGroup = GetComponent<CanvasGroup>();if (canvasGroup == null)canvasGroup = gameObject.AddComponent<CanvasGroup>();animator = GetComponent<Animator>();window = GetComponent<UIWindow>();window.OnShow += () =>{SetEnabledWhileActive(false); // Disable other UI elementsif (selectOnOpenDialog != null)selectOnOpenDialog.Select();};window.OnHide += () =>{SetEnabledWhileActive(true); // Enable other UI elements
            };}public void Toggle(){window.Toggle();if(window.isVisible)SetEnabledWhileActive(false); // Disable other UI elementselseSetEnabledWhileActive(true); // Enable other UI elements
        }/// <summary>/// Disables elements of the UI when a dialog is active. Useful to block user actions while presented with a dialog./// </summary>/// <param name="enabled">Should the items be disabled?</param>protected virtual void SetEnabledWhileActive(bool enabled){if (disableElementsWhileActive == false)return;foreach (var item in InventorySettingsManager.instance.disabledWhileDialogActive){var group = item.gameObject.GetComponent<CanvasGroup>();if (group == null)group = item.gameObject.AddComponent<CanvasGroup>();group.blocksRaycasts = enabled;group.interactable = enabled;}}}
}

ConfirmationDialog

using UnityEngine;
using System.Collections;
using UnityEngine.UI;namespace Devdog.InventorySystem.Dialogs
{public partial class ConfirmationDialog : InventoryUIDialogBase{/// <summary>/// Show this dialog./// <b>Don't forget to call dialog.Hide(); when you want to hide it, this is not done auto. just in case you want to animate it instead of hide it.</b>/// </summary>/// <param name="title">Title of the dialog.</param>/// <param name="description">The description of the dialog.</param>/// <param name="yes">The name of the yes button.</param>/// <param name="no">The name of the no button.</param>/// <param name="yesCallback"></param>/// <param name="noCallback"></param>public virtual void ShowDialog(string title, string description, string yes, string no, InventoryUIDialogCallback yesCallback, InventoryUIDialogCallback noCallback){SetEnabledWhileActive(false);window.Show(); // Have to show it first, otherwise we can't use the elements, as they're disabled.
titleText.text = title;descriptionText.text = description;yesButton.GetComponentInChildren<Text>().text = yes;noButton.GetComponentInChildren<Text>().text = no;yesButton.onClick.RemoveAllListeners();yesButton.onClick.AddListener(() =>{if (window.isVisible == false)return;SetEnabledWhileActive(true);yesCallback(this);window.Hide();});noButton.onClick.RemoveAllListeners();noButton.onClick.AddListener(() =>{if (window.isVisible == false)return;SetEnabledWhileActive(true);noCallback(this);window.Hide();});}/// <summary>/// Show the dialog./// <b>Don't forget to call dialog.Hide(); when you want to hide it, this is not done auto. just in case you want to animate it instead of hide it.</b>/// </summary>/// <param name="title">The title of the dialog. Note that {0} is the item ID and {1} is the item name.</param>/// <param name="description">The description of the dialog. Note that {0} is the item ID and {1} is the item name.</param>/// <param name="yes">The name of the yes button.</param>/// <param name="no">The name of the no button.</param>/// <param name="item">/// You can add an item, if you're confirming something for that item. This allows you to use {0} for the title and {1} for the description inside the title and description variables of the dialog./// An example:/// /// ShowDialog("Are you sure you want to drop {0}?", "{0} sure seems valuable..", ...etc..);/// This will show the item name at location {0} and the description at location {1}./// </param>/// <param name="yesCallback"></param>/// <param name="noCallback"></param>public virtual void ShowDialog(string title, string description, string yes, string no, InventoryItemBase item, InventoryUIDialogCallback yesCallback, InventoryUIDialogCallback noCallback){ShowDialog(string.Format(string.Format(title, item.name, item.description)), string.Format(description, item.name, item.description), yes, no, yesCallback, noCallback);}}
}

转载于:https://www.cnblogs.com/IlidanStormRage/p/5864983.html

Unity3d UGUI 通用Confirm确认对话框实现(Inventory Pro学习总结)相关推荐

  1. confirm确认对话框 弹出确定 提交 取消

    confirm 消息对话框通常用于允许用户做选择的动作,如:"你对吗?"等.弹出对话框(包括一个确定按钮和一个取消按钮). 语法: confirm(str); 参数说明: str: ...

  2. 【Unity3D插件】Inventory Pro插件分享《游戏背包系统》

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 QQ群:1040082875 一.前言 发现就喜欢研究这些插件,为什么呢,因为方便快捷啊.基本不用研究源代 ...

  3. java confirm_confirm确认对话框的实现方法总结

    还记得之前的javascript入门里的讲的confirm 消息对话框吗?不记得也没关系,我们先来回顾一下,然后在详细讲它. 复习: confirm 消息对话框通常用于允许用户做选择的动作,如:&qu ...

  4. javascript window.confirm确认 取消对话框实现代码小结

    本文章讲述的三种都是基于了javascript confirm提示确认框的做法了,只是在不同的地方写哦,有需要的同学可参考一下 confirm() 方法 confirm() 方法用于显示一个带有指定消 ...

  5. 通用窗口类 Inventory Pro 2.1.2 Demo1(下)

    本篇想总结的是Inventory Pro中通用窗口的具体实现,但还是要强调下该插件的重点还是装备系统而不是通用窗口系统,所以这里提到的通用窗口类其实是通用装备窗口类(其实该插件中也有非装备窗口比如No ...

  6. 如何在离开页面时弹出确认对话框

    离开页面确认主要是利用了onbeforeunload事件,当该事件声明为: Code <body onbeforeunload="return pageBeforeunload(eve ...

  7. 为gridview“删除”列添加确认对话框

    如何为gridview控件里的"删除"列添加一个确认对话框?网上众说纷纭,大致见到了三种解决方案,现归纳如下: 1.继承Web.IO里的button控件,为其实现一个IPostba ...

  8. bootstrap的验证和确认对话框

    BootstrapValidator: http://bv.doc.javake.cn/api/ 引用 <!-- jquery-confirm.确认对话框 --> <link hre ...

  9. 为 GridView、DetailsView、FormView 等数据显示控件删除功能添加“确认对话框”

    在模板上操作 Button .LinkButton 或 ImageButton 时,通过设置它们的 OnClientClick 属性,令其返回 JavaScript confirm(string) 函 ...

最新文章

  1. Vue 2.5中将迎来有关TypeScript的改进!
  2. eclipse扩展点_Eclipse扩展点评估变得容易
  3. 清华团队CrossWOZ: 这篇顶会,助你徒手搭建任务导向对话系统
  4. 利用函数wavread对语音信号进行采样_统计与自适应信号处理知识点总结-期末考试...
  5. python下载谷歌地图瓦片_python获取bing地图发布自己的TMS服务(一)下载瓦片
  6. 怎样用52单片机做计算机,利用串口小助手实现AT89S52单片机简易计算器完整程序(Keil)...
  7. 柯美服务器处理文件慢,处理打印机在打印文件时打印速度过慢的原因 看完你就知道了...
  8. 用Python实现一个商场管理系统(附源码)
  9. 51单片机实战教程基础硬件篇(一 电子元件PCB 封装库设计教程)
  10. 辰智:2018中国餐饮大数据白皮书
  11. MySQL江湖路 | 专栏目录
  12. 软件随想录:程序员部落酋长Joel谈软件(阮一峰译)-3
  13. 按键精灵X学习笔记(二):键盘命令
  14. 金融数据分析(十二)房贷综合问题
  15. Qt编写密钥生成器+使用demo(开源)
  16. 半同步半异步模式 -------一个架构模式,清晰的结构,高效并发的I/O
  17. [Swift]LeetCode855. 考场就座 | Exam Room
  18. linux 下查看机器配置命令
  19. 计算两组2D bbox的IOU
  20. win10计算机管理器在哪,Windows10开启服务管理器图文教程|Win10系统服务管理器在哪...

热门文章

  1. 通过命令行启动uwp应用
  2. C语言基础教程篇之作用域规则
  3. 小米6.0以上系统怎么无需root激活Xposed框架的步骤
  4. 18.1 集群介绍 18.2 keepalived介绍 18.3/18.4/18.5 用keepalived配置高可用集群
  5. 杭电算法题 HDU 1000-1004
  6. 集群中运行Tachyon(译)
  7. Linux上FTP服务的相关配置2:搭建FTPs及虚拟账号
  8. linux下转移mysql目录
  9. 程序员面试金典——18.1另类加法
  10. 卷积神经网络发展简史