[unity]屏幕碎裂效果转场

使用的是URP项目,相机设置有所不同;该方法同样可以设置物体碎裂效果

一,制作屏幕碎片

1,新建一个立方体,设置material,按照屏幕比例设置尺寸,这里按照1920x1080设置

需要防止立方体的材质镜像,注意使用正确的面作为屏幕

2,安装Probuilder组件,并在Edit-Preference-Probuilder中打开实验特征

3,Tools-Probuilder-Probuilder Window打开probuilder窗口,选中之前创建的屏幕立方体,使用Probuilderize指令;再新建一个立方体2,厚度大于屏幕立方体,同样使用Probuilderize指令

4,如图打开Boolean Tool,将屏幕立方体1与立方体2分别拖入左侧和右侧。该窗口会将两个立方体的重合部分生成一个新的物体。

5,使用顶点选择模式,该模式可以调整拖动模型的顶点位置,可以同时使用2d模式方便选择,调整立方体2的顶点,按自己需要的设置成碎片形状,点击Boolean Tool的apply,即可按照重合部分生成碎片物体,选择生成的碎片,使用Probuilder窗口的center pivot命令设置好锚点(需要点两次)。重复以上操作直至完成所有碎片

二,预制体结构

将上面制作的碎片防在同一个父物体下,添加rigidbody,初始不使用重力。

为了体现加载进度,可以将一块完整的屏幕放在碎片下,随进度变成黑色

添加一个输出图像的相机:为了体现碎块的效果,使用的是透视相机,渲染类型是overlay,culling mask仅选择碎片所在的layer

三,主要脚本功能

1,Awake:记录每个碎片的初始相对坐标,为了在切换场景时不被销毁,使用DontDestroyOnLoad(this.gameObject);

2,SetPos:设置屏幕碎片放到输出相机前并铺满屏幕,涉及相机参数和API相关知识

3,Reset:设置碎片到初始位置和初始旋转,关闭重力影响,清零速度和角速度,重置背景颜色

4,SetCameraStack:将输出相机设置到当前场景的主相机的stack中,使之得以显示

5,ShowShot:关闭所有协程(中断没完成的协程),开启屏幕截图协程

6,ScreenShot:设置输出相机到主相机的stack中,等待此帧结束,新建一个屏幕大小的纹理,读取屏幕图像,开启输出相机,重置碎片位置,设置碎片材质的主纹理

7,ScreenExplosion:遍历每个碎片,开启重力影响,设置爆炸冲击力

8,StartColorChange:开启颜色变化协程,根据进度将背景逐渐变黑,结束后进行屏幕爆炸,等待一段时间后即可隐藏物体

public class ScreenBreaker : MonoBehaviour
{
    
    public Camera outputCamera;
    public Transform slices;
    
    public Transform bg;

    Vector3[] origLocalPos;

    private void Awake()
    {
        origLocalPos = new Vector3[slices.childCount];
        for(int i = 0; i < slices.childCount; i++)
        {
            origLocalPos[i] = slices.GetChild(i).localPosition;
        }

        SetPos();

        DontDestroyOnLoad(this.gameObject);
        


    }

    public void SetPos()
    {
        int distance = 10;

        //获取屏幕中心点世界位置,z轴设置>0
        Vector3 pos = outputCamera.ScreenToWorldPoint(new Vector3(Screen.width / 2, Screen.height / 2, distance));

        float halfFOV = outputCamera.fieldOfView * 0.5f; // 视野角度的一半
        float cameraHeight = distance * Mathf.Tan(halfFOV * Mathf.Deg2Rad); // 相机在指定距离处的高度
        float scale = cameraHeight / 5;

        //放置到输出相机前
        slices.transform.position = pos;
        slices.gameObject.transform.localScale = Vector3.one * scale;

    }
    /// <summary>
    /// 重置碎片位置并关闭重力影响
    /// </summary>
    private void Reset()
    {
        for (int i = 0; i < slices.childCount; i++)
        {
            Transform t = slices.GetChild(i);
            t.localPosition = origLocalPos[i];
            t.localRotation = Quaternion.identity;
            Rigidbody rb = t.GetComponent<Rigidbody>();
            if( rb != null )
            {
                rb.useGravity = false;
                rb.velocity = Vector3.zero;
                rb.angularVelocity = Vector3.zero;
                
            }
        }
        bg.GetComponent<Renderer>().material.color = Color.white;
        bg.gameObject.SetActive(true);
        bg.localRotation = Quaternion.Euler(0,90,0);

    }

    public void ShowShot(UnityAction callback)
    {
        StopAllCoroutines();
        StartCoroutine(ScreenShot(callback));
    }
    bool isOver = false;

    
    IEnumerator ScreenShot(UnityAction callback)
    {
        isOver = false;
        SetCameraStack(Camera.main);
        

        //等待到此帧结束,完成渲染
        yield return new WaitForEndOfFrame();

        //相机渲染纹理因ui大小与实际看起来不同,因此改成影响直接使用屏幕图像
        Texture2D tex = new Texture2D(Screen.width, Screen.height, TextureFormat.ARGB32, false);

        // 读取像素到Texture2D中
        tex.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
        tex.Apply();

        //开启显示相机
        outputCamera.gameObject.SetActive(true);

        //重置碎片位置
        Reset();

        slices.gameObject.SetActive(true);

        for(int i = 0;i< slices.childCount;i++)
        {
            slices.GetChild(i).GetComponent<Renderer>().material.mainTexture = tex;
        }

        //关闭渲染
        //shotCamera.gameObject.SetActive(false);

        progress = 0;

        callback?.Invoke();
    }
    
    public void StartColorChange(Color target)
    {

        StartCoroutine(ColorChange(target));
    }
    float progress;
    IEnumerator ColorChange(Color target)
    {
        Material m = bg.GetComponent<Renderer>().material;

        float f = 0;
        while (!isOver || f < progress)
        {
            if (f < progress)
                f += 0.01f;
            m.color = (1 - f) * Color.white + f * target;
            yield return null;
        }

        ScreenExplosion();
        onBreak?.Invoke();

        yield return new WaitForSeconds(4f);

        gameObject.SetActive(false);
        //子对象也隐藏,回到初始状态
        outputCamera.gameObject.SetActive(false);
        slices.gameObject.SetActive(false);
        //rt.Release();

    }

    public float explosionForce = 300;
    public float hightPercent = 1;
    private void ScreenExplosion()
    {
        //隐藏背景裂纹
        bg.gameObject.SetActive(false);

        var rs = slices.GetComponentsInChildren<Rigidbody>();
        for (int i = 0; i < rs.Length; i++)
        {
            rs[i].useGravity = true;
            rs[i].AddExplosionForce(explosionForce*UnityEngine.Random.Range(0.8f,1.2f), slices.transform.position + new Vector3(0, 0, -0.5f), slices.transform.localScale.x * 10 * hightPercent * UnityEngine.Random.Range(0.8f, 1.2f));
        }
    }

    public void SetProgress(float progress)
    {
        this.progress = progress;
        if(progress==1)
        {
            isOver = true;
        }
    }

    UnityAction onBreak;
    public void SetBreakCallBack(UnityAction callback)
    {
        this.onBreak = callback;
    }

    public void SetCameraStack(Camera main)
    {
        UniversalAdditionalCameraData mainData = main.GetUniversalAdditionalCameraData();
        
        if (!mainData.cameraStack.Contains(outputCamera))
        {
            mainData.cameraStack.Add(outputCamera);
        }
    }


}

在切换场景时,生成该预制体,开启屏幕截图和颜色转换函数,在异步加载时设置prograss控制进度。当异步加载完成时设置prograss=1,设置屏幕爆炸效果

四,成果

Unity学习-屏幕破碎转场

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值