欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[Unity]UGUI ScrollView ui特效遮挡的问题

程序员文章站 2022-05-31 09:12:50
...

当使用滚动列表的时候,例如商城,好友列表。我们要在每个列表Item上添加UI特效的时候(Particle System或Mesh Renender等)会发现特效并不能根据父控件的大小而裁减掉,如图:

[Unity]UGUI ScrollView ui特效遮挡的问题

注:UGUI和NGUI都存在这个问题,NGUI则是在UIPanel Clipping 为Soft Clip时。

我们要实现的效果是超框的部分也应该和其他UI一样被裁减掉。现在的思路是修改特效的shader,给shader传一个值,记录scrollview四个边在世界坐标的位置,然后在显示的时候判断是否在框内,若不在则隐藏。

首先先处理shader这块,为了不影响其他的特效。可以把需要修改的shader都复制一份重命名。(我这边的处理是 a.shader 复制成 a 1.shader )下面的代码是在原本shader的基础上进行的修改,修改处注释已标出。

Shader:

Shader "PJ Particles/PJ Additive 1" {
	Properties {
		_TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)
		_MainTex ("Particle Texture (A = Transparency)", 2D) = "white" {}
		_InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0
		
		//新增 记录裁剪框的四个边界的值
		_Area ("Area", Vector) = (0,0,1,1)
		//----end----
	}

	Category {
		Tags { "Queue"="Transparent+300" "IgnoreProjector"="True" "RenderType"="Transparent" }
		Blend SrcAlpha One
		AlphaTest Greater .01
		ColorMask RGB
		Cull Off Lighting Off ZWrite Off Fog { Color (0,0,0,0) }
		BindChannels {
			Bind "Color", color
			Bind "Vertex", vertex
			Bind "TexCoord", texcoord
		}
		
		SubShader {
			Pass {
			
				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#pragma fragmentoption ARB_precision_hint_fastest
				#pragma multi_compile_particles
	
				#include "UnityCG.cginc"
	
				sampler2D _MainTex;
				fixed4 _TintColor;
	
				//新增,对应上面的_Area
				float4 _Area;
				//----end----
				
				struct appdata_t {
					float4 vertex : POSITION;
					fixed4 color : COLOR;
					float2 texcoord : TEXCOORD0;
				};
	
				struct v2f {
					float4 vertex : POSITION;
					fixed4 color : COLOR;
					float2 texcoord : TEXCOORD0;
					//新增,记录顶点的世界坐标
					float2 worldPos : TEXCOORD1;
					//----end----
				};
				
				float4 _MainTex_ST;
	
				v2f vert (appdata_t v)
				{
					v2f o;
					o.vertex = UnityObjectToClipPos(v.vertex);
					o.color = v.color;
					o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
					//新增,计算顶点的世界坐标
					o.worldPos = mul(unity_ObjectToWorld, v.vertex).xy;
					//----end----
					return o;
				}
	
				sampler2D _CameraDepthTexture;
				float _InvFade;
				
				fixed4 frag (v2f i) : COLOR
				{
					//新增,判断顶点坐标是否在裁剪框内
					bool inArea = i.worldPos.x >= _Area.x && i.worldPos.x <= _Area.z && i.worldPos.y >= _Area.y && i.worldPos.y <= _Area.w;
					//----end----

					//如果在裁剪框内return原本的效果,否则即隐藏
	                return inArea? 2.0f * i.color * _TintColor * tex2D(_MainTex, i.texcoord) : fixed4(0,0,0,0);
				}
				ENDCG 
			}
		} 
	}
}

shader处理好后,接下来要做的就是将UI特效里面所有的shader都替换成新的。可以写一个脚本,挂在UI特效上,然后再Start里面替换好shader然后计算出裁剪框(UGUI的ScrollView或NGUI的UIPanel,等)的四个边界的世界坐标。最后将这个值传给新的shader即可。代码如下,用的UGUI的ScrollView。当然NGUI的思路也是一样样的:

Unity:

using System.Collections.Generic;
using UnityEngine;

namespace UI {

	public class EffectClip : MonoBehaviour {

        [SerializeField] RectTransform m_rectTrans;//遮挡容器,即ScrollView

        List<Material> m_materialList = new List<Material>();//存放需要修改Shader的Material
        Transform m_canvas;//UI的根,Canvas
        float m_halfWidth, m_halfHeight, m_canvasScale;

        void Start () {
            m_canvas = GameObject.Find("Canvas").transform;

            //获取所有需要修改shader的material,并替换shader
            var particleSystems = GetComponentsInChildren<ParticleSystem>();
            for(int i = 0, j = particleSystems.Length; i < j ; i++) {
                var ps = particleSystems[i];
                var mat = ps.GetComponent<Renderer>().material;
                m_materialList.Add(mat);
                mat.shader = Shader.Find(mat.shader.name + " 1");
            }

            var renders = GetComponentsInChildren<MeshRenderer>();
            for(int i = 0, j = renders.Length; i < j; i++) {
                var ps = renders[i];
                var mat = ps.material;
                m_materialList.Add(mat);
                mat.shader = Shader.Find(mat.shader.name + " 1");
            }

            //获取UI的scale,容器的宽高的一半的值
            m_canvasScale = m_canvas.localScale.x;
            m_halfWidth = m_rectTrans.sizeDelta.x * 0.5f * m_canvasScale;
            m_halfHeight = m_rectTrans.sizeDelta.y * 0.5f * m_canvasScale;

            //给shader的容器坐标变量_Area赋值
            Vector4 area = CalculateArea(m_rectTrans.position);
            for(int i = 0, len = m_materialList.Count; i < len; i++) {
                m_materialList[i].SetVector("_Area", area);
            }
        }

        //计算容器在世界坐标的Vector4,xz为左右边界的值,yw为下上边界值
        Vector4 CalculateArea(Vector3 position) {
            return new Vector4() {
                x = position.x - m_halfWidth,
                y = position.y - m_halfHeight,
                z = position.x + m_halfWidth,
                w = position.y + m_halfHeight
            };
        }
    }
}

最后将脚本挂上所有的UI特效上后:运行看到如下的效果,多余的已被裁剪:

[Unity]UGUI ScrollView ui特效遮挡的问题

相关标签: UGUI ScrollView