根据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. (0)开始 Raspberry Pi 项目前需要知道的 10 件事

    https://www.digikey.cn/zh/articles/techzone/2017/feb/10-things-to-know-before-starting-a-raspberry-p ...

  2. cronicle minio s3 存储配置集成

    cronicle 后端存储是可配置的 ,通过使用不同的存储配置,我们可以解决多实例部署以及数据共享的问题 cronicle 的后端存储模型,设计的特别方便,包含了基于文件的,基于s3 的,同时我们也可 ...

  3. PHP Record the number of login users

    Function to record how many times the user logs in Connect to the database first: you can create a n ...

  4. 第09组 Alpha冲刺(3/6)

    队名:观光队 组长博客 作业博客 组员实践情况 王耀鑫 过去两天完成了哪些任务 文字/口头描述 完成服务器连接数据库部分代码 展示GitHub当日代码/文档签入记录 接下来的计划 服务器网络请求,vu ...

  5. Ubuntu 19.04 国内更新源

    2019年4月18日, Ubuntu 19.04 正式发布. Ubuntu 19.04 的 Codename 是"disco(迪斯科舞厅)": zkf@ubuntu:~$ lsb_ ...

  6. 分析WordPress数据表之评论表(功能篇)

    数据表分析 wp_comments(评论表) 该表字段,如下:comment_ID(评论ID)comment_post_ID(评论文章ID)comment_author(评论者用户名)comment_ ...

  7. ansible-playbook-常用

    创建软链:file: - name: create link hosts: "{{hosts_ip}}" tasks: - name: create link file: src= ...

  8. 在error日志打印异常

    在日志中打印异常,经常会看到以下的写法: logger.error(e.getMessage()); 或者是: e.printStackTrace(); 这两种其实都不太好. e.getMessage ...

  9. 一口气讲完 LSA — PlSA —LDA在自然语言处理中的使用

    自然语言处理之LSA LSA(Latent Semantic Analysis), 潜在语义分析.试图利用文档中隐藏的潜在的概念来进行文档分析与检索,能够达到比直接的关键词匹配获得更好的效果. LSA ...

  10. groupby 的妙用(注意size和count)

    Pandas的groupby()功能很强大,用好了可以方便的解决很多问题,在数据处理以及日常工作中经常能施展拳脚. 今天,我们一起来领略下groupby()的魅力吧. 首先,引入相关package: ...