根据Shader动态生成遮罩

源码地址

圆形遮罩镂空处理脚本:

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; /// <summary>
/// 圆形遮罩镂空
/// </summary>
public class CircleGuidance : MonoBehaviour
{
public static CircleGuidance instance;
/// <summary>
/// 高亮显示目标
/// </summary>
public Image target;
/// <summary>
/// 区域范围缓存
/// </summary>
private Vector3[] corners = new Vector3[];
/// <summary>
/// 镂空区域中心
/// </summary>
private Vector4 center;
/// <summary>
/// 镂空区域半径
/// </summary>
private float radius;
/// <summary>
/// 遮罩材质
/// </summary>
private Material material;
/// <summary>
/// 当前高亮区域半径
/// </summary>
private float currentRadius;
/// <summary>
/// 高亮区域缩放的动画时间
/// </summary>
private float shrinkTime = 0.5f;
/// <summary>
/// 事件渗透组件
/// </summary>
private GuidanceEventPenetrate eventPenetrate;
private void Awake()
{
instance = this;
}
public void Init(Image target)
{
this.target = target;
eventPenetrate = GetComponent<GuidanceEventPenetrate>();
if (eventPenetrate != null)
{
eventPenetrate.SetTargetImage(target);
}
Canvas canvas = GameObject.Find("Canvas").GetComponent<Canvas>();
//获取高亮区域的四个顶点的世界坐标
target.rectTransform.GetWorldCorners(corners);
//计算最终高亮显示区域的半径
radius = Vector2.Distance(WorldToCanvasPos(canvas, corners[]), WorldToCanvasPos(canvas, corners[])) / ;
//计算高亮显示区域的中心
float x = corners[].x + ((corners[].x - corners[].x) / );
float y = corners[].y + ((corners[].y - corners[].y) / );
Vector3 centerWorld = new Vector3(x, y, );
Vector2 center = WorldToCanvasPos(canvas, centerWorld);
//设置遮罩材质中的中心变量
Vector4 centerMat = new Vector4(center.x, center.y, , );
material = GetComponent<Image>().material;
material.SetVector("_Center", centerMat);
//计算当前高亮显示区域的半径
RectTransform canRectTransform = canvas.transform as RectTransform;
if (canRectTransform != null)
{
//获取画布区域的四个顶点
canRectTransform.GetWorldCorners(corners);
//将画布顶点距离高亮区域中心最近的距离昨晚当前高亮区域半径的初始值
foreach (var corner in corners)
{
currentRadius = Mathf.Max(Vector3.Distance(WorldToCanvasPos(canvas, corner), corner), currentRadius);
}
}
material.SetFloat("_Slider", currentRadius);
}
/// <summary>
/// 收缩速度
/// </summary>
private float shrinkVelocity = 0f;
private void Update()
{
//从当前半径到目标半径差值显示收缩动画
float value = Mathf.SmoothDamp(currentRadius, radius, ref shrinkVelocity, shrinkTime);
if (!Mathf.Approximately(value, currentRadius))
{
currentRadius = value;
material.SetFloat("_Slider", currentRadius);
}
} /// <summary>
/// 世界坐标转换为画布坐标
/// </summary>
/// <param name="canvas">画布</param>
/// <param name="world">世界坐标</param>
/// <returns></returns>
private Vector2 WorldToCanvasPos(Canvas canvas, Vector3 world)
{
Vector2 position;
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, world, canvas.GetComponent<Camera>(), out position);
return position;
}
}

矩形遮罩镂空处理脚本:

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; /// <summary>
/// 矩形遮罩镂空
/// </summary>
public class RectGuidance : MonoBehaviour
{
public static RectGuidance instance;
/// <summary>
/// 高亮显示目标
/// </summary>
public Image target;
/// <summary>
/// 区域范围缓存
/// </summary>
private Vector3[] corners = new Vector3[];
/// <summary>
/// 镂空区域中心
/// </summary>
private Vector4 center;
/// <summary>
/// 最终的偏移x
/// </summary>
private float targetOffsetX = ;
/// <summary>
/// 最终的偏移y
/// </summary>
private float targetOffsetY = ;
/// <summary>
/// 遮罩材质
/// </summary>
private Material material;
/// <summary>
/// 当前的偏移x
/// </summary>
private float currentOffsetX = 0f;
/// <summary>
/// 当前的偏移y
/// </summary>
private float currentOffsetY = 0f;
/// <summary>
/// 高亮区域缩放的动画时间
/// </summary>
private float shrinkTime = 0.5f;
/// <summary>
/// 事件渗透组件
/// </summary>
private GuidanceEventPenetrate eventPenetrate; private void Awake()
{
instance = this;
}
public void Init(Image target)
{
this.target = target;
eventPenetrate = GetComponent<GuidanceEventPenetrate>();
if (eventPenetrate != null)
{
eventPenetrate.SetTargetImage(target);
}
Canvas canvas = GameObject.Find("Canvas").GetComponent<Canvas>();
//获取高亮区域的四个顶点的世界坐标
target.rectTransform.GetWorldCorners(corners);
//计算高亮显示区域在画布中的范围
targetOffsetX = Vector2.Distance(WorldToCanvasPos(canvas, corners[]), WorldToCanvasPos(canvas, corners[])) / 2f;
targetOffsetY = Vector2.Distance(WorldToCanvasPos(canvas, corners[]), WorldToCanvasPos(canvas, corners[])) / 2f;
//计算高亮显示区域的中心
float x = corners[].x + ((corners[].x - corners[].x) / );
float y = corners[].y + ((corners[].y - corners[].y) / );
Vector3 centerWorld = new Vector3(x, y, );
Vector2 center = WorldToCanvasPos(canvas, centerWorld);
//设置遮罩材质中的中心变量
Vector4 centerMat = new Vector4(center.x, center.y, , );
material = GetComponent<Image>().material;
material.SetVector("_Center", centerMat);
//计算当前高亮显示区域的半径
RectTransform canRectTransform = canvas.transform as RectTransform;
if (canRectTransform != null)
{
//获取画布区域的四个顶点
canRectTransform.GetWorldCorners(corners);
//计算偏移初始值
for (int i = ; i < corners.Length; i++)
{
if (i % == )
{
currentOffsetX = Mathf.Max(Vector3.Distance(WorldToCanvasPos(canvas, corners[i]), center), currentOffsetX);
}
else
{
currentOffsetY = Mathf.Max(Vector3.Distance(WorldToCanvasPos(canvas, corners[i]), center), currentOffsetY);
}
}
}
//设置遮罩材质中当前偏移的变量
material.SetFloat("_SliderX", currentOffsetX);
material.SetFloat("_SliderY", currentOffsetY);
}
/// <summary>
/// 收缩速度
/// </summary>
private float shrinkVelocityX = 0f;
private float shrinkVelocityY = 0f;
private void Update()
{
//从当前偏移量到目标偏移量差值显示收缩动画
float valueX = Mathf.SmoothDamp(currentOffsetX, targetOffsetX, ref shrinkVelocityX, shrinkTime);
float valueY = Mathf.SmoothDamp(currentOffsetY, targetOffsetY, ref shrinkVelocityY, shrinkTime);
if (!Mathf.Approximately(valueX, currentOffsetX))
{
currentOffsetX = valueX;
material.SetFloat("_SliderX", currentOffsetX);
}
if (!Mathf.Approximately(valueY, currentOffsetY))
{
currentOffsetY = valueY;
material.SetFloat("_SliderY", currentOffsetY);
}
} /// <summary>
/// 世界坐标转换为画布坐标
/// </summary>
/// <param name="canvas">画布</param>
/// <param name="world">世界坐标</param>
/// <returns></returns>
private Vector2 WorldToCanvasPos(Canvas canvas, Vector3 world)
{
Vector2 position;
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, world, canvas.GetComponent<Camera>(), out position);
return position;
}
}

新手引导管理脚本,通过此脚本管理遮罩跟引导步骤,动态添加按钮点击事件等:

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; /// <summary>
/// 新手引导管理
/// </summary>
public class GuideManagers : MonoBehaviour
{
/// <summary>
/// 引导步骤数组(如:第一步-》第二步。。。。)
/// </summary>
public List<GuideUIList> guideList = new List<GuideUIList>();
/// <summary>
/// 当前数组索引
/// </summary>
private int currentIndex = ;
/// <summary>
/// 是否完成所有的新手引导
/// </summary>
private bool isFinish = false;
/// <summary>
/// 遮罩对象
/// </summary>
private GameObject maskPrefabs;
/// <summary>
///
/// </summary>
public void Next()
{
if (isFinish || currentIndex > guideList.Count)
{
return;
}
//注销上一步按钮点击事件
if (currentIndex != && guideList[currentIndex - ].go.GetComponent<EventTriggerListener>() != null)
{
EventTriggerListener.GetListener(guideList[currentIndex - ].go).onClick -= null;
} if (maskPrefabs == null)
{
maskPrefabs = Instantiate(Resources.Load<GameObject>("RectGuidance_Panel"), this.transform);
}
//初始化遮罩
maskPrefabs.GetComponent<RectGuidance>().Init(guideList[currentIndex].go.GetComponent<Image>()); ; currentIndex++;
//给当前按钮添加点击事件
if (currentIndex < guideList.Count)
{
EventTriggerListener.GetListener(guideList[currentIndex - ].go).onClick += (go) =>
{
Next();
};
}
//最后一个按钮点击事件处理
else if (currentIndex == guideList.Count)
{
EventTriggerListener.GetListener(guideList[currentIndex - ].go).onClick += (go) =>
{
maskPrefabs.gameObject.SetActive(false);
//注销最后一个按钮的点击事件
EventTriggerListener.GetListener(guideList[currentIndex - ].go).onClick -= null;
};
isFinish = true;
}
}
}
/// <summary>
/// 引导ui数组
/// </summary>
[Serializable]
public class GuideUIList
{
/// <summary>
/// 引导步骤对象
/// </summary>
public GameObject go; public GuideUIList(GameObject go)
{
this.go = go;
}
}

Unity 新手引导的更多相关文章

  1. Unity3D新手引导开发手记

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

  2. NGUI的新手引导的实现

    先声明一下,UNITY新手,如果说的有不对的地方,欢迎各位大神指正. 最近在项目需要实现新手引导,最基础的需求就是需要一个带黑色遮罩的引导UI,类似下图这种: 对,就是这么敷衍的UI,因为是我随手做的 ...

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

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

  4. (转)Unity3D新手引导开发手记

    转自:http://www.cnblogs.com/ybgame/p/3844315.html 最近开始接手新手引导的开发,记录下这块相关的心得 首先客户端是Unity,在接手前,前面的同学已经初步完 ...

  5. Unity 游戏框架:UI 管理神器 UI Kit

    UI Kit 快速入门 首先我们来进行 UI Kit 的快速入门 制作一个界面的,步骤如下: 准备 生成代码 逻辑编写 运行 1. 准备 先创建一个场景 TestUIHomePanel. 删除 Hie ...

  6. Unity3d入门 - 关于unity工具的熟悉

    上周由于工作内容较多,花在unity上学习的时间不多,但总归还是学习了一些东西,内容如下: .1 根据相关的教程在mac上安装了unity. .2 学习了unity的主要的工具分布和对应工具的相关的功 ...

  7. 聊聊Unity项目管理的那些事:Git-flow和Unity

    0x00 前言 目前所在的团队实行敏捷开发已经有了一段时间了.敏捷开发中重要的一个话题便是如何对项目进行恰当的版本管理.项目从最初使用svn到之后的Git One Track策略再到现在的GitFlo ...

  8. Unity游戏内版本更新

    最近研究了一下游戏内apk包更新的方法. ios对于应用的管理比较严格,除非热更新脚本,不太可能做到端内大版本包的更新.然而安卓端则没有此限制.因此可以做到不跳到网页或应用商店,就覆盖更新apk包. ...

  9. Unity 序列化

    Script Serialization http://docs.unity3d.com/Manual/script-Serialization.html 自定义序列化及例子: http://docs ...

随机推荐

  1. 使用mustache 做为docker容器运行动态配置工具

    很多时候我们需要在启动容器的时候基于配置文件运行,如果在配置比较简单的时候我们可以通过环境变量 注入,同时当前12 factors 越来越融入大家的开发中了(对于配置通过环境变量处理),但是好多老的软 ...

  2. 如何进行seo优化要点总结

    一.搜索引擎工作原理 当我们在输入框中输入关键词,点击搜索或查询时,然后得到结果.深究其背后的故事,搜索引擎做了很多事情. 在搜索引擎网站,比如百度,在其后台有一个非常庞大的数据库,里面存储了海量的关 ...

  3. JavaScript base64 加密解密

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

  4. Linux常用命令简述--dirname与basename

    dirname 获取父目录 basename 显示最后的目录名或文件名 .dirname [root@liang ~]# dirname /etc/httpd/ /etc [root@liang ~] ...

  5. Mongoose 多表(N个表)关联查询aggregate

    Mongoose 多表(N个表)关联查询概述 需求:文章(article),文章分类(articlecate),用户(user)这三个表之间的关系,一篇文章对应文章分类表中的某个类型,对应着用户表中的 ...

  6. win10 关闭开机密码

    在运行对话框里输入:netplwiz 控制面板(control)>电源选项>更改计算机休眠时间-选为 从不

  7. 歪国人整理的 2019 年 Java 开发路线图,值得参考!

      许多Java开发人员都希望通过某种Java成长路线图,来解答有关:该学习哪些技术,使用哪些工具以及框架之类的问题. 在此,我将向大家展示一张根据自己多年经验总结出的路线图.该路线图在保持简单可行的 ...

  8. 19 条效率至少提高 3 倍的 MySQL 技巧

    阅读本文大概需要 4 分钟. 来源:https://zhuanlan.zhihu.com/p/49888088 本文我们来谈谈项目中常用的 MySQL 优化方法,共 19 条,具体如下: 1.EXPL ...

  9. 聊聊Mysql索引和redis跳表 ---redis的有序集合zset数据结构底层采用了跳表原理 时间复杂度O(logn)(阿里)

    redis使用跳表不用B+数的原因是:redis是内存数据库,而B+树纯粹是为了mysql这种IO数据库准备的.B+树的每个节点的数量都是一个mysql分区页的大小(阿里面试) 还有个几个姊妹篇:介绍 ...

  10. 【转】linux sed命令

    转自:linux sed命令就是这么简单 参考:Linux三大剑客之sed:https://blog.csdn.net/solaraceboy/article/details/79272344 阅读目 ...