根据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. 验证码破解 | Selenium模拟登陆12306

    12306官网登录的验证码破解比较简单,验证码是常规的点触类型验证码,使用超级鹰识别率比较高. 思路: (1)webdriver打开浏览器: (2)先对整个屏幕截屏,通过标签定位找到验证码图片,并定位 ...

  2. Anaconda与Python安装版本对应关系 --- 转载

    转载自:https://blog.csdn.net/yuejisuo1948/article/details/81043823 首先解释一下上表. anaconda在每次发布新版本的时候都会给pyth ...

  3. HDU 4828 小明系列故事——捉迷藏

    漂亮妹子点击就送:http://acm.hdu.edu.cn/showproblem.php?pid=4528 Time Limit: 500/200 MS (Java/Others)    Memo ...

  4. 【题解】洛谷 P1080 国王游戏

    目录 题目 思路 \(Code\) 题目 P1080 国王游戏 思路 贪心+高精度.按\(a \times b\)从小到大排序就可以了. \(Code\) #include<bits/stdc+ ...

  5. 洛谷P2607题解

    想要深入学习树形DP,请点击我的博客. 本题的DP模型同 P1352 没有上司的舞会.本题的难点在于如何把基环树DP转化为普通的树上DP. 考虑断边和换根.先找到其中的一个环,在上面随意取两个点, 断 ...

  6. nginx 访问控制之 限速

    nginx限速可以通过 ngx_http_limit_conn_module 和 ngx_http_limit_req_module 模块来实现限速的功能. 一.ngx_http_limit_conn ...

  7. 区间DP复习

    区间DP复习 (难度排序:(A,B),(F,G,E,D,H,I,K),(C),(J,L)) 这是一个基本全在bzoj上的复习专题 没有什么可以说的,都是一些基本的dp思想 A [BZOJ1996] [ ...

  8. 模拟30A 题解

    A. 树 联想起远古考试时做的题 记忆的轮廓. 树上走一些步数的期望. 显然可以直接解方程. 然而复杂度$O(qn^3)$,利用树上的性质优化一下, 直接一遍dfs过程中解出来,可以$O(qnlogm ...

  9. Linux kill、kill-15、kill-9区别

    进程状态转换图 kill和kill -9,两个命令在linux中都有杀死进程的效果,然而两命令的执行过程却大有不同,在程序中如果用错了,可能会造成莫名其妙的现象. 执行kill(不加 -* 默认kil ...

  10. oracle通过dblink连接mysql配置详解(全Windows下)

    关于oracle通过dblink连接mysql,经过了两周的空闲时间研究学习,终于配置好了,真是不容易啊,仔细想想的话,其实也没花多长时间,就是刚开始走了一段弯路,所以把这次的经验分享出来,让大家少走 ...