使用动效时,有时会需要使特定组件外的动效不显示,比如粒子特效只显示在特定区域或当列表中的特效移出显示区域时不显示动效,这时就需要使用遮罩,但是简单的使用Mask对动效是不起作用的。针对Spine动效和ParticleSystem动效需要分开处理。
一、在Spine动效中,只需要把Material的Shader从Spine/Skeleton改成Spine/SkeletonGraphic即可,如下图:
=>
,这样拖动列表时,动效就会被遮住了。
2、在ParticleSystem动效中,需要使用代码来屏蔽。
public class ContentMask : MonoBehaviour
{
private HashSet<ParticleSystemRenderer> renderers = new HashSet<ParticleSystemRenderer>();
private MaterialPropertyBlock propBlock;
private bool isDirty;
void Awake()
{
propBlock = new MaterialPropertyBlock();
}
public void Add(ParticleSystemRenderer[] renders)
{
foreach (var render in renders)
{
this.renderers.Add(render);
isDirty = true;
}
}
public void Remove(ParticleSystemRenderer[] renders)
{
foreach (var render in renders)
{
this.renderers.Remove(render);
isDirty = true;
}
}
private IEnumerator Start()
{
yield return new WaitForEndOfFrame();
SetClip();
}
private void Update()
{
if (isDirty)
{
isDirty = false;
SetClip();
}
}
protected void OnRectTransformDimensionsChange()
{
if (!isActiveAndEnabled) return;
SetClip();//重新再给材质赋裁切参数
}
private void SetClip()
{
if(propBlock == null){
propBlock = new MaterialPropertyBlock();
}
Camera canvasCamera = GetComponentInParent<Canvas>()?.rootCanvas?.worldCamera;
if (canvasCamera == null) return;
Vector3[] corners = new Vector3[4];
RectTransform rectTransform = transform as RectTransform;
rectTransform.GetWorldCorners(corners);
corners[0] = canvasCamera.WorldToScreenPoint(corners[0]);
corners[2] = canvasCamera.WorldToScreenPoint(corners[2]);
float minX = corners[0].x;
float minY = corners[0].y;
float maxX = corners[2].x;
float maxY = corners[2].y;
propBlock.SetVector("_ClipRect", new Vector4(minX, minY, maxX, maxY));
foreach (var renderer in renderers)
{
renderer.SetPropertyBlock(propBlock);
}
}
public void Refresh()
{
isDirty = true;
}
}
将上面的脚本挂到遮罩上(列表要挂在Viewport上)。
public class ParticleMask : MonoBehaviour
{
private const string additiveShaderName = "Legacy Shaders/Particles/Additive";
private const string alphaBlendedShaderName = "Legacy Shaders/Particles/Alpha Blended";
private const string clipAdditiveShaderName = "Custom/Particles/ClipAdditive";
private const string clipAlphaBlendedShaderName = "Custom/Particles/ClipAlphaBlended";
static class ClipShaders
{
public readonly static Shader Additive = Shader.Find(clipAdditiveShaderName);
public readonly static Shader AlphaBlended = Shader.Find(clipAlphaBlendedShaderName);
}
private ContentMask particleMask;
private ParticleSystemRenderer[] renders;
private List<Material> materials = new List<Material>();
private bool isActive = false;
private void Awake()
{
particleMask = GetComponentInParent<ContentMask>();
isActive = particleMask != null;
enabled = isActive;
if (isActive)
{
renders = GetComponentsInChildren<ParticleSystemRenderer>(true);
foreach (var psr in renders)
{
for (int i = 0; i < psr.sharedMaterials.Length; i++)
{
if (psr.sharedMaterials[i] == null) continue;
switch (psr.sharedMaterials[i].shader.name)
{
case additiveShaderName:
psr.materials[i].shader = ClipShaders.Additive;
materials.Add(psr.materials[i]);
break;
case alphaBlendedShaderName:
psr.materials[i].shader = ClipShaders.AlphaBlended;
materials.Add(psr.materials[i]);
break;
default:
break;
}
}
}
}
}
private void OnEnable()
{
if (!isActive) return;
particleMask.Add(renders);
}
private void OnDisable()
{
if (!isActive) return;
particleMask.Remove(renders);
}
private void OnDestroy()
{
foreach (var material in materials)
{
Destroy(material);
}
}
}
将上面的脚本挂在粒子上。还需要加一个Shader脚本。
Shader "Custom/Particles/ClipAdditive"
{
Properties{
_TintColor("Tint Color", Color) = (0.5,0.5,0.5,0.5)
_MainTex("Particle Texture", 2D) = "white" {}
_InvFade("Soft Particles Factor", Range(0.01,3.0)) = 1.0
//-------------------add----------------------
_ClipRect("ClipRect", Vector) = (-10,-10,10,10)
//-------------------add----------------------
}
Category{
Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "PreviewType" = "Plane" }
Blend SrcAlpha One
ColorMask RGB
Cull Off Lighting Off ZWrite Off
SubShader{
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_particles
#pragma multi_compile_fog
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed4 _TintColor;
//-------------------add----------------------
float4 _ClipRect;
//-------------------add----------------------
struct appdata_t {
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f {
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_FOG_COORDS(1)
//#ifdef SOFTPARTICLES_ON
float4 projPos : TEXCOORD2;
//#endif
//-------------------add----------------------
float2 projPosScreenSize : TEXCOORD3;
//-------------------add----------------------
UNITY_VERTEX_OUTPUT_STEREO
};
float4 _MainTex_ST;
v2f vert(appdata_t v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.vertex = UnityObjectToClipPos(v.vertex);
//#ifdef SOFTPARTICLES_ON
o.projPos = ComputeScreenPos(o.vertex);
//-------------------add----------------------
o.projPosScreenSize.xy = (o.projPos.xy / o.projPos.w) * _ScreenParams.xy;
#ifdef SOFTPARTICLES_ON
//-------------------add----------------------
COMPUTE_EYEDEPTH(o.projPos.z);
#endif
o.color = v.color;
o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
float _InvFade;
fixed4 frag(v2f i) : SV_Target
{
#ifdef SOFTPARTICLES_ON
float sceneZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));
float partZ = i.projPos.z;
float fade = saturate(_InvFade * (sceneZ - partZ));
i.color.a *= fade;
#endif
fixed4 col = 2.0f * i.color * _TintColor * tex2D(_MainTex, i.texcoord);
col.a = saturate(col.a); // alpha should not have double-brightness applied to it, but we can't fix that legacy behavior without breaking everyone's effects, so instead clamp the output to get sensible HDR behavior (case 967476)
UNITY_APPLY_FOG_COLOR(i.fogCoord, col, fixed4(0,0,0,0)); // fog towards black due to our blend mode
//-------------------add----------------------
float2 inside = step(_ClipRect.xy, i.projPosScreenSize.xy) * step(i.projPosScreenSize.xy, _ClipRect.zw);
col.a *= inside.x * inside.y;
//-------------------add----------------------
return col;
}
ENDCG
}
}
}
FallBack "Diffuse"
}
Shader "Custom/Particles/ClipAlphaBlended"
{
Properties{
_TintColor("Tint Color", Color) = (0.5,0.5,0.5,0.5)
_MainTex("Particle Texture", 2D) = "white" {}
_InvFade("Soft Particles Factor", Range(0.01,3.0)) = 1.0
//-------------------add----------------------
_ClipRect("ClipRect", Vector) = (-10,-10,10,10)
//-------------------add----------------------
}
Category{
Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "PreviewType" = "Plane" }
Blend SrcAlpha OneMinusSrcAlpha
ColorMask RGB
Cull Off Lighting Off ZWrite Off
SubShader{
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_particles
#pragma multi_compile_fog
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed4 _TintColor;
//-------------------add----------------------
float4 _ClipRect;
//-------------------add----------------------
struct appdata_t {
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f {
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_FOG_COORDS(1)
//#ifdef SOFTPARTICLES_ON
float4 projPos : TEXCOORD2;
//#endif
//-------------------add----------------------
float2 projPosScreenSize : TEXCOORD3;
//-------------------add----------------------
UNITY_VERTEX_OUTPUT_STEREO
};
float4 _MainTex_ST;
v2f vert(appdata_t v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.vertex = UnityObjectToClipPos(v.vertex);
//#ifdef SOFTPARTICLES_ON
o.projPos = ComputeScreenPos(o.vertex);
//-------------------add----------------------
o.projPosScreenSize.xy = (o.projPos.xy / o.projPos.w) * _ScreenParams.xy;
#ifdef SOFTPARTICLES_ON
//-------------------add----------------------
COMPUTE_EYEDEPTH(o.projPos.z);
#endif
o.color = v.color * _TintColor;
o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
float _InvFade;
fixed4 frag(v2f i) : SV_Target
{
#ifdef SOFTPARTICLES_ON
float sceneZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));
float partZ = i.projPos.z;
float fade = saturate(_InvFade * (sceneZ - partZ));
i.color.a *= fade;
#endif
fixed4 col = 2.0f * i.color * tex2D(_MainTex, i.texcoord);
col.a = saturate(col.a); // alpha should not have double-brightness applied to it, but we can't fix that legacy behavior without breaking everyone's effects, so instead clamp the output to get sensible HDR behavior (case 967476)
UNITY_APPLY_FOG(i.fogCoord, col);
//-------------------add----------------------
float2 inside = step(_ClipRect.xy, i.projPosScreenSize.xy) * step(i.projPosScreenSize.xy, _ClipRect.zw);
col.a *= inside.x * inside.y;
//-------------------add----------------------
return col;
}
ENDCG
}
}
}
FallBack "Diffuse"
}
然后粒子的Shader需要设置一下
或者
,这样遮罩才会生效。