UGUI ScrollView ui特效遮挡的问题 agile Posted on Apr 24 2020 当使用滚动列表的时候,例如商城,好友列表。我们要在每个列表Item上添加UI特效的时候(Particle System或Mesh Renender等)会发现特效并不能根据父控件的大小而裁减掉,如图:  注:UGUI和NGUI都存在这个问题,NGUI则是在UIPanel Clipping 为Soft Clip时。 我们要实现的效果是超框的部分也应该和其他UI一样被裁减掉。现在的思路是修改特效的shader,给shader传一个值,记录scrollview四个边在世界坐标的位置,然后在显示的时候判断是否在框内,若不在则隐藏。 首先先处理shader这块,为了不影响其他的特效。可以把需要修改的shader都复制一份重命名。(我这边的处理是 a.shader 复制成 a 1.shader )下面的代码是在原本shader的基础上进行的修改,修改处注释已标出。 Shader: ```C# 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: ```C# 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特效上后:运行看到如下的效果,多余的已被裁剪:  C# 7.0 的新特性 C中调用Lua函数