Unity特效在ScrollView中的裁剪的几个方法

在 Unity 中使用 ScrollView 时,特效(如粒子系统、Shader 效果等)可能会被裁剪,尤其是在 ScrollView 的视口(Viewport)之外。为了确保特效在 ScrollView 中正确显示,可以考虑以下几种方法:

1. 使用 Mask 组件

Mask 组件可以用于限制子对象的渲染区域。将 Mask 组件添加到 ScrollView 的内容区域(Content)上,可以确保只有在该区域内的子对象会被渲染。

  • 步骤
    1. ScrollView 的内容区域(Content)上添加 Mask 组件。
    2. 将需要显示的特效(如粒子系统)作为 Content 的子对象。
    3. 确保特效的渲染顺序在 ScrollView 的内容区域之上。

2. 帧动画代替动效

缺点:效果打折

3. 调整特效的渲染顺序

确保特效的渲染顺序在 ScrollView 的内容区域之上。可以通过调整特效的 Sorting LayerOrder in Layer 来实现。

  • 步骤
    1. 选择特效对象(如粒子系统)。
    2. Renderer 组件中设置 Sorting LayerOrder in Layer,确保其值高于 ScrollView 的内容区域。

4. 使用 RawImage 或 Image 组件

如果特效是基于纹理的(如 Shader 效果),可以使用 RawImageImage 组件来显示特效。

  • 步骤
    1. ScrollView 的内容区域中添加一个 RawImageImage 组件。
    2. 将特效的纹理(如 RenderTexture)赋值给 RawImageImagetexture 属性。
    3. 确保 RawImageImageRectTransform 设置正确,以便在 ScrollView 中显示。

5. 使用 RenderTexture

如果特效是基于摄像机的(如全屏特效),可以使用 RenderTexture 来捕捉特效并在 UI 中显示。

  • 步骤
    1. 创建一个新的 RenderTexture
    2. 在一个摄像机上设置该 RenderTexture 作为目标纹理。
    3. ScrollView 中添加一个 RawImage,并将 RenderTexture 赋值给它的 texture 属性。

6. 使用 Shader 和 Stencil Buffer

如果你需要更复杂的裁剪效果,可以使用 Shader 和 Stencil Buffer 来实现。

  • 步骤
    1. 编写一个自定义 Shader,使用 Stencil Buffer 来控制特效的显示区域。
    2. 将该 Shader 应用到特效的材质上。
    3. 确保特效的渲染顺序和 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. 创建材质

  1. Assets 文件夹中右键点击,选择 Create > Material,命名为 ClippingMaterial
  2. 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. 设置场景

  1. 在场景中创建一个 ScrollView
  2. ScrollViewViewport 下创建一个 ImageRawImage,并将 ClippingMaterial 赋值给它的材质。
  3. ScrollViewClipping 脚本添加到 ScrollView 对象上,并在 Inspector 中将 ScrollViewRectTransformClippingMaterial 赋值给相应的字段。

5. 测试

运行场景并滚动 ScrollView,你应该会看到通过 Shader 实现的裁剪效果。特效或内容在 ScrollView 的视口外部将变得透明。

总结

通过以上步骤,你可以在 Unity 中使用 Shader 实现 ScrollView 的裁剪效果。这个方法提供了灵活的视觉效果,并且可以根据需要进行扩展和修改。你可以根据具体需求调整 Shader 的逻辑,以实现更复杂的裁剪效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你一身傲骨怎能输

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值