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

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

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

前两个项目我一直用的是 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块钱请我喝一杯咖啡的话,请用手机扫描二维码即可通过支付宝直接向我捐款哦。


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

查看所有标签

猜你喜欢:

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

Mission Python

Mission Python

Sean McManus / No Starch Press / 2018-9-18 / GBP 24.99

Launch into coding with Mission Python, a space-themed guide to building a complete computer game in Python. You'll learn programming fundamentals like loops, strings, and lists as you build Escape!, ......一起来看看 《Mission Python》 这本书的介绍吧!

html转js在线工具
html转js在线工具

html转js在线工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具