在 Unity 中使用 ScrollView 时,特效(如粒子系统、Shader 效果等)可能会被裁剪,尤其是在 ScrollView 的视口(Viewport)之外。为了确保特效在 ScrollView 中正确显示,可以考虑以下几种方法:
1. 使用 Mask 组件
Mask 组件可以用于限制子对象的渲染区域。将 Mask 组件添加到 ScrollView 的内容区域(Content)上,可以确保只有在该区域内的子对象会被渲染。
- 步骤:
- 在
ScrollView的内容区域(Content)上添加Mask组件。 - 将需要显示的特效(如粒子系统)作为
Content的子对象。 - 确保特效的渲染顺序在
ScrollView的内容区域之上。
- 在
2. 帧动画代替动效
缺点:效果打折
3. 调整特效的渲染顺序
确保特效的渲染顺序在 ScrollView 的内容区域之上。可以通过调整特效的 Sorting Layer 和 Order in Layer 来实现。
- 步骤:
- 选择特效对象(如粒子系统)。
- 在
Renderer组件中设置Sorting Layer和Order in Layer,确保其值高于ScrollView的内容区域。
4. 使用 RawImage 或 Image 组件
如果特效是基于纹理的(如 Shader 效果),可以使用 RawImage 或 Image 组件来显示特效。
- 步骤:
- 在
ScrollView的内容区域中添加一个RawImage或Image组件。 - 将特效的纹理(如 RenderTexture)赋值给
RawImage或Image的texture属性。 - 确保
RawImage或Image的RectTransform设置正确,以便在ScrollView中显示。
- 在
5. 使用 RenderTexture
如果特效是基于摄像机的(如全屏特效),可以使用 RenderTexture 来捕捉特效并在 UI 中显示。
- 步骤:
- 创建一个新的
RenderTexture。 - 在一个摄像机上设置该
RenderTexture作为目标纹理。 - 在
ScrollView中添加一个RawImage,并将RenderTexture赋值给它的texture属性。
- 创建一个新的
6. 使用 Shader 和 Stencil Buffer
如果你需要更复杂的裁剪效果,可以使用 Shader 和 Stencil Buffer 来实现。
- 步骤:
- 编写一个自定义 Shader,使用 Stencil Buffer 来控制特效的显示区域。
- 将该 Shader 应用到特效的材质上。
- 确保特效的渲染顺序和
ScrollView的内容区域相匹配。
缺点:
如果涉及Shader较多的话,开发和维护成本较高。
7. 添加摄像机
Camera中Culling Mask中可以设置其“摄像”的Layer层,我们先添加一个新的Layer层Effect,并将新添加的Camera的显示层设置为Effect。
Camera中的Viewport Rect是其“摄像”的区域,这里我们要调整为这个区域与ScrollView的显示区域吻合。
特效的Layer要设置为Effect,特效不再会出现在UICamera中,而是由新的Camera负责显示。由于“摄像”区域的设置,特效在区域内的部分会正常显示,拖动超出“摄像”范围后,就看不到了,实现了裁剪的效果。
总结
在 Unity 中处理 ScrollView 中的特效裁剪问题时,可以使用多种方法,包括使用 Mask 组件、调整渲染顺序、使用 RenderTexture、动态调整位置等。选择合适的方法取决于具体的需求和特效类型。通过这些方法,可以确保特效在 ScrollView 中正确显示,提升用户体验。
Shader实现裁剪代码
将ScrollView的显示区域传给特效的Shader,超出显示区域部分将透明度设置为0,实现裁剪。这个方案实施分为两个部分:修改特效的Shader和显示区域传给Shader。
修改特效的Shader,下面是我们修改后的Shader,其中///add clip的部分是为了实现裁剪添加的部分:
Shader “Effect_Mid/ScrollClipAdditive”
{
Properties
{
…
///add clip
_MinX (“Min X”, Float) = -10
_MaxX (“Max X”, Float) = 10
_MinY (“Min Y”, Float) = -10
_MaxY (“Max Y”, Float) = 10
///add clip
}
SubShader
{
Tags
{
…
}
Pass
{
…
// ZTest Off
Fog { Color (0,0,0,0) }
…
///add clip
float _MinX;
float _MaxX;
float _MinY;
float _MaxY;
///add clip
…
struct VS_OUTPUT
{
......
///add clip
float3 vpos : TEXCOORD2;
///add clip
};
VS_OUTPUT vert(VS_INPUT In)
{
......
///add clip
Out.vpos = mul(UNITY_MATRIX_MVP, In.position);
///add clip
......
return Out;
}
float4 frag(VS_OUTPUT In) : COLOR
{
......
///add clip
color.a *= (In.vpos.x >= _MinX );
color.a *= (In.vpos.x <= _MaxX);
color.a *= (In.vpos.y >= _MinY );
color.a *= (In.vpos.y <= _MaxY);
///add clip
return color;
}
ENDCG
}
}
Fallback off
CustomEditor "EffectMaterialEd"
}
public class Effect: MonoBehaviour
{
//要实现特效参见的UIPanel(UIPanel和ScrollView是结合使用的,真正管理裁剪区域的是UIPanel)
public UIPanel m_RootPanel = null;
//默认值,写了一个很大的数值,保证不设置Panel时可以正常显示
float m_MinX = -10;
float m_MinY = -10;
float m_MaxX = 10;
float m_MaxY = 10;
void Start()
{
Reset();
}
public void Reset()
{
if (m_RootPanel != null)
{
//获取UI摄像机,为了获取panel相对于屏幕中心点的位置
GameObject UIRootCamera = GameObject.Find("UI/UIRoot(Clone)/UICamera(Clone)");
if (UIRootCamera != null)
{
//获取panel相对于屏幕中心点的位置,准确说是获取ScrollView的实际显示区域相对于屏幕中心点的位置
Vector3 toCameraPos = UIRootCamera.transform.InverseTransformPoint(m_RootPanel.transform.position);
//处理UIPanel的Center偏移
toCameraPos += new Vector3(m_RootPanel.baseClipRegion.x, m_RootPanel.baseClipRegion.y, 0);
//去除滑动的偏移量
//添加特效的时候ScrollView可能已经滑动过了,这时候ScrollView的坐标位置发生了变化
//我们根据ScrollView的坐标位置计算显示区域的位置,要计算这部分滑动的量
toCameraPos += new Vector3(m_RootPanel.clipOffset.x, m_RootPanel.clipOffset.y, 0);
//获取Panel实际显示区域的大小
Vector2 viewSize = m_RootPanel.GetViewSize();
//过渡部分处理,panel的过渡部分透明度会渐变消失
//我们项目使用的Softness基本都是4,比较小,这里处理为Softness的一半区域后消失
//虽然方案不完美,看上去还可以(有需要的话可以把Softness的值也传给Shader,做过渡消失的处理)
Vector2 clipSoftness = m_RootPanel.clipSoftness;
Vector2 clipSize = viewSize - clipSoftness * 0.5f;
//获取窗口大小
Vector2 screenViewSize = m_RootPanel.GetWindowSize();
float width = screenViewSize.x;
float height = screenViewSize.y;
//计算要传送给Shder的四个值,最小X,最大X,最小Y,最大Y
//这个值的大小是相对于屏幕常和宽的比例值
m_MinX = -((float)(clipSize.x * 0.5f - toCameraPos.x)) / (width * 0.5f);
m_MinY = -((float)(clipSize.y * 0.5f - toCameraPos.y)) / (height * 0.5f);
m_MaxX = ((float)(clipSize.x * 0.5f + toCameraPos.x)) / (width * 0.5f);
m_MaxY = ((float)(clipSize.y * 0.5f + toCameraPos.y)) / (height * 0.5f);
//传输范围数据
ExcuteChild(gameObject);
}
}
}
//传输范围数据
void ExcuteChild(GameObject go)
{
if (go.renderer != null)
{
//material与sharedMaterial用法相同,但是效率有差别
//sharedMaterial是共享材质,修改的话内存只占用一份,如果使用material的话,每次属性修改都会new一份新的出来
//但在编辑器模式下,修改sharedMaterial会导致本地文件发生变化,为防止不必要的文件变动和提交,这里区分平台处理
#if UNITY_EDITOR
Material[] list = go.renderer.materials;
#else
Material[] list = go.renderer.sharedMaterials;
#endif
for (int i = 0; i < list.Length; i++)
{
list[i].SetFloat(“_MinX”, m_MinX);
list[i].SetFloat(“_MinY”, m_MinY);
list[i].SetFloat(“_MaxX”, m_MaxX);
list[i].SetFloat(“_MaxY”, m_MaxY);
}
}
//遍历处理子节点
int count = go.transform.childCount;
for (int i = 0; i < count; i++)
{
ExcuteChild(go.transform.GetChild(i).gameObject);
}
}
}
在 Unity 中处理 ScrollView中通过Shader实现裁剪的逻辑代码
在 Unity 中,通过 Shader 实现 ScrollView 中的裁剪效果可以提供更灵活的视觉效果。下面是一个简单的示例,展示如何使用 Shader 和 C# 脚本来实现 ScrollView 中的裁剪逻辑。
1. 创建 Shader
首先,我们需要创建一个自定义 Shader,用于裁剪效果。这个 Shader 将根据 ScrollView 的视口来裁剪显示的内容。
1.1. Shader 代码
在 Unity 中,右键点击 Assets 文件夹,选择 Create > Shader > Unlit Shader,命名为 ClippingShader,然后替换其内容如下:
Shader "Custom/ClippingShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_ClipRect ("Clip Rect", Vector) = (0, 0, 1, 1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _ClipRect;
v2f vert (appdata_t v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
// 进行裁剪
if (i.uv.x < _ClipRect.x || i.uv.x > _ClipRect.z || i.uv.y < _ClipRect.y || i.uv.y > _ClipRect.w)
{
col.a = 0; // 设置透明度为0
}
return col;
}
ENDCG
}
}
FallBack "Diffuse"
}
2. 创建材质
- 在
Assets文件夹中右键点击,选择Create > Material,命名为ClippingMaterial。 - 将
ClippingShader赋值给ClippingMaterial的 Shader 属性。
3. 创建 C# 脚本
接下来,我们需要一个 C# 脚本来控制 Shader 中的裁剪矩形。
3.1. C# 脚本代码
在 Assets 文件夹中创建一个新的 C# 脚本,命名为 ScrollViewClipping.cs,并添加以下代码:
using UnityEngine;
using UnityEngine.UI;
public class ScrollViewClipping : MonoBehaviour
{
public RectTransform scrollViewRectTransform; // ScrollView 的 RectTransform
public Material clippingMaterial; // 使用的裁剪材质
private void Update()
{
// 获取 ScrollView 的视口矩形
Vector4 clipRect = GetClipRect();
// 更新材质的裁剪矩形
clippingMaterial.SetVector("_ClipRect", clipRect);
}
private Vector4 GetClipRect()
{
// 获取 ScrollView 的视口位置和大小
RectTransformUtility.ScreenPointToLocalPointInRectangle(scrollViewRectTransform,
new Vector2(scrollViewRectTransform.rect.xMin, scrollViewRectTransform.rect.yMin),
null, out Vector2 min);
RectTransformUtility.ScreenPointToLocalPointInRectangle(scrollViewRectTransform,
new Vector2(scrollViewRectTransform.rect.xMax, scrollViewRectTransform.rect.yMax),
null, out Vector2 max);
// 返回裁剪矩形
return new Vector4(min.x / scrollViewRectTransform.rect.width,
min.y / scrollViewRectTransform.rect.height,
max.x / scrollViewRectTransform.rect.width,
max.y / scrollViewRectTransform.rect.height);
}
}
4. 设置场景
- 在场景中创建一个
ScrollView。 - 在
ScrollView的Viewport下创建一个Image或RawImage,并将ClippingMaterial赋值给它的材质。 - 将
ScrollViewClipping脚本添加到ScrollView对象上,并在 Inspector 中将ScrollView的RectTransform和ClippingMaterial赋值给相应的字段。
5. 测试
运行场景并滚动 ScrollView,你应该会看到通过 Shader 实现的裁剪效果。特效或内容在 ScrollView 的视口外部将变得透明。
总结
通过以上步骤,你可以在 Unity 中使用 Shader 实现 ScrollView 的裁剪效果。这个方法提供了灵活的视觉效果,并且可以根据需要进行扩展和修改。你可以根据具体需求调整 Shader 的逻辑,以实现更复杂的裁剪效果。
1万+

被折叠的 条评论
为什么被折叠?



