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

栏目: 后端 · 发布时间: 5年前

内容简介:前两个项目我一直用的是

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

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

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

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

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

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

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面板上。

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

Mask.shader

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)

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

RectItem3D.cs

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中。

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,如果有知道的朋友欢迎告诉我。

雨松MOMO提醒您:亲,如果您觉得本文不错,快快将这篇文章分享出去吧 。另外请点击网站顶部彩色广告或者捐赠支持本站发展,谢谢!

最后编辑:

作者:雨松MOMO

专注移动互联网,Unity3D游戏开发

站内专栏 QQ交谈 腾讯微博 新浪微博

捐 赠 如果您愿意花20块钱请我喝一杯咖啡的话,请用手机扫描二维码即可通过支付宝直接向我捐款哦。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Go语言实战

Go语言实战

威廉·肯尼迪 (William Kennedy)、布赖恩·克特森 (Brian Ketelsen)、埃里克·圣马丁 (Erik St.Martin) / 李兆海 / 人民邮电出版社 / 2017-3-1 / CNY 59.00

Go语言结合了底层系统语言的能力以及现代语言的高级特性,旨在降低构建简单、可靠、高效软件的门槛。本书向读者提供一个专注、全面且符合语言习惯的视角。Go语言实战同时关注语言的规范和实现,涉及的内容包括语法、类型系统、并发、管道、测试,以及其他一些主题。一起来看看 《Go语言实战》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换