unity--实现新手引导功能
一:矩形鏤空功能
1、新建一個(gè)場(chǎng)景,創(chuàng)建兩個(gè)按鈕,一個(gè)Image
2、導(dǎo)入shader,創(chuàng)建兩個(gè)材質(zhì),將兩個(gè)shader拖到兩個(gè)材質(zhì)上。將材質(zhì)拖動(dòng)到Image組件的Material上。
3、創(chuàng)建腳本RectGuide,創(chuàng)建一個(gè)方法Guide(參數(shù):Canvas(為了將世界轉(zhuǎn)換屏幕坐標(biāo)提供需要的Camera,target(要鏤空的組件)),測(cè)試一下
GetWorldCorners:在世界空間中得到計(jì)算的矩形的角。參數(shù)角的數(shù)組
WorldToScreenPoint參數(shù):camera(通過canvas傳入),vector3(世界坐標(biāo))
using Sytem.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RectGuide : MonoBehaviour
{
private Material material; // 材質(zhì)
private Vector3 center; // 鏤空區(qū)域的中心
private float width; // 鏤空區(qū)域的寬
private float height; // 鏤空區(qū)域的高
private RectTransform target;// 要顯示的目標(biāo),通過目標(biāo)計(jì)算鏤空區(qū)域的中心,寬高
public Vector3[] targetCorners = new Vector3[4];//存儲(chǔ)要鏤空組件的四個(gè)角的數(shù)組
// 引導(dǎo)
public void Guide(Canvas canvas, RectTransform target)
{
this.target = target; // 將傳進(jìn)來的目標(biāo)組件賦值給target
// 獲取中心點(diǎn)
// GetWorldCorners:在世界空間中得到計(jì)算的矩形的角。參數(shù)角的數(shù)組
target.GetWorldCorners(targetCorners);
// 講四個(gè)角的世界坐標(biāo)轉(zhuǎn)為屏幕坐標(biāo)
for (int i = 0; i < targetCorners.Length; i++)
{
// WorldToScreenPoint參數(shù):camera(通過canvas傳入),vector3(世界坐標(biāo))
targetCorners[i]=RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, targetCorners[i]);
}
}
private void Update()
{
Guide(GameObject.Find("Canvas").GetComponent<Canvas>(),GameObject.Find("Button").GetComponent<RectTransform>());
}
}
問題:shader的坐標(biāo)的原點(diǎn)是中心,而轉(zhuǎn)換的屏幕坐標(biāo)的原點(diǎn)是左下角,所以需要轉(zhuǎn)化
4、RectTransformUtility.ScreenPointToLocalPointInRectangle:屏幕坐標(biāo)轉(zhuǎn)換為局部坐標(biāo)
參數(shù):
RectTransform rect ------轉(zhuǎn)換為誰的局部坐標(biāo)
Vector2 screenPoint ------要轉(zhuǎn)換的屏幕坐標(biāo)
Camera cam, ------相機(jī)
out Vector2 localPoint ---- 輸出的局部坐標(biāo)
// 講四個(gè)角的世界坐標(biāo)轉(zhuǎn)為屏幕坐標(biāo)
for (int i = 0; i < targetCorners.Length; i++)
{
// WorldToScreenPoint參數(shù):camera(通過canvas傳入),vector3(世界坐標(biāo))
targetCorners[i]=RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, targetCorners[i]);
// 屏幕坐標(biāo)轉(zhuǎn)換為局部坐標(biāo)
//out的是vector2類型,事先聲明
Vector2 localPoint;
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.GetComponent<RectTransform>(),
targetCorners[i], canvas.worldCamera, out localPoint);
targetCorners[i] = localPoint;
}
5、將上述代碼封裝成一個(gè)方法,完整的為:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RectGuide : MonoBehaviour
{
private Material material; // 材質(zhì)
private Vector3 center; // 鏤空區(qū)域的中心
private float width; // 鏤空區(qū)域的寬
private float height; // 鏤空區(qū)域的高
private RectTransform target;// 要顯示的目標(biāo),通過目標(biāo)計(jì)算鏤空區(qū)域的中心,寬高
public Vector3[] targetCorners = new Vector3[4];//存儲(chǔ)要鏤空組件的四個(gè)角的數(shù)組
// 引導(dǎo)
public void Guide(Canvas canvas, RectTransform target)
{
this.target = target; // 將傳進(jìn)來的目標(biāo)組件賦值給target
// 獲取中心點(diǎn)
// GetWorldCorners:在世界空間中得到計(jì)算的矩形的角。參數(shù)角的數(shù)組
target.GetWorldCorners(targetCorners);
// 講四個(gè)角的世界坐標(biāo)轉(zhuǎn)為局部坐標(biāo)坐標(biāo)
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)
{
//把世界坐標(biāo)轉(zhuǎn)化為屏幕坐標(biāo)
Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, world);
// 屏幕坐標(biāo)轉(zhuǎn)換為局部坐標(biāo)
//out的是vector2類型,事先聲明
Vector2 localPoint;
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.GetComponent<RectTransform>(),
screenPoint, canvas.worldCamera, out localPoint);
return localPoint;
}
}
6、由獲取到的鏤空矩形四個(gè)角的局部坐標(biāo),計(jì)算鏤空區(qū)域的中心點(diǎn),賦值給material材質(zhì),在賦值之前,要對(duì)聲明的材質(zhì)變量,賦值
給材質(zhì)賦值的時(shí)候要用它實(shí)際的名字_Center,而不是顯示的名字Center
private void Start()
{
// 獲取材質(zhì)
material = transform.GetComponent<Image>().material;
//如果沒有獲取到材質(zhì),就拋出異常
if(material == null)
{
throw new System.Exception("為獲取到材質(zhì)");
}
}
//計(jì)算中心點(diǎn)
center.x = targetCorners[0].x + (targetCorners[3].x - targetCorners[0].x) / 2;
center.y = targetCorners[0].y + (targetCorners[1].y - targetCorners[0].y) / 2;
//設(shè)置材質(zhì)的中心點(diǎn)
material.SetVector("_Center", center);
完整代碼:改動(dòng)組件的值,鏤空區(qū)域的中心點(diǎn)能夠跟隨移動(dòng)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RectGuide : MonoBehaviour
{
private Material material; // 材質(zhì)
private Vector3 center; // 鏤空區(qū)域的中心
private float width; // 鏤空區(qū)域的寬
private float height; // 鏤空區(qū)域的高
private RectTransform target;// 要顯示的目標(biāo),通過目標(biāo)計(jì)算鏤空區(qū)域的中心,寬高
public Vector3[] targetCorners = new Vector3[4];//存儲(chǔ)要鏤空組件的四個(gè)角的數(shù)組
private void Start()
{
// 獲取材質(zhì)
material = transform.GetComponent<Image>().material;
//如果沒有獲取到材質(zhì),就拋出異常
if(material == null)
{
throw new System.Exception("為獲取到材質(zhì)");
}
}
// 引導(dǎo)
public void Guide(Canvas canvas, RectTransform target)
{
this.target = target; // 將傳進(jìn)來的目標(biāo)組件賦值給target
// 獲取中心點(diǎn)
// GetWorldCorners:在世界空間中得到計(jì)算的矩形的角。參數(shù)角的數(shù)組
target.GetWorldCorners(targetCorners);
// 講四個(gè)角的世界坐標(biāo)轉(zhuǎn)為局部坐標(biāo)坐標(biāo)
for (int i = 0; i < targetCorners.Length; i++)
{
targetCorners[i] = WorldToScreenPoint(canvas, targetCorners[i]);
}
//計(jì)算中心點(diǎn)
center.x = targetCorners[0].x + (targetCorners[3].x - targetCorners[0].x) / 2;
center.y = targetCorners[0].y + (targetCorners[1].y - targetCorners[0].y) / 2;
//設(shè)置材質(zhì)的中心點(diǎn)
material.SetVector("_Center", center);
}
public Vector2 WorldToScreenPoint(Canvas canvas,Vector3 world)
{
//把世界坐標(biāo)轉(zhuǎn)化為屏幕坐標(biāo)
Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, world);
// 屏幕坐標(biāo)轉(zhuǎn)換為局部坐標(biāo)
//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、計(jì)算鏤空區(qū)域的寬和高
// 計(jì)算寬高
width = targetCorners[3].x - targetCorners[0].x;
height = targetCorners[1].y - targetCorners[0].y;
//設(shè)置材質(zhì)的寬高
material.SetFloat("_SliderX", width);
material.SetFloat("_SliderY", height);
這個(gè)后運(yùn)行,寬高會(huì)比實(shí)際的大,所以將獲得的寬高/2
// 計(jì)算寬高
width = (targetCorners[3].x - targetCorners[0].x)/2;
height = (targetCorners[1].y - targetCorners[0].y)/2;
//設(shè)置材質(zhì)的寬高
material.SetFloat("_SliderX", width);
material.SetFloat("_SliderY", height);
到此完成的效果就是,鏤空區(qū)域能夠能隨button組件移動(dòng),寬高、中心都會(huì)跟隨button組件,但是還不能點(diǎn)擊,并且也不能有動(dòng)畫
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RectGuide : MonoBehaviour
{
private Material material; // 材質(zhì)
private Vector3 center; // 鏤空區(qū)域的中心
private float width; // 鏤空區(qū)域的寬
private float height; // 鏤空區(qū)域的高
private RectTransform target;// 要顯示的目標(biāo),通過目標(biāo)計(jì)算鏤空區(qū)域的中心,寬高
private Vector3[] targetCorners = new Vector3[4];//存儲(chǔ)要鏤空組件的四個(gè)角的數(shù)組
private void Start()
{
// 獲取材質(zhì)
material = transform.GetComponent<Image>().material;
//如果沒有獲取到材質(zhì),就拋出異常
if (material == null)
{
throw new System.Exception("為獲取到材質(zhì)");
}
}
// 引導(dǎo)
public void Guide(Canvas canvas, RectTransform target)
{
this.target = target; // 將傳進(jìn)來的目標(biāo)組件賦值給target
// 獲取中心點(diǎn)
// GetWorldCorners:在世界空間中得到計(jì)算的矩形的角。參數(shù)角的數(shù)組
target.GetWorldCorners(targetCorners);
// 講四個(gè)角的世界坐標(biāo)轉(zhuǎn)為局部坐標(biāo)坐標(biāo)
for (int i = 0; i < targetCorners.Length; i++)
{
targetCorners[i] = WorldToScreenPoint(canvas, targetCorners[i]);
}
//計(jì)算中心點(diǎn)
center.x = targetCorners[0].x + (targetCorners[3].x - targetCorners[0].x) / 2;
center.y = targetCorners[0].y + (targetCorners[1].y - targetCorners[0].y) / 2;
//設(shè)置材質(zhì)的中心點(diǎn)
material.SetVector("_Center", center);
// 計(jì)算寬高
width = (targetCorners[3].x - targetCorners[0].x)/2;
height = (targetCorners[1].y - targetCorners[0].y)/2;
//設(shè)置材質(zhì)的寬高
material.SetFloat("_SliderX", width);
material.SetFloat("_SliderY", height);
}
public Vector2 WorldToScreenPoint(Canvas canvas, Vector3 world)
{
//把世界坐標(biāo)轉(zhuǎn)化為屏幕坐標(biāo)
Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, world);
// 屏幕坐標(biāo)轉(zhuǎn)換為局部坐標(biāo)
//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>());
}
}
二、圓形鏤空
圓形和矩形大部分代碼都是一樣的,就是矩形要計(jì)算寬高,而圓形要計(jì)算半徑,所以可以創(chuàng)建一個(gè)基類GuideBase。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GuideBase : MonoBehaviour
{
// 將私有的變量,變成protect,這樣才可以繼承
protected Material material; // 材質(zhì)
protected Vector3 center; // 鏤空區(qū)域的中心
protected RectTransform target;// 要顯示的目標(biāo),通過目標(biāo)計(jì)算鏤空區(qū)域的中心,寬高
protected Vector3[] targetCorners = new Vector3[4];//存儲(chǔ)要鏤空組件的四個(gè)角的數(shù)組
protected virtual void Start()
{
// 獲取材質(zhì)
material = transform.GetComponent<Image>().material;
//如果沒有獲取到材質(zhì),就拋出異常
if (material == null)
{
throw new System.Exception("為獲取到材質(zhì)");
}
}
// 引導(dǎo)
public virtual void Guide(Canvas canvas, RectTransform target)
{
this.target = target; // 將傳進(jìn)來的目標(biāo)組件賦值給target
// 獲取中心點(diǎn)
// GetWorldCorners:在世界空間中得到計(jì)算的矩形的角。參數(shù)角的數(shù)組
target.GetWorldCorners(targetCorners);
// 講四個(gè)角的世界坐標(biāo)轉(zhuǎn)為局部坐標(biāo)坐標(biāo)
for (int i = 0; i < targetCorners.Length; i++)
{
targetCorners[i] = WorldToScreenPoint(canvas, targetCorners[i]);
}
//計(jì)算中心點(diǎn)
center.x = targetCorners[0].x + (targetCorners[3].x - targetCorners[0].x) / 2;
center.y = targetCorners[0].y + (targetCorners[1].y - targetCorners[0].y) / 2;
//設(shè)置材質(zhì)的中心點(diǎn)
material.SetVector("_Center", center);
}
// 功能性的方法,不需要重寫,所以不需要?jiǎng)?chuàng)建成虛方法
public Vector2 WorldToScreenPoint(Canvas canvas, Vector3 world)
{
//把世界坐標(biāo)轉(zhuǎn)化為屏幕坐標(biāo)
Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, world);
// 屏幕坐標(biāo)轉(zhuǎn)換為局部坐標(biāo)
//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; // 鏤空區(qū)域的寬
private float height; // 鏤空區(qū)域的高
// 引導(dǎo)
public override void Guide(Canvas canvas, RectTransform target)
{
//調(diào)用下base,中的方法
base.Guide(canvas,target);
// 中心點(diǎn)的計(jì)算在base.Guide(canvas,target)有了,
// 計(jì)算寬高
width = (targetCorners[3].x - targetCorners[0].x)/2;
height = (targetCorners[1].y - targetCorners[0].y)/2;
//設(shè)置材質(zhì)的寬高
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; // 鏤空區(qū)域圓形的半徑
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;
//計(jì)算半徑,寬/2的平方 + 高/2的平方,之后開方
r = Mathf.Sqrt(width * width + height * height);
// 賦值給材質(zhì)
material.SetFloat("_Slider", r);
}
// Update is called once per frame
void Update()
{
Guide(GameObject.Find("Canvas").GetComponent<Canvas>(), GameObject.Find("Button").GetComponent<RectTransform>());
}
}
將image的材質(zhì)換成CircleMateri,將CircleGuide腳本掛載到Image上,運(yùn)行。
三、新手引導(dǎo)的方法封裝
1、在GuideBase中加上[RequireComponent(typeof(Image))],保證有image組件,而且移除不了。
材質(zhì)的初始化,不在start中了。在Guide方法中(這點(diǎn)也沒搞懂)
2、創(chuàng)建GuideController腳本
創(chuàng)建枚舉,里面可以選擇引導(dǎo)的類型(Rect或者Circle)
需要保證有CircleGuide、RectGuide組件(自己創(chuàng)建的矩形鏤空和圓形鏤空,在這里里面可以將update測(cè)試代碼注釋了)
創(chuàng)建變量包括(矩形引導(dǎo)腳本組件、圓形引導(dǎo)組件、矩形材質(zhì)、圓形材質(zhì)(根據(jù)情況,自己選擇需要的材質(zhì),就不用自己手動(dòng)拖到Image組件的Material上了),Image組件)
初始化,獲得(引導(dǎo)頁(yè)面)的圓形、矩形組件、image組件
創(chuàng)建方法Guide(參數(shù):canvas鏤空組件引導(dǎo)類型),在這里用switch
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
// 枚舉,引導(dǎo)的類型
public enum GuideType
{
Rect,
Circle
}
//組件:需要的組件將會(huì)自動(dòng)被添加到game object(游戲物體)。上
[RequireComponent(typeof(CircleGuide))]
[RequireComponent(typeof(RectGuide))]
public class GuideController : MonoBehaviour
{
//
private CircleGuide circleGuide;
private RectGuide rectGuide;
//材質(zhì)不一樣,所以創(chuàng)建兩個(gè)材質(zhì)
[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("材質(zhì)未賦值");
}
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、創(chuàng)建GuidePanel腳本
獲取canvas、GuideController guideController、調(diào)用guideController中的Guide方法,傳入?yún)?shù),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、創(chuàng)建頁(yè)面,將GuideController腳本、GuidePanel腳本掛上,記得更改image組件的大小、顏色、透明度。
四、事件滲透
問題:現(xiàn)在雖然鏤空,但是按鈕不能點(diǎn)擊
1、給需要能點(diǎn)擊的UI控件上綁定,實(shí)現(xiàn)一個(gè)接口ICanvasRaycastFilter
在方法IsRaycastLocationValid中判斷當(dāng)前點(diǎn)擊的位置是否符合響應(yīng)事件的條件,return true事件不能滲透,false能滲透
應(yīng)用場(chǎng)景:
1.引導(dǎo)挖洞
2.ui事件觸發(fā),并且不影響下面的其他控件的事件響應(yīng)
2、RectTransformUtility.RectangleContainsScreenPoint(target, sp);矩形區(qū)域包不包含鼠標(biāo)點(diǎn)擊的點(diǎn)
3、在GuideController腳本中改
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
// 枚舉,引導(dǎo)的類型
public enum GuideType
{
Rect,
Circle
}
//組件:需要的組件將會(huì)自動(dòng)被添加到game object(游戲物體)。上
[RequireComponent(typeof(CircleGuide))]
[RequireComponent(typeof(RectGuide))]
public class GuideController : MonoBehaviour,ICanvasRaycastFilter
{
//
private CircleGuide circleGuide;
private RectGuide rectGuide;
//材質(zhì)不一樣,所以創(chuàng)建兩個(gè)材質(zhì)
[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("材質(zhì)未賦值");
}
circleGuide = transform.GetComponent<CircleGuide>();
rectGuide = transform.GetComponent<RectGuide>();
}
public void Guide(Canvas canvas,RectTransform target,GuideType guideType)
{
//引導(dǎo)的時(shí)候,將傳入的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">鼠標(biāo)點(diǎn)的點(diǎn)(屏幕坐標(biāo)),看看在不在鏤空區(qū)域,在就把事件滲透,不在攔截</param>
/// <param name="eventCamera"></param>
/// <returns></returns>
public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
{
if (target==null) { return true; }//點(diǎn)擊不了
// 看看鼠標(biāo)點(diǎn)擊在不在鏤空區(qū)域
return !RectTransformUtility.RectangleContainsScreenPoint(target, sp);
}
}
五、完善優(yōu)化
1、加動(dòng)畫,需要在GuideBase中寫個(gè)Guide重載函數(shù),矩形更改寬高,圓形更改半徑,都不用在基類中寫
//寫個(gè)重載函數(shù)
public virtual void Guide(Canvas canvas, RectTransform target,float scale,float time)
{
}
2、找到子類,重寫,在里面不用調(diào)用基類的方法,以圓形為例
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CircleGuide : GuideBase
{
private float r; // 鏤空區(qū)域圓形的半徑
private float scaleR; //變化之前的半徑大小
private float timer; //計(jì)時(shí)器
private float time; // 時(shí)間
private bool isScaling; // 是否正在變化
//重寫引導(dǎo),沒有動(dòng)畫
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;
//計(jì)算半徑,寬/2的平方 + 高/2的平方,之后開方
r = Mathf.Sqrt(width * width + height * height);
// 賦值給材質(zhì)
material.SetFloat("_Slider", r);
}
//重寫引導(dǎo),有動(dòng)畫
public override void Guide(Canvas canvas, RectTransform target, float scale, float time)
{
//base.Guide(canvas, target, scale, time);不用調(diào)用因?yàn)槭强盏? 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,時(shí)間越短變化的就得越快
//deltaTime時(shí)間增量,保證不同幀率移動(dòng)的是一樣的
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腳本中,創(chuàng)建重載函數(shù)
public void Guide(Canvas canvas, RectTransform target, GuideType guideType, float scale, float time)
{
//引導(dǎo)的時(shí)候,將傳入的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中調(diào)用
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; // 鏤空區(qū)域的寬
private float height; // 鏤空區(qū)域的高
private float scaleW; //變化之前的寬
private float scaleH; //變化之前的高
private float timer; //計(jì)時(shí)器
private float time; // 時(shí)間
private bool isScaling; // 是否正在變化
// 引導(dǎo)
public override void Guide(Canvas canvas, RectTransform target)
{
//調(diào)用下base,中的方法
base.Guide(canvas,target);
// 中心點(diǎn)的計(jì)算在base.Guide(canvas,target)有了,
// 計(jì)算寬高
width = (targetCorners[3].x - targetCorners[0].x)/2;
height = (targetCorners[1].y - targetCorners[0].y)/2;
//設(shè)置材質(zhì)的寬高
material.SetFloat("_SliderX", width);
material.SetFloat("_SliderY", height);
}
//重寫引導(dǎo),有動(dòng)畫
public override void Guide(Canvas canvas, RectTransform target, float scale, float time)
{
//base.Guide(canvas, target, scale, time);不用調(diào)用因?yàn)槭强盏? 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,時(shí)間越短變化的就得越快
//deltaTime時(shí)間增量,保證不同幀率移動(dòng)的是一樣的
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;
// 枚舉,引導(dǎo)的類型
public enum GuideType
{
Rect,
Circle
}
//組件:需要的組件將會(huì)自動(dòng)被添加到game object(游戲物體)。上
[RequireComponent(typeof(CircleGuide))]
[RequireComponent(typeof(RectGuide))]
public class GuideController : MonoBehaviour,ICanvasRaycastFilter
{
//
private CircleGuide circleGuide;
private RectGuide rectGuide;
//材質(zhì)不一樣,所以創(chuàng)建兩個(gè)材質(zhì)
[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("材質(zhì)未賦值");
}
circleGuide = transform.GetComponent<CircleGuide>();
rectGuide = transform.GetComponent<RectGuide>();
}
public void Guide(Canvas canvas, RectTransform target, GuideType guideType)
{
//引導(dǎo)的時(shí)候,將傳入的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)
{
//引導(dǎo)的時(shí)候,將傳入的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">鼠標(biāo)點(diǎn)的點(diǎn)(屏幕坐標(biāo)),看看在不在鏤空區(qū)域,在就把事件滲透,不在攔截</param>
/// <param name="eventCamera"></param>
/// <returns></returns>
public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
{
if (target==null) { return true; }//點(diǎn)擊不了
// 看看鼠標(biāo)點(diǎn)擊在不在鏤空區(qū)域
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
復(fù)制這段內(nèi)容后打開百度網(wǎng)盤手機(jī)App,操作更方便哦
總結(jié)
以上是生活随笔為你收集整理的unity--实现新手引导功能的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android 弹出网格菜单,在andr
- 下一篇: %matplotlib qt5 作用