【Unity3D ugui】UI特效的位置自適應及調整層次關係的一種解決方案

前言

在UI上顯示3D的特效,要考慮兩個問題:
1、特效的位置自適應與UGUI自適應一致,否則在16:9下把特效調好位置後,切成16:10後,位置對應不上
2、特效顯示層次最好能夾在UI中間

UGUI畢竟是個新的UI系統,各方面還很不成熟,顯示特效的問題着實讓我頭疼了一番。
1、UI特效疊層顯示可以參考雨鬆MOMO的博客:UGUI研究院之不添加攝像機解決UI與UI特效疊層問題(九),但是隻能解決疊層的問題,而且對於複雜的界面系統,每一層都加一個UIDepth的組件非常蛋疼。自適應的問題要根據不同分辨率再進行一番艱難的調整,除非你可以寫一套類似的Canvas自適應系統。
2、使用UGUI的RawImage,把Camera拍攝特效輸出的RenderTexture拖到RawImage上,運行時即可看到效果。這種方法不僅能使用UGUI的自適應,而且層級也可以調整,通常顯示UI模型時都是用這種方法,但是對於半透明的粒子、Mesh就不那麼好處理了。最後找到一位前輩的文章,終於解決了困擾了我好幾天的問題。想看原文點這裏

原理

1、用一個單獨的相機,對着特效拍照,設置輸出的Target Texture
2、使用UGUI的RawImage組件,設置Texture爲相機輸出的Texture

這部分的內容,其實官方已經給出demo,打開RenderTexture場景就可以看到這個例子,就不詳述了

實現

參照原文
下面用圖來說明幾個步驟:
先說一下demo的層次結構,“Window”下有兩個Image,一個RawImage,RawImage夾在兩個Image中間,我們想要的效果就是RawImage在Image1上,在Image2下。
這裏寫圖片描述

1、相機參數設置
這裏寫圖片描述

2、特效設置層次爲UI3D(自己添加的Layer)
這裏寫圖片描述
相機拍到特效的效果如下:
這裏寫圖片描述

3、在UI中加上一個RawImage,設置Texture爲上一步相機輸出的Texture,加一個Default No-Alpha的材質(Shader在下面貼出,demo資源裏也有)
這裏寫圖片描述

4、運行查看效果
這裏寫圖片描述

一些問題

1、RenderTexture的尺寸越大,內存佔用越多,儘可能縮減RenderTexture的尺寸吧
2、RenderTexture的尺寸要與RawImage的大小一致,否則出現拉伸變形
3、每個特效對應一個相機,如果特效多的話,還是用代碼管理特效相機吧
進一步思考,通常美術把一個特效的prefab發過來,我們只要把這個prefab和UI中的RawImage綁定即可,相機什麼的纔不想每次都動手加一遍,所以有了下面偷懶的代碼

using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(RawImage))]
public class UI3DEffect : MonoBehaviour
{
    [SerializeField]
    private GameObject effectPrefab;
    private GameObject effectGO;
    private RenderTexture renderTexture;
    private Camera rtCamera;
    private RawImage rawImage;

    void Awake()
    {
        //RawImage可以手動添加,設置no alpha材質,以顯示帶透明的粒子
        rawImage = gameObject.GetComponent<RawImage>();
        if (rawImage == null)
        {
            rawImage = gameObject.AddComponent<RawImage>();
        }
    }

    public RectTransform rectTransform
    {
        get
        {
            return transform as RectTransform;
        }
    }

    void OnEnable()
    {
        if (effectPrefab != null)
        {
            effectGO = Instantiate(effectPrefab);

            GameObject cameraObj = new GameObject("UIEffectCamera");
            rtCamera = cameraObj.AddComponent<Camera>();
            renderTexture = new RenderTexture((int)rectTransform.sizeDelta.x, (int)rectTransform.sizeDelta.y, 24);
            renderTexture.antiAliasing = 4;
            rtCamera.clearFlags = CameraClearFlags.SolidColor;
            rtCamera.backgroundColor = new Color();
            rtCamera.cullingMask = 1 << 8;
            rtCamera.targetTexture = renderTexture;

            effectGO.transform.SetParent(cameraObj.transform, false);

            rawImage.enabled = true;
            rawImage.texture = renderTexture;
        }
        else
        {
            rawImage.texture = null;
            Debug.LogError("EffectPrefab can't be null");
        }
    }

    void OnDisable()
    {
        if (effectGO != null)
        {
            Destroy(effectGO);
            effectGO = null;
        }
        if (rtCamera != null)
        {
            Destroy(rtCamera.gameObject);
            rtCamera = null;
        }
        if (renderTexture != null)
        {
            Destroy(renderTexture);
            renderTexture = null;
        }
        rawImage.enabled = false;
    }
}

當RawImage啓用時,立刻創建動態相機、RenderTexture,設置參數。不啓用時,自動銷燬相機和RenderTexture。當然特效多的話,也可以做個對象池把Camera和RenderTexture緩存起來。
然後用的時候是這樣的,只需要一步,把特效prefab拖進來即可
這裏寫圖片描述

demo

UI-Default-No-Alpha.shader

Shader "UI/Default No-Alpha"
{
    Properties
    {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
        _Color("Tint", Color) = (1,1,1,1)

        _StencilComp("Stencil Comparison", Float) = 8
        _Stencil("Stencil ID", Float) = 0
        _StencilOp("Stencil Operation", Float) = 0
        _StencilWriteMask("Stencil Write Mask", Float) = 255
        _StencilReadMask("Stencil Read Mask", Float) = 255

        _ColorMask("Color Mask", Float) = 15
    }

    SubShader
    {
        Tags
        {
            "Queue" = "Transparent"
            "IgnoreProjector" = "True"
            "RenderType" = "Transparent"
            "PreviewType" = "Plane"
            "CanUseSpriteAtlas" = "True"
        }

        Stencil
        {
            Ref[_Stencil]
            Comp[_StencilComp]
            Pass[_StencilOp]
            ReadMask[_StencilReadMask]
            WriteMask[_StencilWriteMask]
        }

        Cull Off
        Lighting Off
        ZWrite Off
        ZTest[unity_GUIZTestMode]
        Fog{ Mode Off }
        Blend One Zero
        ColorMask[_ColorMask]
        Blend One OneMinusSrcAlpha // 源rgba*1 + 背景rgba*(1-源A值)  

        Pass
        {
            CGPROGRAM
            #pragma vertex vert  
            #pragma fragment frag  
            #include "UnityCG.cginc"  

            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color : COLOR;
                half2 texcoord  : TEXCOORD0;
            };

            fixed4 _Color;

            v2f vert(appdata_t IN)
            {
                v2f OUT;
                OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
                OUT.texcoord = IN.texcoord;
        #ifdef UNITY_HALF_TEXEL_OFFSET  
                OUT.vertex.xy -= (_ScreenParams.zw - 1.0);
        #endif  
                OUT.color = IN.color * _Color;
                return OUT;
            }

            sampler2D _MainTex;

            fixed4 frag(v2f IN) : SV_Target
            {
                half4 color = tex2D(_MainTex, IN.texcoord) * IN.color;
                //clip (color.a - 0.01);  
                return color;
            }
            ENDCG
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章