写在前面

这一篇我个人认为还是很常用的,一开始也是实习的时候学到的,所以我觉得实习真的是一个快速学习工程技巧的途径。
提醒:这篇教程比较复杂,如果你不熟悉NGUI、iTween、C#的回调函数机制,那么这篇文章可能对你比较有难度,当然你可以挑战自我。
言归正传,消息框,也就是Message Box,在Windows下很常见,如下图:
在游戏里,我们也会用到这样的消息框。例如用户按了返回按钮,一般都会弹出一个确认退出的按钮。用户在执行某些重要操作时,我们总是希望再一次确认以防用户的无意操作,以此来提高用户体验。这篇教程就会详细叙述Unity中消息框的一种实现。(由于见识有限,我相信肯定还有其他的方式,可能更简单易用,如有不足欢迎指出。)

准备工作

插件和工具

这篇教程还是有点复杂的,需要各个方面的力量帮助我们:
  • 既然是界面,那么就离不开NGUI。确保你的项目里有NGUI插件,以及必要的图集来制作消息框的背景(为了省事你可以直接只用NGUI例子中的Atlas)。如果你需要中文显示,那么还需要用NGUI制作一个中文字体。网络上有很多教程。
  • 单例脚本。这里需要单例模式主要是为了实现那种无需在面板中(指的是除消息框以外的其他对象)引用任何资源,这样你看起来就像是在VS2010下调用MessageBox.Show一样。
  • iTween插件。这个插件是免费的,而且非常小巧。使用它主要是为了美化消息框的弹出效果,例如放大弹出、从上向下弹出等等。本例使用的是从上向下弹出。
准备好了这些工具后,我们需要制作本地化文字,这是为了定义消息框中的按钮文字以及弹出时显示的标题和内容文字,当然你可以略去这一步,但是这是非常不建议的,因为在真正的项目中管理好所有的文本是很重要的。本地化是NGUI的功能,经常被用于转换多种语言,例如中文、英文等。你可以在NGUI的例子中找到对应的场景和教程(Example 10 - Localization),涉及到的脚本主要是Localization.cs(统一管理所有的可用语言)、UILocalize.cs(指明一个UILabel显示的文字)。这里假设你知道这些脚本是干嘛的。

本地化文本

要实现一个本地化文本非常容易。只需要两步:
  1. 创建一个纯文本文件,例如cn.txt,里面将定义所有用到的字符串。
    1. Confirm = 确定
    2. Cancel = 取消
    3.  
    4. QuitConfirmTitle = 退出确认
    5. QuitConfirmContent = 继续将退出游戏。\n确定退出?

    对于我们的弹出框,只用到上述四个文本。等号左边的名字相当于这个字符串的ID,等号右边是内容。

  2. 制作一个Localization Prefab。这点和NGUI例子很相似,就是为了方便以后修改。对于我们的教程,如果你不制作成一个Prefab也是可以的,但是还是不建议,还是那句话,这种思维还是很重要的。制作好的prefab如下:

    细心的你可能发现除了上述提到的脚本,还有一个脚本:DontDestroyOnLoad.cs。代码如下:
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class DontDestroyOnLoad : MonoBehaviour {
    5.  
    6. // Use this for initialization
    7. void Start () {
    8. DontDestroyOnLoad(this.gameObject);
    9. }
    10. }

    它的作用显而易见,就是为了不让我们的本地化文本在场景切换时被销毁。这样就不用每一个场景都实例化一个Localization Prefab,而只需要在游戏的第一个场景中包含一个Localization Prefab即可。

  3. 新建一个场景,并把之前的Localization Prefab拖进去。

实现

呼呼,下面的内容比较复杂,希望你能耐心看下去。

测试功能:检测退出按钮

我们首先写一个测试脚本,它的功能就是检测用户是否按下了退出按钮,否则就会尝试调用我们的消息框(当然这里还没有定义,我只是想从最高层向底层一层一层讲解)。
下面是KeyDetecter.cs:
  1. using UnityEngine;
  2. using System.Collections;
  3.  
  4. public class KeyDetecter : MonoBehaviour {
  5.  
  6. private CommonUIManager m_CommonUIManager = null;
  7.  
  8. void Start() {
  9. m_CommonUIManager = Singleton.getInstance("CommonUIManager") as CommonUIManager;
  10. }
  11.  
  12. // Update is called once per frame
  13. void Update () {
  14. if (Input.GetKey(KeyCode.Escape) && m_CommonUIManager != null) {
  15. m_CommonUIManager.ShowMessageBox(
  16. Localization.instance.Get("QuitConfirmTitle"),
  17. Localization.instance.Get("QuitConfirmContent"),
  18. MessageBox.Style.OKAndCancel,
  19. OnReceiveQuitConfirmResult);
  20. }
  21.  
  22. }
  23.  
  24. void OnReceiveQuitConfirmResult(MessageBox.Result result) {
  25. if (result == MessageBox.Result.OK) {
  26. Application.Quit();
  27. }
  28. }
  29. }
上述代码很短,最重要的部分是ShowMessageBox部分。由于我们还没有实现CommonUIManager,这里你可以理解ShowMessageBox就是弹出一个消息框,它的标题是Localization.instance.Get("QuitConfirmTitle")(根据cn.txt我们知道对应的文本是“退出确认”),内容是Localization.instance.Get("QuitConfirmContent")(根据cn.txt我们知道对应文本是“继续将退出游戏。\n确定退出?”),并且它的类型是包含OK和Cancel两个按钮的标准弹出框,用户点击后的回调函数是OnReceiveQuitConfirmResult。
OnReceiveQuitConfirmResult函数根据用户选择结果来判断是否真正退出游戏。

界面

最麻烦的部分来了。我们使用NGUI制作界面。用NGUI创建一个全新的UI,并重新命名。按照类似下图的组织方式创建其他界面元素:
从最上面说起。
CommonUIRoot,即之前的UI Root,的位置很重要,由于弹出框界面引入了一个新的Camera,因此为了防止它的视野范围和其他场景中已有的Camera重合,应尽量把它的位置调整到一个完全空白的位置。本例设置的位置是(0,2000,-2000)。
Camera、Anchor和Panel。确保Anchor的Side设置成Center。如果你想要自适应多种大小的屏幕,那么就需要调整Camera的Size,并向Panel添加UIStretch脚本,具体过程请Google。
Window和WindowRoot。这两个主要是为了设置弹出动画而设置的,它们原本都是空对象,而后添加了一些脚本或者动画。
Window设置如下,可以看到它添加了iTween的一个脚本,主要用于实现从上向下弹出的移动效果。注意,它的位置和iTween脚本中From和To参数的设置有很大关系。
这里的WindowRoot就是一个空对象,但是如果需要自定义的动画时就需要可以再通过一些技巧设置WindowRoot。这里就不讲了。
后面的元素用红色的文字和方框注释过了。其中需要解释的就是LockCollider。它的作用就是通过一个BoxCollider挡住后面所有可点击的UI,使得用户只能点击消息框上的按钮。所以它的Z坐标比其他元素更靠后,而且大小应该超过屏幕的大小。
最后,还需要设置三个消息框按钮,包括它们的文字和回调函数。
OKAndCancelButtonGroup下一共包含两个按钮。首先为CancelButton添加回调函数,这是通过把NGUI的UIButtonMessage脚本添加到CancelButton对象上实现的(即调用CommonUIRoot上脚本的OnCancel函数,当然这里我们还没有实现这个脚本):
我们希望取消按钮上的文字为“取消”,因此向CancelButton对象下面的Label添加如下脚本(通过cn.txt我们知道Cancel对应的文本是“取消”):
同理,设置另外两个按钮。OKAndCancelButtonGroup的ConfirmButton和OnlyOKButtonGroup的ConfirmButton设置相同,它们的回调函数名为OnConfirm,UILocalize脚本的Key设置为Confirm。
最后!!!真的是最后了。。。我们需要用脚本管理上面这些界面元素。建立一个新的脚本CommonUIDetail.cs:
  1. using UnityEngine;
  2. using System.Collections;
  3.  
  4. public class CommonUIDetail : MonoBehaviour {
  5.  
  6. public TweenPosition messageBoxTween;
  7. public UILabel messageBoxTitle;
  8. public UILabel messageBoxContent;
  9. public GameObject[] buttonGroups = new GameObject[(int)MessageBox.Style.eNumCount];
  10.  
  11. public System.Action messageBoxConfirmCallback = null;
  12. public System.Action messageBoxCancelCallback = null;
  13.  
  14. void OnConfirm() {
  15. if (messageBoxConfirmCallback != null) {
  16. messageBoxConfirmCallback();
  17. }
  18. }
  19.  
  20. void OnCancel() {
  21. if (messageBoxCancelCallback != null) {
  22. messageBoxCancelCallback();
  23. }
  24. }
  25. }

并把该脚本添加到CommonUIRoot上,并给面板上的各个变量赋值:

调整所有UI的位置,使它们看起来像一个弹出框。
完成后,把整个CommonUIRoot及其所有子对象制作成一个Prefab,并且放在Assets/Resources文件夹下,这样才能通过Resources.Load来动态加载它。

单例脚本

还记得之前测试脚本里未实现的CommonUIManager吗?现在我们就来实现最关键的ShowMessageBox代码。CommonUIManager.cs如下:
  1. using UnityEngine;
  2. using System.Collections;
  3.  
  4. public class MessageBox {
  5. public delegate void OnReceiveMessageBoxResult(MessageBox.Result result);
  6.  
  7. public enum Style {
  8. OnlyOK,
  9. OKAndCancel,
  10. eNumCount
  11. }
  12.  
  13. public enum Result {
  14. OK,
  15. Cancel,
  16. eNumCount
  17. }
  18. }
  19.  
  20. public class CommonUIManager : MonoBehaviour {
  21.  
  22. public GameObject commonUIPrefab = null;
  23.  
  24. public GameObject root;
  25. public TweenPosition messageBoxTween;
  26. public UILabel messageBoxTitle;
  27. public UILabel messageBoxContent;
  28. public GameObject[] buttonGroups = new GameObject[(int)MessageBox.Style.eNumCount];
  29.  
  30. private MessageBox.OnReceiveMessageBoxResult messageBoxCallback = null;
  31.  
  32. // Use this for initialization
  33. void Start () {
  34.  
  35. }
  36.  
  37. // Update is called once per frame
  38. void Update () {
  39.  
  40. }
  41.  
  42. public void ShowMessageBox(string title, string content, MessageBox.Style style,
  43. MessageBox.OnReceiveMessageBoxResult callback) {
  44. if (root == null) {
  45. commonUIPrefab = Resources.Load("CommonUIRoot") as GameObject;
  46. root = GameObject.Instantiate(commonUIPrefab) as GameObject;
  47. root.transform.parent = this.transform;
  48.  
  49. CommonUIDetail uiDetail = root.GetComponent<CommonUIDetail>();
  50. messageBoxTween = uiDetail.messageBoxTween;
  51. messageBoxTitle = uiDetail.messageBoxTitle;
  52. messageBoxContent = uiDetail.messageBoxContent;
  53. buttonGroups[(int)MessageBox.Style.OnlyOK] = uiDetail.buttonGroups[(int)MessageBox.Style.OnlyOK];
  54. buttonGroups[(int)MessageBox.Style.OKAndCancel] = uiDetail.buttonGroups[(int)MessageBox.Style.OKAndCancel];
  55.  
  56. uiDetail.messageBoxConfirmCallback = OnConfirm;
  57. uiDetail.messageBoxCancelCallback = OnCancel;
  58. }
  59.  
  60. messageBoxTitle.text = title;
  61. messageBoxContent.text = content;
  62. messageBoxCallback = callback;
  63.  
  64. switch ((int)style) {
  65. case (int)MessageBox.Style.OnlyOK:
  66. buttonGroups[(int)MessageBox.Style.OnlyOK].SetActive(true);
  67. buttonGroups[(int)MessageBox.Style.OKAndCancel].SetActive(false);
  68. break;
  69. case (int)MessageBox.Style.OKAndCancel:
  70. buttonGroups[(int)MessageBox.Style.OnlyOK].SetActive(false);
  71. buttonGroups[(int)MessageBox.Style.OKAndCancel].SetActive(true);
  72. break;
  73. }
  74.  
  75. messageBoxTween.Play(true);
  76. }
  77.  
  78. void OnConfirm() {
  79. if (messageBoxCallback != null) {
  80. messageBoxCallback(MessageBox.Result.OK);
  81. messageBoxCallback = null;
  82. }
  83.  
  84. messageBoxTween.Play(false);
  85. }
  86.  
  87. void OnCancel() {
  88. if (messageBoxCallback != null) {
  89. messageBoxCallback(MessageBox.Result.Cancel);
  90. messageBoxCallback = null;
  91. }
  92.  
  93. messageBoxTween.Play(false);
  94. }
  95. }
我只能帮你到这里了,这代码不长,相信如果你肯花时间一定可以看懂。
把之前的KeyDetector.cs代码添加到场景中的MainCamera上,运行,点击键盘上的ESC按键,应该就会弹出你的消息框了。
至此,整个过程结束。

效果

保存所有代码,此时不应该再有任何报错,如果有的话,自己找找原因吧……
下面是我制作的游戏中实现的弹出框:

结束语

啊哦,这是最近写的最复杂的一篇教程了,里面有些部分肯定说的不是非常清楚,如果实在遇到无法理解的错误(有明显编辑错误提示的,请调动您珍贵的大脑,看一下代码,去自己解决),乐意解答。
祝好运!

【Unity技巧】自定义消息框(弹出框)的更多相关文章

  1. ⒁bootstrap组件 工具提示框 弹出框 警告框 基础案例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. 玩转EasyUi弹出框

    这两天在搞EasyUi的弹出框,弹出框之前也搞过很多个版本,总是觉得不那么完美,刚好最近有时间,就往多处想了想,功能基本上达到我的预期,并且在开发过程中遇到很多小技巧,特撰文如下. 走起:在EasyU ...

  3. 通过jquery获得某个元素的位置, 透明div, 弹出框, 然后在旁边显示toggle子级联菜单-hover的bug解决

    jquery的"筛选选择器", 都是用冒号开头的, 即, 冒号选择器就是 筛选选择器.如: :first, :last, :eq(index), :first-child,...等 ...

  4. Bootstrap 弹出框和警告框插件

    一.弹出框 弹出框即点击一个元素弹出一个包含标题和内容的容器. //基本用法 <button class="btn btn-lg btn-danger" type=" ...

  5. vue--vant组件库Dialog弹出框

    安装vant UI框架: cnpm install vant –-save-dev 导入组件-在main.js里: import Vant from 'vant'; import'vant/lib/v ...

  6. 第二百四十六节,Bootstrap弹出框和警告框插件

    Bootstrap弹出框和警告框插件 学习要点: 1.弹出框 2.警告框 本节课我们主要学习一下 Bootstrap 中的弹出框和警告框插件. 一.弹出框 弹出框即点击一个元素弹出一个包含标题和内容的 ...

  7. 【cocos2dx 小技巧】半透明屏蔽罩和弹出框的实现

    今天介绍一下,弹出框的和屏蔽罩的小实现~ 弹出框主要用到了cocos2dx生命周期里面的OnEnter()函数,就是当Layer被addChild的时候会调用的函数(所以假设把OnEnter的代码加到 ...

  8. unity 之 自定义弹出框

    一.弹出框的搭建: 布局如图:Message为整个父物体,并且添加UiMessage代码.panel为遮罩. MessageBox为整个提示框,Panel为标题,ok为确定按钮,cancel为取消按钮 ...

  9. 四种常见的提示弹出框(success,warning,error,loading)原生JavaScript和jQuery分别实现

    原文:四种常见的提示弹出框(success,warning,error,loading)原生JavaScript和jQuery分别实现 虽然说现在官方的自带插件已经有很多了,但是有时候往往不能满足我们 ...

随机推荐

  1. bootstrap插件fileinput.js 出现出现$("#xxxx").fileinput({}); 不生效的情况解决

    如果出现$("#xxxx").fileinput({}); 不生效的情况请将fileinput.js中最后几行注释掉: /* $(document).ready(function ...

  2. css3部分整理

    1.css弹性盒子属性 父级元素属性的设置 #father{ width: 800px; height: 300px; background-color: darkgray; /*定义父级元素为弹性元 ...

  3. easing--缓动函数--贝塞尔函数--圆盘转动抽奖应用

    http://gsgd.co.uk/sandbox/jquery/easing/jquery.easing.1.3.js http://www.robertpenner.com/easing/penn ...

  4. git遇到的问题

    push代码时出现的问题: ! [remote rejected] HEAD -> (unpacker error)) 解决办法: $ git push --no-thin origin rel ...

  5. SpringMVC之简单的增删改查示例(SSM整合)

    本篇文章主要介绍了SpringMVC之简单的增删改查示例(SSM整合),这个例子是基于SpringMVC+Spring+Mybatis实现的.有兴趣的可以了解一下. 虽然已经在做关于SpringMVC ...

  6. Python中迭代输出(index,value)的几种方法

    需求如下:迭代输出序列的索引(index)和索引值(value). 1.创建测试列表: >>> lst = [1,2,3,4,5] 2.实现方法如下: #方法1:range()+le ...

  7. jboss规则引擎KIE Drools 6.3.0 Final 教程(1)

    前言 目前世面上中文的KIE DROOLS Workbench(JBOSS BRMS)的教程几乎没有,有的也只有灵灵碎碎的使用机器来翻译的(翻的不知所云)或者是基于老版本的JBOSS Guvnor即5 ...

  8. Dynamics CRM2016 在实体命名时需要注意的事项

    在使用web api的过程中遇到个很无语的设置,体现在对实体名的设置上,之前看到accounts以为只是在实体名上加个s,也没往深处看,但真正进入项目实施了问题就来了,city直接变成了cities不 ...

  9. [CSDN_Markdown] 使用CSDN Markdown编辑器

    简介 最近CSDN支持Markdown语法写博客了,甚是欢喜.前几天写了一篇实验了下,感觉不错.准备写几篇文章介绍一下如何使用CSDN的Markdown编辑器写博客,不求全面,但求够用,望大家批评指正 ...

  10. 两个无序数组分别叫A和B,长度分别是m和n,求中位数,要求时间复杂度O(m+n),空间复杂度O(1) 。

    #include <iostream> using namespace std; /*函数作用:取待排序序列中low.mid.high三个位置上数据,选取他们中间的那个数据作为枢轴*/ i ...