前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >unity--实现新手引导功能 一:矩形镂空功能 三、新手引导的方法封装四、事件渗透五、完善优化

unity--实现新手引导功能 一:矩形镂空功能 三、新手引导的方法封装四、事件渗透五、完善优化

作者头像
酱紫安
发布2021-03-16 14:55:54
5.3K0
发布2021-03-16 14:55:54
举报
文章被收录于专栏:python学习路

一:矩形镂空功能

1、新建一个场景,创建两个按钮,一个Image

2、导入shader,创建两个材质,将两个shader拖到两个材质上。将材质拖动到Image组件的Material上。

3、创建脚本RectGuide,创建一个方法Guide(参数:Canvas(为了将世界转换屏幕坐标提供需要的Camera,target(要镂空的组件)),测试一下

  • GetWorldCorners:在世界空间中得到计算的矩形的角。参数角的数组
  • WorldToScreenPoint参数:camera(通过canvas传入),vector3(世界坐标)
代码语言:javascript
复制
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 ---- 输出的局部坐标
代码语言:javascript
复制
// 讲四个角的世界坐标转为屏幕坐标
        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、将上述代码封装成一个方法,完整的为:

代码语言:javascript
复制
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

代码语言:javascript
复制
  private void Start()
    {
        // 获取材质
        material = transform.GetComponent<Image>().material;
        //如果没有获取到材质,就抛出异常
        if(material == null)
        {
            throw new System.Exception("为获取到材质");
        }
    }
代码语言:javascript
复制
//计算中心点
        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);

完整代码:改动组件的值,镂空区域的中心点能够跟随移动

代码语言:javascript
复制
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、计算镂空区域的宽和高

代码语言:javascript
复制
// 计算宽高
        width = targetCorners[3].x - targetCorners[0].x;
        height = targetCorners[1].y - targetCorners[0].y;
        //设置材质的宽高
        material.SetFloat("_SliderX", width);
        material.SetFloat("_SliderY", height);

这个后运行,宽高会比实际的大,所以将获得的宽高/2

代码语言:javascript
复制
// 计算宽高
        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组件,但是还不能点击,并且也不能有动画

代码语言:javascript
复制
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。

代码语言:javascript
复制
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脚本

代码语言:javascript
复制
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脚本

代码语言:javascript
复制
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
代码语言:javascript
复制
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组件镂空

代码语言:javascript
复制
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脚本中改

代码语言:javascript
复制
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重载函数,矩形更改宽高,圆形更改半径,都不用在基类中写

代码语言:javascript
复制
//写个重载函数
    public virtual void Guide(Canvas canvas, RectTransform target,float scale,float time)
    {

    }

2、找到子类,重写,在里面不用调用基类的方法,以圆形为例

代码语言:javascript
复制
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脚本中,创建重载函数

代码语言:javascript
复制
 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中调用

代码语言:javascript
复制
 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全部代码

代码语言:javascript
复制
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>());
    //}
}
代码语言:javascript
复制
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);
    }
}
代码语言:javascript
复制
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,操作更方便哦

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一:矩形镂空功能
  • 三、新手引导的方法封装
  • 四、事件渗透
  • 五、完善优化
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档