先声明一下,UNITY新手,如果说的有不对的地方,欢迎各位大神指正。

  最近在项目需要实现新手引导,最基础的需求就是需要一个带黑色遮罩的引导UI,类似下图这种:

  对,就是这么敷衍的UI,因为是我随手做的。

  这里有两个关键点:

  1. 黑色的遮罩要怎么做,才能挡住其他部分而留出目标的按钮

  2.点击事件要怎么处理

  我找到过一种解决方案: 添加一个顶层UI,添加一个黑色半透明的UIWidget,然后COPY一份要引导的空间的GameObject, 然后加到这个引导的UI上,只要这个COPY的GAMEOBJECT的深度比半透明黑的要高就行了。然后收到点击时间后,再去模拟点击原来的控件。这个方案应该是可行的,但是我没有采纳,主要是不想要这个拷贝的步骤,点击事件的模拟也不是一件开心的事情。我决定用另外一个方案,上层的遮罩的黑色半透明区域和boxcolider都只覆盖目标以外的部分,点击事件的话直接添加原来的事件监听里,来省下GameObject的拷贝的过程。

  实现我想要的黑色的遮罩的效果,最早我是想用一个笨办法:加4个UIWidget和4个BoxColider,就能彻底围住目标控件。这么做也不是不行,实在是有些丑陋,所幸我发现已经有人分享这样的控件: http://blog.csdn.net/lzdidiv/article/details/72293727

  感谢这位作者,我测试过,效果完全没问题。

  接下来是点击事件的问题。我添加了一个脚本NavControl,用来定位目标控件和做点击事件的添加的:

using System;
using System.Collections.Generic;
using UnityEngine; public class NavControl : MonoBehaviour
{
public static List<NavControl> activeControlList = new List<NavControl>(); public string nodeId;
public UIWidget targetWidget; private UIEventListener.VoidDelegate clickCallback;
public bool AddOnClickEvent(UIEventListener.VoidDelegate callback)
{
if (callback == null)
return false; UIEventListener listener = this.gameObject.GetComponent<UIEventListener>();
if (listener == null)
{
UIToggle toggle = this.gameObject.GetComponent<UIToggle>();
if (toggle)
{
EventDelegate.Add(toggle.onChange, onClick2);
}
else
{
Debug.LogError(string.Format("onAddClickEvent Can't not find event listener:{0}", this.nodeId));
return false;
}
}
else
{
listener.onClick += onClick;
} clickCallback = callback;
return true;
} public void RemoveOnClickEvent()
{
UIEventListener listener = this.gameObject.GetComponent<UIEventListener>();
if (listener == null)
{
UIToggle toggle = this.gameObject.GetComponent<UIToggle>();
if (toggle)
{
EventDelegate.Remove(toggle.onChange, onClick2);
}
else
{
Debug.LogError(string.Format("onRemoveClickEvent Can't not find event listener:{0}", this.nodeId));
}
return;
}
else
{
listener.onClick -= onClick;
}
} public static NavControl getNavControl(string key)
{
for(int i = 0; i < activeControlList.Count; ++i)
{
if (activeControlList[i].nodeId == key)
{
return activeControlList[i];
}
} return null;
} private void onClick(GameObject go1)
{
if (clickCallback == null || targetWidget == null)
return; clickCallback(targetWidget.gameObject);
} private void onClick2()
{
if (clickCallback == null || targetWidget == null)
return; clickCallback(targetWidget.gameObject);
} void Awake()
{
if (targetWidget == null)
{
targetWidget = this.gameObject.GetComponent<UIWidget>();
}
} void OnEnable()
{
activeControlList.Add(this);
} void OnDisable()
{
activeControlList.Remove(this);
}
}

  在AddOnClickEvent函数里面,可以看到我支持了两种情况的点击事件的插入:  UIEventListener和UIToggle, 在我的项目里,这两种已经足够覆盖新手引导的需求,如果有其他的需求,扩展这里的逻辑就是。

  在这个脚本里面,有一个nodeId的变量,nodeId必须是全局唯一的,这个是用到标记新手引导对应的控件。而另外一个成员targetWidget, 是用来做遮罩的对位的,如果没有设置,会默认从当前的GameObject上获取UIWidget

  接下来我写了个贼简单的demo,提供我的思路给大家参考。

  如第一个敷衍的图,上面有三个按钮, 现在我需要引导玩家依次点击按钮111,按钮222和按钮Close, prefabe结构如下:

  在btn1,btn2和btn3上,我都添加NavControl的脚本

这里的NodeId我设置成和GameObject的名字一样,以示区分。

在nav的gameObject下我添加UIMaskWidget的脚本。

TestUI挂上了一个简单的测试脚本,代码如下:

using UnityEngine;
using System.Collections;
using System.Collections.Generic; public class TestNav : MonoBehaviour {
private GameObject btn1;
private GameObject btn2;
private GameObject btn3;
private GameObject btnStart;
private UIMaskWidget maskWidget;
private Queue<string> navQueue = new Queue<string>(); // Use this for initialization
void Start () {
navQueue.Enqueue("btn1");
navQueue.Enqueue("btn2");
navQueue.Enqueue("btn3"); btn1 = gameObject.transform.FindChild("btn1").gameObject;
btn2 = gameObject.transform.FindChild("btn2").gameObject;
btn3 = gameObject.transform.FindChild("btn3").gameObject;
btnStart = gameObject.transform.FindChild("btnStart").gameObject;
maskWidget = gameObject.transform.FindChild("nav").GetComponent<UIMaskWidget>(); UIEventListener.Get(btnStart).onClick += (GameObject go1) =>
{
btn1.SetActive(true);
btn2.SetActive(true);
btn3.SetActive(true);
maskWidget.gameObject.SetActive(true); btnStart.SetActive(false); showNextNav();
}; UIEventListener.Get(btn1).onClick += (GameObject go) =>
{ }; UIEventListener.Get(btn2).onClick += (GameObject go) =>
{ }; UIEventListener.Get(btn3).onClick += (GameObject go) =>
{ };
} void showNextNav()
{
if (navQueue.Count == 0)
{
maskWidget.gameObject.SetActive(false);
return;
} string key = navQueue.Dequeue();
NavControl control = NavControl.getNavControl(key);
if (control != null)
{
if (control.targetWidget != null)
{
this.maskWidget.transform.position = control.targetWidget.transform.position;
this.maskWidget.width = control.targetWidget.width;
this.maskWidget.height = control.targetWidget.height; control.AddOnClickEvent((GameObject btnGo) => {
showNextNav();
}
);
}
}
} // Update is called once per frame
void Update () { }
}

  关键的逻辑就在showNextNav这个函数里面, 调用NavControl的静态函数获取目标控件,然后设置UIMaskWidget的位置和遮罩的区域。

  代码比较简单,只是演示我的思路,有疑问或者异议,欢迎讨论。

  我的代码是基于unity5.3.4, ngui 3.8.2, 测试工程链接: https://files.cnblogs.com/files/lyrers/NavControl.zip。

NGUI的新手引导的实现的更多相关文章

  1. NGUI 新手引导

    现在我们的游戏已到了开发后期,这个时候需要做新手引导这一块(恶心的新手引导,真想说游戏行业究竟哪个2B最先想出来要引导的???代码搞的到处都是,改了一次又改!) 吐槽过后进入正题:主要还是UI相关的操 ...

  2. Unity3d 用NGUI制作做新手引导的思路

    一.先看下效果 Prefab结构 二.实现思路: 1.prefab上的Panel层级设置成较高 2.背景由5个UISprite拼接起来的,4个(L,R,U,D)当作遮罩,1个镂空(Hollow)当作点 ...

  3. NGUI利用深度测试实现新手引导遮罩

    实现原理:实际上就是先利用渲染队列渲染,然后再利用ZTest,改变渲染的遮挡关系. PS:Depth Testing:深度测试,也叫深度缓冲.只有最靠近观察者的物体会被绘制.深度即Z,该值越小表示离观 ...

  4. UISprite(NGUI)扩展 图片镂空

    NGUI 版本2.6.3 在做新手引导时,需要高亮特定UI元素,也就是加个黑色蒙板,然后在蒙版上显示这个UI元素,为了简单方便我扩展了下UISprite,来镂空指定空间. 分为2种,矩形和圆镂空,矩形 ...

  5. 【转】发布一个基于NGUI编写的UI框架

    发布一个基于NGUI编写的UI框架 1.加载,显示,隐藏,关闭页面,根据标示获得相应界面实例 2.提供界面显示隐藏动画接口 3.单独界面层级,Collider,背景管理 4.根据存储的导航信息完成界面 ...

  6. 如何简单的实现新手引导之UGUI篇

    一个完整的游戏项目肯定是要做新手引导的,而引导做的好坏可能会影响玩家的留存.那么怎么简单的实现个简有效的引导呢!先不说废话,先看看效果,这是一个基于UGUI做的一个简单的引导! 怎么样,看着是那么回事 ...

  7. 关于Unity中NGUI的Tab商城、Scrollview和打字机效果的实现

    Tab商城实例 UIToggle 和 UIToggledObjects+ Box Collider(实现商城功能必备) 1.创建两个个UI Sprite,Sprite1和Sprite2 2.给Spri ...

  8. UE4新手引导入门教程

    请大家去这个地址下载:file:///D:/UE4%20Doc/虚幻4新手引导入门教程.pdf

  9. Unity3D新手引导开发手记

    最近开始接手新手引导的开发,记录下这块相关的心得 首先客户端是Unity,在接手前,前面的同学已经初步完成了新手引导框架的搭建,这套框架比较简单,有优点也有缺点,稍后一一点评 我们的新手引导是由一个个 ...

随机推荐

  1. [拾 得] 一枚迷人的贝壳 SHELL / Linux | shell 脚本初步入门

    坚持知识分享,该文章由Alopex编著, 转载请注明源地址: http://www.cnblogs.com/alopex/   索引: 什么是shell shell的分类 shell脚本的执行方式   ...

  2. jquery 循环获取checkBox的值,以及对复选框选中,取消,操作按钮

    法一:jquery 循环获取选中checkBox框的值 function checkeds() { $("input:checkbox").each(function(index) ...

  3. 【笔记】vue-cli 开发环境中跨域连接后台api(vue-resource 跨域post 请求)

    在vue-cli 项目中很多人会用到mock 数据(模拟数据),但是我觉得如果在真实的数据库交互中开发会更有安全感一些,所以查了一下百度很多人推荐的就是: 跨域! 跨域是什么概念?不同的主机名,同主机 ...

  4. js截取字符串的方法

    1,slice(a, b) 第一个参数表示起始位置,第二个表示截取到但不包含 关于参数正负问题,只要记住一点:永远不能倒着截取!否则返回空字符串 2,substring(a, b) 第一个参数表示起始 ...

  5. 安利一个神器:Tmux

    对于程序员来说,一个好用且高效的软件工具就如同加持了神技的游戏角色.下面就给大家介绍一个神器 Tmux 以及个人的使用总结. 一.我所认识的 Tmux 在工作中,我把 tmux 当作终端会话管理器来使 ...

  6. Discuz论坛URL静态化规则urlrewrite

    http://blog.csdn.net/u014181418/article/details/53467980 1.在论坛代码目录下新建文件".htaccess" vim /us ...

  7. java -cp用法

    原文出处:http://blog.csdn.net/zhuying_linux/article/details/7714194.感谢作者的分享 java -cp classpath Specify a ...

  8. 在 Tomcat 上配置虚拟主机

    .Tomcat 服务器的server.xml文件   (1)Tomcat 组件 Tomcat服务器是由一系列可配置的组件构成,其中核心组件是 Catalina Servlet 容器,它是所有其他 To ...

  9. 探索ArrayList自动改变size真相

    探索ArrayList自动改变size真相 ArrayList的列表对象实质上是存储在一个引用型数组里的,有人认为该数组有“自动增长机制”可以自动改变size大小.正式地说,该数组是无法改变 大小的, ...

  10. SpringMVC空字符串转为null

    空字符串转为null 现在我遇到这样一个需求,那就是我想要吧前端传过来的值变为空,因为所谓前端的校验,其实都不是校验,如果前端传给后台一个表单,可是表单未填入值,我们后台进行判断的时候 既需要判断nu ...