UGUI研究院之一个脚本同时裁切UI元素和粒子特效和3D模型(三十一)

前两个项目我一直用的是 UGUI研究院之Mask裁切UI粒子特效或者3D模型(十七) 做的裁切,但是实际开发中由循环列表中需要动态创建元素,每个新创建的元素都需要根据MaskRect的区域重新给Shader中指定,这个小小的隐患总是引起BUG。最近在做技术储备看到了Stencil一个东西,发现可以很好的代替原来裁切的方法,如下图所示,一共有2个裁切区域,同时裁切 UI 模型 粒子特效。

 

 

首先无论怎么做,我觉得需要一个脚本,只需要将模型 UI 特效挂在下面就可以全部裁切,运行期间动态添加不需要调用刷新方法。如下图所示,添加一个新的脚本RectMask3D来代替UGUI自带的RectMask2D

 

RectMask3D.cs 代码中的m_ID是为了区分裁切区域,大部分情况下裁切区域都是一致的,当然也不排除有重叠的情况,就像最上面的图一样。

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

using UnityEngine;

using UnityEngine.UI;

public class RectMask3D : RectMask2D

{

    GameObject m_Mask3D = null;

    [SerializeField] private Material m_Material;

    [SerializeField] private int m_ID = 1;

 

    public int id

    {

        get { return m_ID; }

        set

        {

            if (value != m_ID)

            {

                m_ID = value;

                Refresh();

            }

        }

    }

 

#if UNITY_EDITOR

    protected void OnValidate()

    {

        if (Application.isPlaying)

        {

            Refresh();

        }

    }

#endif

    protected override void Awake()

    {

        base.Awake();

        if (Application.isPlaying)

        {

            Refresh();

        }

    }

 

    private void Refresh()

    {

        if (m_Mask3D == null)

        {

            m_Mask3D = GameObject.CreatePrimitive(PrimitiveType.Quad);

            m_Mask3D.layer = LayerMask.NameToLayer("UI");

            m_Mask3D.name = "Mask3D";

            m_Mask3D.hideFlags = HideFlags.NotEditable;

            m_Mask3D.GetComponent<MeshRenderer>().material = m_Material;

            m_Mask3D.transform.SetParent(transform);

        }

        m_Mask3D.transform.localPosition = Vector3.zero;

        m_Mask3D.transform.localScale = this.rectTransform.sizeDelta;

 

        var material = m_Mask3D.GetComponent<Renderer>().material;

        material.SetInt("_ID", m_ID);

    }

 

}

#if UNITY_EDITOR

[UnityEditor.CustomEditor(typeof(RectMask3D))]

class SuperMask2DInspector : UnityEditor.Editor

{

 

    public override void OnInspectorGUI()

    {

        base.OnInspectorGUI();

    }

}

#endif

m_Material 是裁切3D所需要的,Shader中需要Stencil,为了避免每次添加一个新的裁切区域手动绑定,可以提前挂在Inspector面板上。


Mask.shader

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

Shader "Unlit/Mask"

{

   Properties

   {

       [HideInInspector] _ID ("_ID",int) = 1

   }

   SubShader

   {

        Pass

        {

            Tags{ "RenderType" = "Opaque" "Queue" = "Geometry-1" }

            ColorMask 0

            ZWrite off  

            ZTest off  

            Stencil

            {

                Ref [_ID]

                Comp always

                Pass replace //替换相同ID模板像素

            }

            CGINCLUDE

            struct appdata {

                float4 vertex : POSITION;

            };

            struct v2f {

                float4 vertex : SV_POSITION;

            };

            v2f vert(appdata v) {

                v2f o;

                return o;

            }

            half4 frag(v2f i) : SV_Target{

                return 0;

            }

            ENDCG

        }

    }

}

接着就是Mask裁切的元素了,由于脚本继承了RectMask2D所以自带就可以裁切UI,需要处理的就是特效和模型,这里我用特效来举例,其他的做法都类似。给参与裁切的特效或者模型绑定RectImage3D脚本,(也可以不绑定,主要就是ID的值需要和RectMask3D里填的一致)。Type枚举可以设置永远显示(Always)或者被裁切(Equal)


RectItem3D.cs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

using UnityEngine;

public class RectItem3D : MonoBehaviour

{

    [SerializeField]

    private int m_ID = 1;

    [SerializeField]

    private MaskType m_Type = MaskType.Always;

 

    public MaskType type

    {

        get { return m_Type; }

        set

        {

            if(value != m_Type)

            {

                m_Type = value;

                Refresh();

            }

        }

    }

 

 

#if UNITY_EDITOR

    protected void OnValidate()

    {

        if (Application.isPlaying)

        {

            Refresh();

        }

    }

#endif

 

    private void Awake()

    {

        Refresh();

    }

 

    void Refresh()

    {

        foreach (var render in GetComponentsInChildren<Renderer>(true))

        {

            var material = render.material;

 

            material.SetInt("_ID", m_ID);

            material.SetInt("_StencilComp", (int)m_Type);

        }

    }

 

    public enum MaskType : byte

    {

        Always = 8,

        Equal = 3

    }

 

}

同样还需要给粒子或者模型绑定新的Shader,这里我就举个简单的粒子,将//—-add—-中间的代码添加到需要裁切的模型或者粒子的Shader中。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

Shader "Unlit/Cube"

{

Properties

{

_MainTex ("Texture", 2D) = "white" {}

        //----add----

        [HideInInspector] _ID ("_ID",int) = 1

        [HideInInspector] _StencilComp ("_StencilComp",Float) = 8

        //----add----

}

SubShader

{

        Tags { "RenderType"="Opaque" "Queue"="Geometry"}

        Pass

{

            //----add----

            Stencil

            {  

                Ref [_ID]              

                Comp [_StencilComp]    

                Pass keep            

            }  

            //----add----

            

CGPROGRAM

#pragma vertex vert

#pragma fragment frag

#include "UnityCG.cginc"

 

struct appdata

{

                float4 vertex : POSITION;

                float2 texcoord : TEXCOORD0;

 

};

 

struct v2f

{

                float4 vertex : POSITION;

                float2 texcoord : TEXCOORD0;

};

 

 

            sampler2D _MainTex;

            float4 _MainTex_ST;

            

v2f vert (appdata v)

{

            v2f o;

                o.vertex = UnityObjectToClipPos(v.vertex);

                o.texcoord = v.texcoord;

                return o;

}

fixed4 frag (v2f IN) : SV_Target

{

                return tex2D(_MainTex, IN.texcoord);

}

ENDCG

}

}

}

这样需要裁切的粒子或者模型就可以保证只使用同一个Shader,运行时代码灵活的控制。

最后说说我的总结:

1.我还是建议模型使用RT,因为UI界面需要叠层挡住模型的现象,关于AlphaBlend或者显示不清楚的现象,可以参考我之前的文章 UGUI研究院之在UI上使用RenderTexture显示模型+AlphaBlend特效(二十五)

2.粒子特效适合使用这篇文章的方法,其实UI的粒子特效需要裁切的 无非就是 滑动列表中的图标上的转圈特效,配合SortOrder可以很好解决层级的问题。

3.RectTransform的Scale是(1,1,1) 但是模板需要添加scale的区域,我不知道怎么可以和RectTransform公用一个,所以我不得不创建一个和RectTransform区域相同的m_Mask3D,如果有知道的朋友欢迎告诉我。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值