unity--實現新手引導功能


一:矩形鏤空功能

1、新建一個場景,創建兩個按鈕,一個Image

2、導入shader,創建兩個材質,將兩個shader拖到兩個材質上。將材質拖動到Image組件的Material上。

 

 3、創建腳本RectGuide,創建一個方法Guide(參數:Canvas(為了將世界轉換屏幕坐標提供需要的Camera,target(要鏤空的組件)),測試一下

  • GetWorldCorners:在世界空間中得到計算的矩形的角。參數角的數組
  • WorldToScreenPoint參數:camera(通過canvas傳入),vector3(世界坐標)

using Sytem.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class RectGuide : MonoBehaviour
{
    private Material material;  // 材質
    private Vector3 center;     // 鏤空區域的中心
    private float width;        // 鏤空區域的寬
    private float height;       // 鏤空區域的高

    private RectTransform target;// 要顯示的目標,通過目標計算鏤空區域的中心,寬高
    public Vector3[] targetCorners = new Vector3[4];//存儲要鏤空組件的四個角的數組
  
    // 引導
    public void Guide(Canvas canvas, RectTransform target)
    {
        this.target = target;  // 將傳進來的目標組件賦值給target

        // 獲取中心點
        // GetWorldCorners:在世界空間中得到計算的矩形的角。參數角的數組
        target.GetWorldCorners(targetCorners);
        // 講四個角的世界坐標轉為屏幕坐標
        for (int i = 0; i < targetCorners.Length; i++)
        {
            // WorldToScreenPoint參數:camera(通過canvas傳入),vector3(世界坐標)
            targetCorners[i]=RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, targetCorners[i]);
        }
    }

    private void Update()
    {
        Guide(GameObject.Find("Canvas").GetComponent<Canvas>(),GameObject.Find("Button").GetComponent<RectTransform>());
    }
}

 問題:shader的坐標的原點是中心,而轉換的屏幕坐標的原點是左下角,所以需要轉化

4、RectTransformUtility.ScreenPointToLocalPointInRectangle:屏幕坐標轉換為局部坐標

  參數:

  •     RectTransform rect ------轉換為誰的局部坐標
  •     Vector2 screenPoint  ------要轉換的屏幕坐標
  •     Camera cam,             ------相機
  •     out Vector2 localPoint ---- 輸出的局部坐標
// 講四個角的世界坐標轉為屏幕坐標
        for (int i = 0; i < targetCorners.Length; i++)
        {
            // WorldToScreenPoint參數:camera(通過canvas傳入),vector3(世界坐標)
            targetCorners[i]=RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, targetCorners[i]);

            // 屏幕坐標轉換為局部坐標
            //out的是vector2類型,事先聲明
            Vector2 localPoint;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.GetComponent<RectTransform>(),
                                                targetCorners[i], canvas.worldCamera, out localPoint);
            targetCorners[i] = localPoint;
        }

5、將上述代碼封裝成一個方法,完整的為:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class RectGuide : MonoBehaviour
{
    private Material material;  // 材質
    private Vector3 center;     // 鏤空區域的中心
    private float width;        // 鏤空區域的寬
    private float height;       // 鏤空區域的高

    private RectTransform target;// 要顯示的目標,通過目標計算鏤空區域的中心,寬高
    public Vector3[] targetCorners = new Vector3[4];//存儲要鏤空組件的四個角的數組
  
    // 引導
    public void Guide(Canvas canvas, RectTransform target)
    {
        this.target = target;  // 將傳進來的目標組件賦值給target

        // 獲取中心點
        // GetWorldCorners:在世界空間中得到計算的矩形的角。參數角的數組
        target.GetWorldCorners(targetCorners);

        // 講四個角的世界坐標轉為局部坐標坐標
        for (int i = 0; i < targetCorners.Length; i++)
        {
            targetCorners[i] = WorldToScreenPoint(canvas, targetCorners[i]);
        }
    }

    //private void Update()
    //{
    //    Guide(GameObject.Find("Canvas").GetComponent<Canvas>(),GameObject.Find("Button").GetComponent<RectTransform>());
    //}

    public Vector2 WorldToScreenPoint(Canvas canvas,Vector3 world)
    {
        //把世界坐標轉化為屏幕坐標
        Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, world);

        // 屏幕坐標轉換為局部坐標
        //out的是vector2類型,事先聲明
        Vector2 localPoint;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.GetComponent<RectTransform>(),
                                            screenPoint, canvas.worldCamera, out localPoint);
        return localPoint;
    }
}

6、由獲取到的鏤空矩形四個角的局部坐標,計算鏤空區域的中心點,賦值給material材質,在賦值之前,要對聲明的材質變量,賦值

  給材質賦值的時候要用它實際的名字_Center,而不是顯示的名字Center

  private void Start()
    {
        // 獲取材質
        material = transform.GetComponent<Image>().material;
        //如果沒有獲取到材質,就拋出異常
        if(material == null)
        {
            throw new System.Exception("為獲取到材質");
        }
    }
//計算中心點
        center.x = targetCorners[0].x + (targetCorners[3].x - targetCorners[0].x) / 2;
        center.y = targetCorners[0].y + (targetCorners[1].y - targetCorners[0].y) / 2;
        //設置材質的中心點
        material.SetVector("_Center", center);

完整代碼:改動組件的值,鏤空區域的中心點能夠跟隨移動

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class RectGuide : MonoBehaviour
{
    private Material material;  // 材質
    private Vector3 center;     // 鏤空區域的中心
    private float width;        // 鏤空區域的寬
    private float height;       // 鏤空區域的高

    private RectTransform target;// 要顯示的目標,通過目標計算鏤空區域的中心,寬高
    public Vector3[] targetCorners = new Vector3[4];//存儲要鏤空組件的四個角的數組

    private void Start()
    {
        // 獲取材質
        material = transform.GetComponent<Image>().material;
        //如果沒有獲取到材質,就拋出異常
        if(material == null)
        {
            throw new System.Exception("為獲取到材質");
        }
    }
    // 引導
    public void Guide(Canvas canvas, RectTransform target)
    {
        this.target = target;  // 將傳進來的目標組件賦值給target

        // 獲取中心點
        // GetWorldCorners:在世界空間中得到計算的矩形的角。參數角的數組
        target.GetWorldCorners(targetCorners);

        // 講四個角的世界坐標轉為局部坐標坐標
        for (int i = 0; i < targetCorners.Length; i++)
        {
            targetCorners[i] = WorldToScreenPoint(canvas, targetCorners[i]);
        }

        //計算中心點
        center.x = targetCorners[0].x + (targetCorners[3].x - targetCorners[0].x) / 2;
        center.y = targetCorners[0].y + (targetCorners[1].y - targetCorners[0].y) / 2;
        //設置材質的中心點
        material.SetVector("_Center", center);

    }

    public Vector2 WorldToScreenPoint(Canvas canvas,Vector3 world)
    {
        //把世界坐標轉化為屏幕坐標
        Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, world);

        // 屏幕坐標轉換為局部坐標
        //out的是vector2類型,事先聲明
        Vector2 localPoint;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.GetComponent<RectTransform>(),
                                            screenPoint, canvas.worldCamera, out localPoint);
        return localPoint;
    }

    private void Update()
    {
        Guide(GameObject.Find("Canvas").GetComponent<Canvas>(), GameObject.Find("Button").GetComponent<RectTransform>());
    }
}

 7、計算鏤空區域的寬和高

// 計算寬高
        width = targetCorners[3].x - targetCorners[0].x;
        height = targetCorners[1].y - targetCorners[0].y;
        //設置材質的寬高
        material.SetFloat("_SliderX", width);
        material.SetFloat("_SliderY", height);

這個后運行,寬高會比實際的大,所以將獲得的寬高/2

// 計算寬高
        width = (targetCorners[3].x - targetCorners[0].x)/2;
        height = (targetCorners[1].y - targetCorners[0].y)/2;
        //設置材質的寬高
        material.SetFloat("_SliderX", width);
        material.SetFloat("_SliderY", height);

到此完成的效果就是,鏤空區域能夠能隨button組件移動,寬高、中心都會跟隨button組件,但是還不能點擊,並且也不能有動畫

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class RectGuide : MonoBehaviour
{
    private Material material;  // 材質
    private Vector3 center;     // 鏤空區域的中心
    private float width;        // 鏤空區域的寬
    private float height;       // 鏤空區域的高

    private RectTransform target;// 要顯示的目標,通過目標計算鏤空區域的中心,寬高
    private Vector3[] targetCorners = new Vector3[4];//存儲要鏤空組件的四個角的數組

    private void Start()
    {
        // 獲取材質
        material = transform.GetComponent<Image>().material;
        //如果沒有獲取到材質,就拋出異常
        if (material == null)
        {
            throw new System.Exception("為獲取到材質");
        }
    }
    // 引導
    public void Guide(Canvas canvas, RectTransform target)
    {
        this.target = target;  // 將傳進來的目標組件賦值給target

        // 獲取中心點
        // GetWorldCorners:在世界空間中得到計算的矩形的角。參數角的數組
        target.GetWorldCorners(targetCorners);

        // 講四個角的世界坐標轉為局部坐標坐標
        for (int i = 0; i < targetCorners.Length; i++)
        {
            targetCorners[i] = WorldToScreenPoint(canvas, targetCorners[i]);
        }

        //計算中心點
        center.x = targetCorners[0].x + (targetCorners[3].x - targetCorners[0].x) / 2;
        center.y = targetCorners[0].y + (targetCorners[1].y - targetCorners[0].y) / 2;
        //設置材質的中心點
        material.SetVector("_Center", center);
        
        // 計算寬高
        width = (targetCorners[3].x - targetCorners[0].x)/2;
        height = (targetCorners[1].y - targetCorners[0].y)/2;
        //設置材質的寬高
        material.SetFloat("_SliderX", width);
        material.SetFloat("_SliderY", height);

    }

    public Vector2 WorldToScreenPoint(Canvas canvas, Vector3 world)
    {
        //把世界坐標轉化為屏幕坐標
        Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, world);

        // 屏幕坐標轉換為局部坐標
        //out的是vector2類型,事先聲明
        Vector2 localPoint;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.GetComponent<RectTransform>(),
                                            screenPoint, canvas.worldCamera, out localPoint);
        return localPoint;
    }

    private void Update()
    {
        Guide(GameObject.Find("Canvas").GetComponent<Canvas>(), GameObject.Find("Button").GetComponent<RectTransform>());
    }
}

二、圓形鏤空

圓形和矩形大部分代碼都是一樣的,就是矩形要計算寬高,而圓形要計算半徑,所以可以創建一個基類GuideBase。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GuideBase : MonoBehaviour
{
    // 將私有的變量,變成protect,這樣才可以繼承
    protected Material material;  // 材質
    protected Vector3 center;     // 鏤空區域的中心


    protected RectTransform target;// 要顯示的目標,通過目標計算鏤空區域的中心,寬高
    protected Vector3[] targetCorners = new Vector3[4];//存儲要鏤空組件的四個角的數組

    protected virtual void Start()
    {
        // 獲取材質
        material = transform.GetComponent<Image>().material;
        //如果沒有獲取到材質,就拋出異常
        if (material == null)
        {
            throw new System.Exception("為獲取到材質");
        }
    }
    // 引導
    public virtual void Guide(Canvas canvas, RectTransform target)
    {
        this.target = target;  // 將傳進來的目標組件賦值給target

        // 獲取中心點
        // GetWorldCorners:在世界空間中得到計算的矩形的角。參數角的數組
        target.GetWorldCorners(targetCorners);

        // 講四個角的世界坐標轉為局部坐標坐標
        for (int i = 0; i < targetCorners.Length; i++)
        {
            targetCorners[i] = WorldToScreenPoint(canvas, targetCorners[i]);
        }

        //計算中心點
        center.x = targetCorners[0].x + (targetCorners[3].x - targetCorners[0].x) / 2;
        center.y = targetCorners[0].y + (targetCorners[1].y - targetCorners[0].y) / 2;
        //設置材質的中心點
        material.SetVector("_Center", center);

    }

    // 功能性的方法,不需要重寫,所以不需要創建成虛方法
    public Vector2 WorldToScreenPoint(Canvas canvas, Vector3 world)
    {
        //把世界坐標轉化為屏幕坐標
        Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, world);

        // 屏幕坐標轉換為局部坐標
        //out的是vector2類型,事先聲明
        Vector2 localPoint;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.GetComponent<RectTransform>(),
                                            screenPoint, canvas.worldCamera, out localPoint);
        return localPoint;
    }
  
}

繼承GuideBase后的RectGuide腳本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class RectGuide : GuideBase
{
    
    private float width;        // 鏤空區域的寬
    private float height;       // 鏤空區域的高


    // 引導
    public override void Guide(Canvas canvas, RectTransform target)
    {
        //調用下base,中的方法
        base.Guide(canvas,target);
        // 中心點的計算在base.Guide(canvas,target)有了,
        // 計算寬高
        width = (targetCorners[3].x - targetCorners[0].x)/2;
        height = (targetCorners[1].y - targetCorners[0].y)/2;
        //設置材質的寬高
        material.SetFloat("_SliderX", width);
        material.SetFloat("_SliderY", height);

    }

    private void Update()
    {
        Guide(GameObject.Find("Canvas").GetComponent<Canvas>(), GameObject.Find("Button").GetComponent<RectTransform>());
    }
}

繼承GuideBase后的CircleGuide腳本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CircleGuide : GuideBase
{
    private float r; // 鏤空區域圓形的半徑
    public override void Guide(Canvas canvas, RectTransform target)
    {
        base.Guide(canvas, target);
        float width = (targetCorners[3].x - targetCorners[0].x) / 2;
        float height = (targetCorners[1].y - targetCorners[0].y) / 2;
        //計算半徑,寬/2的平方 + 高/2的平方,之后開方
        r = Mathf.Sqrt(width * width + height * height);
        // 賦值給材質
        material.SetFloat("_Slider", r);
    }

    // Update is called once per frame
    void Update()
    {
        Guide(GameObject.Find("Canvas").GetComponent<Canvas>(), GameObject.Find("Button").GetComponent<RectTransform>());
    }
}

將image的材質換成CircleMateri,將CircleGuide腳本掛載到Image上,運行。

 三、新手引導的方法封裝

1、在GuideBase中加上[RequireComponent(typeof(Image))],保證有image組件,而且移除不了。

   材質的初始化,不在start中了。在Guide方法中(這點也沒搞懂

 2、創建GuideController腳本

  • 創建枚舉,里面可以選擇引導的類型(Rect或者Circle)
  • 需要保證有CircleGuide、RectGuide組件(自己創建的矩形鏤空和圓形鏤空,在這里里面可以將update測試代碼注釋了
  • 創建變量包括(矩形引導腳本組件、圓形引導組件、矩形材質、圓形材質(根據情況,自己選擇需要的材質,就不用自己手動拖到Image組件的Material上了),Image組件
  • 初始化,獲得(引導頁面)的圓形、矩形組件、image組件
  • 創建方法Guide(參數:canvas\鏤空組件\引導類型),在這里用switch
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

// 枚舉,引導的類型
public enum GuideType
{
    Rect,
    Circle
}
//組件:需要的組件將會自動被添加到game object(游戲物體)。上
[RequireComponent(typeof(CircleGuide))]
[RequireComponent(typeof(RectGuide))]
public class GuideController : MonoBehaviour
{
    //
    private CircleGuide circleGuide;
    private RectGuide rectGuide;
    //材質不一樣,所以創建兩個材質
    [SerializeField]
    private Material rectMat;
    [SerializeField]
    private Material circleMat;

    //需要image,
    private Image mask;
    //
    private void Awake()
    {
        
        mask = transform.GetComponent<Image>();
        if (rectMat==null || circleMat==null)
        {
            throw new System.Exception("材質未賦值");
        }
        circleGuide = transform.GetComponent<CircleGuide>();
        rectGuide = transform.GetComponent<RectGuide>();
    }

    public void Guide(Canvas canvas,RectTransform target,GuideType guideType)
    {
        // TODO
        switch (guideType)
        {
            case GuideType.Rect:
                mask.material = rectMat;
                rectGuide.Guide(canvas, target);
                break;
            case GuideType.Circle:
                mask.material = circleMat;
                circleGuide.Guide(canvas, target);
                break;
            
        }

    }
}

3、創建GuidePanel腳本

  獲取canvas、GuideController guideController、調用guideController中的Guide方法,傳入參數,2秒后切換button_cir組件鏤空

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GuidePanel : MonoBehaviour
{
    GuideController guideController;
    Canvas canvas;

    private void Awake()
    {
        canvas = GetComponentInParent<Canvas>();
    }
    // Start is called before the first frame update
    void Start()
    {
        guideController = GetComponent<GuideController>();
        guideController.Guide(canvas, GameObject.Find("Button").GetComponent<RectTransform>(),GuideType.Rect);
        Invoke("test", 2);
    }

    void test()
    {
        guideController.Guide(canvas, GameObject.Find("Button_cir").GetComponent<RectTransform>(), GuideType.Circle);
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

4、創建頁面,將GuideController腳本、GuidePanel腳本掛上,記得更改image組件的大小、顏色、透明度

四、事件滲透

問題:現在雖然鏤空,但是按鈕不能點擊

1、給需要能點擊的UI控件上綁定,實現一個接口ICanvasRaycastFilter

在方法IsRaycastLocationValid中判斷當前點擊的位置是否符合響應事件的條件,return true事件不能滲透,false能滲透

應用場景:

1.引導挖洞

2.ui事件觸發,並且不影響下面的其他控件的事件響應

 2、RectTransformUtility.RectangleContainsScreenPoint(target, sp);矩形區域包不包含鼠標點擊的點

3、在GuideController腳本中改

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

// 枚舉,引導的類型
public enum GuideType
{
    Rect,
    Circle
}
//組件:需要的組件將會自動被添加到game object(游戲物體)。上
[RequireComponent(typeof(CircleGuide))]
[RequireComponent(typeof(RectGuide))]
public class GuideController : MonoBehaviour,ICanvasRaycastFilter
{
    //
    private CircleGuide circleGuide;
    private RectGuide rectGuide;
    //材質不一樣,所以創建兩個材質
    [SerializeField]
    private Material rectMat;
    [SerializeField]
    private Material circleMat;

    //需要image,
    private Image mask;

    private RectTransform target;
    //
    private void Awake()
    {

        mask = transform.GetComponent<Image>();
        if (rectMat==null || circleMat==null)
        {
            throw new System.Exception("材質未賦值");
        }
        circleGuide = transform.GetComponent<CircleGuide>();
        rectGuide = transform.GetComponent<RectGuide>();
    }

    public void Guide(Canvas canvas,RectTransform target,GuideType guideType)
    {
        //引導的時候,將傳入的target賦值
        this.target = target;
        // TODO
        switch (guideType)
        {
            case GuideType.Rect:
                mask.material = rectMat;
                rectGuide.Guide(canvas, target);
                break;
            case GuideType.Circle:
                mask.material = circleMat;
                circleGuide.Guide(canvas, target);
                break;
            
        }

    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="sp">鼠標點的點(屏幕坐標),看看在不在鏤空區域,在就把事件滲透,不在攔截</param>
    /// <param name="eventCamera"></param>
    /// <returns></returns>
    public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
    {
        if (target==null) { return true; }//點擊不了
        // 看看鼠標點擊在不在鏤空區域
        return !RectTransformUtility.RectangleContainsScreenPoint(target, sp);
    }
}

五、完善優化

1、加動畫,需要在GuideBase中寫個Guide重載函數,矩形更改寬高,圓形更改半徑,都不用在基類中寫

//寫個重載函數
    public virtual void Guide(Canvas canvas, RectTransform target,float scale,float time)
    {

    }

 

2、找到子類,重寫,在里面不用調用基類的方法,以圓形為例

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CircleGuide : GuideBase
{
    private float r; // 鏤空區域圓形的半徑

    private float scaleR; //變化之前的半徑大小

    private float timer; //計時器
    private float time; // 時間
    private bool isScaling; // 是否正在變化
    //重寫引導,沒有動畫
    public override void Guide(Canvas canvas, RectTransform target)
    {
        base.Guide(canvas, target);
        float width = (targetCorners[3].x - targetCorners[0].x) / 2;
        float height = (targetCorners[1].y - targetCorners[0].y) / 2;
        //計算半徑,寬/2的平方 + 高/2的平方,之后開方
        r = Mathf.Sqrt(width * width + height * height);
        // 賦值給材質
        material.SetFloat("_Slider", r);
    }

    //重寫引導,有動畫
    public override void Guide(Canvas canvas, RectTransform target, float scale, float time)
    {
        //base.Guide(canvas, target, scale, time);不用調用因為是空的
        this.Guide(canvas,target);
        scaleR = r * scale;
        this.material.SetFloat("_Slider", scaleR);
        this.time = time;
        isScaling = true;
        timer = 0;

    }

    private void Update()
    {
        if (isScaling)
        {
            // 每秒變化1/time,時間越短變化的就得越快
            //deltaTime時間增量,保證不同幀率移動的是一樣的
            timer += Time.deltaTime * 1 / time;
            this.material.SetFloat("_Slider", Mathf.Lerp(scaleR, r, timer)); 
            if(timer >= 1)
            {
                timer = 0;
                isScaling = false;
            }
        }
    }
    // Update is called once per frame
    //void Update()
    //{
    //    Guide(GameObject.Find("Canvas").GetComponent<Canvas>(), GameObject.Find("Button_cir").GetComponent<RectTransform>());
    //}
}

 

3、GuideController腳本中,創建重載函數

 public void Guide(Canvas canvas, RectTransform target, GuideType guideType, float scale, float time)
    {
        //引導的時候,將傳入的target賦值
        this.target = target;
        // TODO
        switch (guideType)
        {
            case GuideType.Rect:
                mask.material = rectMat;
                rectGuide.Guide(canvas, target, scale, time);
                break;
            case GuideType.Circle:
                mask.material = circleMat;
                circleGuide.Guide(canvas, target, scale, time);
                break;

        }

    }

 

4、在GuidePanel,test中調用

 void test()
    {
        //guideController.Guide(canvas, GameObject.Find("Button_cir").GetComponent<RectTransform>(), GuideType.Circle);
        guideController.Guide(canvas, GameObject.Find("Button_cir").GetComponent<RectTransform>(), GuideType.Circle,2,0.5f);
    }

 

5、矩形類似,可以用繼承,但是我沒有用,暈,直接按照circle格式寫的,類似,到此RectGuide全部代碼

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class RectGuide : GuideBase
{
    
    private float width;        // 鏤空區域的寬
    private float height;       // 鏤空區域的高
    private float scaleW; //變化之前的寬
    private float scaleH; //變化之前的高

    private float timer; //計時器
    private float time; // 時間
    private bool isScaling; // 是否正在變化

    // 引導
    public override void Guide(Canvas canvas, RectTransform target)
    {
        //調用下base,中的方法
        base.Guide(canvas,target);
        // 中心點的計算在base.Guide(canvas,target)有了,
        // 計算寬高
        width = (targetCorners[3].x - targetCorners[0].x)/2;
        height = (targetCorners[1].y - targetCorners[0].y)/2;
        //設置材質的寬高
        material.SetFloat("_SliderX", width);
        material.SetFloat("_SliderY", height);

    }
    //重寫引導,有動畫
    public override void Guide(Canvas canvas, RectTransform target, float scale, float time)
    {
        //base.Guide(canvas, target, scale, time);不用調用因為是空的
        this.Guide(canvas, target);
        scaleW = width * scale;
        scaleH = height * scale;
        this.material.SetFloat("_SliderX", scaleW);
        this.material.SetFloat("_SliderY", scaleH);
        this.time = time;
        isScaling = true;
        timer = 0;

    }
    private void Update()
    {
        if (isScaling)
        {
            // 每秒變化1/time,時間越短變化的就得越快
            //deltaTime時間增量,保證不同幀率移動的是一樣的
            timer += Time.deltaTime * 1 / time;
            this.material.SetFloat("_SliderX", Mathf.Lerp(scaleW, width, timer));
            this.material.SetFloat("_SliderY", Mathf.Lerp(scaleH, height, timer));
            if (timer >= 1)
            {
                timer = 0;
                isScaling = false;
            }
        }
    }
    //private void Update()
    //{
    //    Guide(GameObject.Find("Canvas").GetComponent<Canvas>(), GameObject.Find("Button").GetComponent<RectTransform>());
    //}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

// 枚舉,引導的類型
public enum GuideType
{
    Rect,
    Circle
}
//組件:需要的組件將會自動被添加到game object(游戲物體)。上
[RequireComponent(typeof(CircleGuide))]
[RequireComponent(typeof(RectGuide))]
public class GuideController : MonoBehaviour,ICanvasRaycastFilter
{
    //
    private CircleGuide circleGuide;
    private RectGuide rectGuide;
    //材質不一樣,所以創建兩個材質
    [SerializeField]
    private Material rectMat;
    [SerializeField]
    private Material circleMat;

    //需要image,
    private Image mask;

    private RectTransform target;
    //
    private void Awake()
    {

        mask = transform.GetComponent<Image>();
        if (rectMat==null || circleMat==null)
        {
            throw new System.Exception("材質未賦值");
        }
        circleGuide = transform.GetComponent<CircleGuide>();
        rectGuide = transform.GetComponent<RectGuide>();
    }

    public void Guide(Canvas canvas, RectTransform target, GuideType guideType)
    {
        //引導的時候,將傳入的target賦值
        this.target = target;
        // TODO
        switch (guideType)
        {
            case GuideType.Rect:
                mask.material = rectMat;
                rectGuide.Guide(canvas, target);
                break;
            case GuideType.Circle:
                mask.material = circleMat;
                circleGuide.Guide(canvas, target);
                break;

        }
    }
    public void Guide(Canvas canvas, RectTransform target, GuideType guideType, float scale, float time)
    {
        //引導的時候,將傳入的target賦值
        this.target = target;
        // TODO
        switch (guideType)
        {
            case GuideType.Rect:
                mask.material = rectMat;
                rectGuide.Guide(canvas, target, scale, time);
                break;
            case GuideType.Circle:
                mask.material = circleMat;
                circleGuide.Guide(canvas, target, scale, time);
                break;

        }

    }



    /// <summary>
    /// 
    /// </summary>
    /// <param name="sp">鼠標點的點(屏幕坐標),看看在不在鏤空區域,在就把事件滲透,不在攔截</param>
    /// <param name="eventCamera"></param>
    /// <returns></returns>
    public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
    {
        if (target==null) { return true; }//點擊不了
        // 看看鼠標點擊在不在鏤空區域
        return !RectTransformUtility.RectangleContainsScreenPoint(target, sp);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GuidePanel : MonoBehaviour
{
    GuideController guideController;
    Canvas canvas;

    private void Awake()
    {
        canvas = GetComponentInParent<Canvas>();
    }
    // Start is called before the first frame update
    void Start()
    {
        guideController = GetComponent<GuideController>();
        //guideController.Guide(canvas, GameObject.Find("Button").GetComponent<RectTransform>(),GuideType.Rect);
        guideController.Guide(canvas, GameObject.Find("Button").GetComponent<RectTransform>(),GuideType.Rect,3,1.5f);
        Invoke("test", 2);
    }

    void test()
    {
        //guideController.Guide(canvas, GameObject.Find("Button_cir").GetComponent<RectTransform>(), GuideType.Circle);
        guideController.Guide(canvas, GameObject.Find("Button_cir").GetComponent<RectTransform>(), GuideType.Circle,3,1.0f);
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

鏈接:https://pan.baidu.com/s/1BzGGotE8imnp1nYo9T4pAQ
提取碼:uvf1
復制這段內容后打開百度網盤手機App,操作更方便哦


免責聲明!

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



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