Unity 新手引導


根據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[4];
    /// <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[0]), WorldToCanvasPos(canvas, corners[2])) / 2;
        //計算高亮顯示區域的中心
        float x = corners[0].x + ((corners[3].x - corners[0].x) / 2);
        float y = corners[0].y + ((corners[1].y - corners[0].y) / 2);
        Vector3 centerWorld = new Vector3(x, y, 0);
        Vector2 center = WorldToCanvasPos(canvas, centerWorld);
        //設置遮罩材質中的中心變量
        Vector4 centerMat = new Vector4(center.x, center.y, 0, 0);
        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;
    }
}
View Code

矩形遮罩鏤空處理腳本:

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[4];
    /// <summary>
    /// 鏤空區域中心
    /// </summary>
    private Vector4 center;
    /// <summary>
    /// 最終的偏移x
    /// </summary>
    private float targetOffsetX = 0;
    /// <summary>
    /// 最終的偏移y
    /// </summary>
    private float targetOffsetY = 0;
    /// <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[0]), WorldToCanvasPos(canvas, corners[3])) / 2f;
        targetOffsetY = Vector2.Distance(WorldToCanvasPos(canvas, corners[0]), WorldToCanvasPos(canvas, corners[1])) / 2f;
        //計算高亮顯示區域的中心
        float x = corners[0].x + ((corners[3].x - corners[0].x) / 2);
        float y = corners[0].y + ((corners[1].y - corners[0].y) / 2);
        Vector3 centerWorld = new Vector3(x, y, 0);
        Vector2 center = WorldToCanvasPos(canvas, centerWorld);
        //設置遮罩材質中的中心變量
        Vector4 centerMat = new Vector4(center.x, center.y, 0, 0);
        material = GetComponent<Image>().material;
        material.SetVector("_Center", centerMat);
        //計算當前高亮顯示區域的半徑
        RectTransform canRectTransform = canvas.transform as RectTransform;
        if (canRectTransform != null)
        {
            //獲取畫布區域的四個頂點
            canRectTransform.GetWorldCorners(corners);
            //計算偏移初始值
            for (int i = 0; i < corners.Length; i++)
            {
                if (i % 2 == 0)
                {
                    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;
    }
}
View Code

新手引導管理腳本,通過此腳本管理遮罩跟引導步驟,動態添加按鈕點擊事件等:

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 = 0;
    /// <summary>
    /// 是否完成所有的新手引導
    /// </summary>
    private bool isFinish = false;
    /// <summary>
    /// 遮罩對象
    /// </summary>
    private GameObject maskPrefabs;
    /// <summary>
    /// 
    /// </summary>
    public void Next()
    {
        if (isFinish || currentIndex > guideList.Count)
        {
            return;
        }
        //注銷上一步按鈕點擊事件
        if (currentIndex != 0 && guideList[currentIndex - 1].go.GetComponent<EventTriggerListener>() != null)
        {
            EventTriggerListener.GetListener(guideList[currentIndex - 1].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 - 1].go).onClick += (go) =>
            {
                Next();
            };
        }
        //最后一個按鈕點擊事件處理
        else if (currentIndex == guideList.Count)
        {
            EventTriggerListener.GetListener(guideList[currentIndex - 1].go).onClick += (go) =>
            {
                maskPrefabs.gameObject.SetActive(false);
                //注銷最后一個按鈕的點擊事件
                EventTriggerListener.GetListener(guideList[currentIndex - 1].go).onClick -= null;
            };
            isFinish = true;
        }
    }
}
/// <summary>
/// 引導ui數組
/// </summary>
[Serializable]
public class GuideUIList
{
    /// <summary>
    /// 引導步驟對象
    /// </summary>
    public GameObject go;

    public GuideUIList(GameObject go)
    {
        this.go = go;
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM