[Unity]UGUI ScrollView ui特效遮挡的问题
程序员文章站
2022-05-31 09:12:50
...
当使用滚动列表的时候,例如商城,好友列表。我们要在每个列表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:
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特效上后:运行看到如下的效果,多余的已被裁剪:
上一篇: 广度优先 深度优先访问 树
下一篇: UGUI scrollView循环滚动