1. 程式人生 > >新手引導的介面部分操作區域的處理(一)

新手引導的介面部分操作區域的處理(一)

挖坑和高亮

這裡總結了一些常用的方式,不全面歸納如下:1.將目標操作物件(Button/或過載自寫的互動事件)區域提取到引導灰色層之上     處理方式:  A. 在物件上加Canvs控制層級                         B. 直接把物件的父節點設定到引導層上                      優點:a.即達到了高亮又解決了唯一可點選的問題           b.點選的是實際的按鍵,省去了再處理互動事件的麻煩     缺點:a.如果這個物件的可操作區域和對應顯示區域大小不一樣時會比較尷尬           b.把層級提上來之後,之後再放下去,對於一些比較複雜的介面來說很麻煩(比如這個物件上還有一些層級不一樣的特效                 粒子,還需要單獨去設定這些粒子的層級)           c.如果是B這種方法實現的話,目標物件如果是在Layout列表裡的話,處理起來就麻煩

2.在引導Mask上挖坑,區域為可操作區域(真挖坑)     處理方式:寫個Shader挖形狀掛在Raycast關閉的Image上,再用四張Image做遮罩動態計算修改空點選範圍                    優點:a.點選的是實際的按鍵,省去了再處理互動事件的麻煩           b.高亮區域可控,也比較方便           c.不改變原來物件的層級和節點結構,省去了維護這個的麻煩     缺點:a.Shader沒法挖出想要的形狀,特別是有些UI形狀古怪的           b.實際可操作區域永遠是大小可變的矩形,沒有圓形(基本能滿足需求)           3.在Mask上挖坑,實際並沒有挖出操作區域

    處理方式:寫Shader挖坑,並開啟Image的Raycast,判斷操作區域時,通過判斷這個Image上的alph = 0 來知道點選的是可操作區域     優點:a.不改變原來物件的層級和節點結構     缺點:a.高亮區域很難挖出想要的形狀           b.需要另外想辦法獲取物件上的互動事件           4.複製一個待操作物件FakeObj到引導Mask上     處理方式:mask全遮住,點選後判斷是點在FakeObj上則把互動傳遞下去

以上幾種方法還能衍生出很多組合,看實際專案需求吧。 我的做法是,上面分具體情況利用了上面的兩種方法:挖坑/賦值假物件(想怎麼樣通過配置確定,實際上寫了4種類型來應對不同的需求),因為操作區域可能需要:長按 / 拖拽 / 操作後需要等待伺服器回撥 / 。。。

下面是挖坑的Shader程式碼:

可同時挖兩個坑(圓+矩形 / 圓+圓),能力有限,矩形的邊緣虛化還沒有想到更好的辦法

Shader "UICustom/GuideMask"
{
    Properties
    {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
        _Color("Tint", Color) = (0.573,0.573,0.573,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
            //中心 - x/y/寬/高
            _Origin("圓心",Vector) = (0,0,0,0)
            //中心2 - x/y/寬/高
            _Origin2("圓心2",Vector) = (0,0,0,0)
            //裁剪方式 0圓形 1矩形
            _MaskType("Type",Float) = 0
            //螢幕高寬
            _ScreenY("螢幕高",Float) = 1920
            _ScreenX("螢幕寬",Float) = 1080
            [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip("Use Alpha Clip", Float) = 0
            [PowerSlider(1)] _DimRange("DimRange", Range(0.0, 1.0)) = 0.066
            [PowerSlider(1)] _DimRectRange("DimRectRange", Range(0.0, 1.0)) = 0.006

    }
        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]
            Blend SrcAlpha OneMinusSrcAlpha
            ColorMask[_ColorMask]

            //pass 1
            Pass
        {
            Name "GDMask"
            CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    #pragma target 3.0
    #include "UnityCG.cginc"
    #include "UnityUI.cginc"
    #pragma multi_compile __ UNITY_UI_ALPHACLIP

            struct appdata_t
        {
            float4 vertex : POSITION;
            float4 color : COLOR;
            float2 texcoord : TEXCOORD0;
        };
        struct v2f
        {
            float4 vertex : SV_POSITION;
            fixed4 color : COLOR;
            float2 texcoord : TEXCOORD0;
            float4 worldPosition : TEXCOORD1;
            UNITY_VERTEX_OUTPUT_STEREO
        };
        fixed4 _Color;
        fixed4 _TextureSampleAdd;
        float4 _ClipRect;
        float4 _Origin;
        float _MaskType;
        float _ScreenX;
        float _ScreenY;
        float _DimRange;
        float _DimRectRange;
        float4 _Origin2;

        v2f vert(appdata_t IN)
        {
            v2f OUT;
            UNITY_SETUP_INSTANCE_ID(IN);
            UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
            OUT.worldPosition = IN.vertex;
            OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
            OUT.texcoord = IN.texcoord;
            OUT.color = IN.color * _Color;
            return OUT;
        }
        sampler2D _MainTex;
        fixed4 frag(v2f IN) : SV_Target
        {
            half2 uv = IN.texcoord;
            half4 col = IN.color;
            //開始裁剪
            //外部直接給座標 寬 高 GPU計算比率

            half posX = (_Origin.x + _ScreenX / 2) / _ScreenX;
            half posY = (_Origin.y + _ScreenY / 2) / _ScreenY;
            half2 pos = half2(posX, posY);

            if (_MaskType == 0) {

                posX = posX * _ScreenX / _ScreenY;
                pos = half2(posX, posY);
                half rid = _Origin.z / _ScreenY / 2;
                uv.x = uv.x * _ScreenX / _ScreenY;
                half2 nor = uv - pos;
                half len = length(nor);

                if (len < rid + _DimRange)
                {
                    if (len < rid)
                    {
                        col.a = 0;
                    }
                    else {
                        //向內虛化18.2.5Sam
                        if (len >= rid)
                            col.a = col.a*((len - rid) / _DimRange);
                    }
                }


            }
            //矩形剪裁
            else {

                //矩形剪裁 + 四角模糊
                float w = _Origin.z / _ScreenX / 2;
                float h = _Origin.w / _ScreenY / 2;
                float dr_w = _Origin.z / _ScreenX / 2 + _DimRectRange;
                float dr_h = _Origin.w / _ScreenY / 2 + _DimRectRange;

                //向內虛化18.2.5Sam
                if (uv.x > pos.x - dr_w && uv.x<pos.x + dr_w && uv.y>pos.y - dr_h && uv.y < pos.y + dr_h)
                {
                    if (uv.x > pos.x - w && uv.x < pos.x + w
                        && uv.y > pos.y - h && uv.y < pos.y + h)
                    {
                        col.a = 0;
                    }

                    if ((uv.x <= pos.x - w && uv.y <= pos.y - h)
                        || (uv.x <= pos.x - w && uv.y >= pos.y + h)
                        || (uv.x >= pos.x + w && uv.y <= pos.y - h)
                        || (uv.x >= pos.x + w && uv.y >= pos.y + h)
                        )
                    {
                        ////上下左右
                        //half4 a = tex2D(_MainTex, uv + float2(_Offset, 0));
                        //half4 b = tex2D(_MainTex, uv + float2(-_Offset, 0));
                        //half4 c = tex2D(_MainTex, uv + float2(0, _Offset));
                        //half4 d = tex2D(_MainTex, uv + float2(0, -_Offset));
                        //偏45度上下左右
                        float _Offset = 0.01;
                        half4 e = tex2D(_MainTex, uv + float2(_Offset / 2, _Offset / 2));
                        half4 f = tex2D(_MainTex, uv + float2(-_Offset / 2, _Offset / 2));
                        half4 g = tex2D(_MainTex, uv + float2(_Offset / 2, -_Offset / 2));
                        half4 h = tex2D(_MainTex, uv + float2(-_Offset / 2, -_Offset / 2));
                        //+ e + f + g + h
                        half4 texCol = tex2D(_MainTex, uv);
                        texCol = (texCol + (e + f + g + h)) * 0.14 *_Color;//;
                        return texCol;
                    }

                    //-----------上下透明度過渡--------------------------------------------
                    if (uv.x <= pos.x - w || uv.x >= pos.x + w)
                    {
                        if (uv.x <= pos.x - w)
                        {
                            col.a = col.a*((pos.x - w - uv.x) / _DimRectRange);
                        }
                        else {
                            col.a = col.a*((uv.x - (pos.x + w)) / _DimRectRange);
                        }
                    }
                    else if (uv.y <= pos.y - h || uv.y >= pos.y + h)
                    {

                        if (uv.y <= pos.y - h)
                        {
                            col.a = col.a*((pos.y - h - uv.y) / _DimRectRange);
                        }
                        else
                        {
                            col.a = col.a*((uv.y - (pos.y + h)) / _DimRectRange);
                        }
                    }
                    //-----------------------------------------------------------------
                }

            }

            //---------圓2--------------------------------------
            if (_Origin2.z > 0)
            {
                half posX2 = (_Origin2.x + _ScreenX / 2) / _ScreenX;
                half posY2 = (_Origin2.y + _ScreenY / 2) / _ScreenY;
                half2 pos2 = half2(posX2, posY2);

                posX2 = posX2 * _ScreenX / _ScreenY;
                pos2 = half2(posX2, posY2);
                half rid2 = _Origin2.z / _ScreenY / 2;
                if (_MaskType != 0)
                uv.x = uv.x * _ScreenX / _ScreenY;
                half2 nor2 = uv - pos2;
                half len2 = length(nor2);

                if (len2 < rid2 + _DimRange)
                {
                    if (len2 < rid2 && col.a >0)
                    {
                        col.a = 0;
                    }
                    else {
                        //向內虛化18.2.5Sam
                        if (len2 >= rid2)
                            col.a = col.a*((len2 - rid2) / _DimRange);
                    }
                }
            }
            //-----------------------------------------------------


            half4 color = (tex2D(_MainTex, uv) + _TextureSampleAdd) * col;
            color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
            clip(col.a);
    #ifdef UNITY_UI_ALPHACLIP
            clip(color.a - 0.001);
    #endif
            return color;
        }
            ENDCG
        }//end pass 1

        }
}

效果如下

Vect4 型別 x y 表示螢幕座標的位置, z w 分別表示寬和高(圓形的話,只用z表示半徑)

引數中 DimRange 和 DimRectRange 調整好後基本可以不動了,

只需調整 _Origin _Origin2 _MaskType來控制大小和形狀即可,具體在腳本里怎麼獲取在說就多餘了。

shader是借鑑別人的的基礎上改的,有點糙,但是基本滿足了我的需求,請多指教。

最後,不得不說這些方法能做到的都是有限的,對於不同專案出現的不同需求,還是需要自己靈活使用。