[Unity]UGUI ScrollView ui特效遮擋的問題
阿新 • • 發佈:2019-02-17
當使用滾動列表的時候,例如商城,好友列表。我們要在每個列表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特效上後:執行看到如下的效果,多餘的已被裁剪: