之前写过一篇单个 UIPanel 对粒子的裁剪,地址是:https://www.cnblogs.com/jietian331/p/5075487.html
但项目中有时会遇到多个UIPanel,如下面问题,UISprite 被剪裁了,但粒子未被剪裁:
转载请注明出处:https://www.cnblogs.com/jietian331/p/10938837.html
这时解决问题的思路是,找出所有的有剪裁功能的 UIPanel,并求出它们剪裁区域的交集然后传到shader中,效果如下:
代码如下:
1 using System.Collections.Generic; 2 using UnityEngine; 3 4 namespace Modules.UI 5 { 6 // 对子节点下所有的粒子和模型进行剪裁,且支持多个 UIPanel 7 public class EffectClip : MonoBehaviour 8 { 9 List<Material> m_materials; 10 List<UIPanel> m_uiPanels; 11 UIRoot m_uiRoot; 12 13 14 #region Properties 15 16 UIRoot ObjUIRoot 17 { 18 get 19 { 20 if (!m_uiRoot) 21 m_uiRoot = GetComponentInParent<UIRoot>(); 22 return m_uiRoot; 23 } 24 } 25 26 // 找到所有粒子,模型的material 27 List<Material> Materials 28 { 29 get 30 { 31 if (m_materials == null) 32 { 33 m_materials = new List<Material>(); 34 35 // particle system 的剪裁 36 var particleSystems = GetComponentsInChildren<ParticleSystem>(); 37 for (int i = 0, j = particleSystems.Length; i < j; i++) 38 { 39 var ps = particleSystems[i]; 40 var mat = ps.GetComponent<Renderer>().material; 41 string shaderName = mat.shader.name + " 1"; // 所用shader,重写一份带剪裁功能的 42 Shader shader = Shader.Find(shaderName); 43 if (shader) 44 { 45 m_materials.Add(mat); 46 mat.shader = shader; 47 } 48 else 49 { 50 Debug.LogError("Shader not found, name: " + shaderName); 51 } 52 } 53 54 // mesh 的剪裁 55 var renders = GetComponentsInChildren<MeshRenderer>(); 56 for (int i = 0, j = renders.Length; i < j; i++) 57 { 58 var ps = renders[i]; 59 var mat = ps.material; 60 string shaderName = mat.shader.name + " 1"; // 所用shader,重写一份带剪裁功能的 61 Shader shader = Shader.Find(shaderName); 62 if (shader) 63 { 64 m_materials.Add(mat); 65 mat.shader = shader; 66 } 67 else 68 { 69 Debug.LogError("Shader not found, name: " + shaderName); 70 } 71 } 72 } 73 return m_materials; 74 } 75 } 76 77 List<UIPanel> Panels 78 { 79 get 80 { 81 if (m_uiPanels == null) 82 { 83 // 获取所有开启剪裁的 UIPanel 84 m_uiPanels = new List<UIPanel>(); 85 UIPanel[] panels = GetComponentsInParent<UIPanel>(); 86 for (int i = 0; i < panels.Length; i++) 87 { 88 UIPanel p = panels[i]; 89 if (p && p.clipping == UIDrawCall.Clipping.SoftClip) 90 { 91 m_uiPanels.Add(p); 92 } 93 } 94 } 95 return m_uiPanels; 96 } 97 } 98 99 #endregion 100 101 102 void Update() 103 { 104 var mats = Materials; 105 if (mats != null && mats.Count > 0) 106 { 107 for (int i = 0; i < m_materials.Count; i++) 108 { 109 Vector4 area = CalcClipArea(); 110 m_materials[i].SetVector("_Area", area); 111 } 112 } 113 } 114 115 // 整合所有 UIPanel 的剪裁区域 116 Vector4 CalcClipArea() 117 { 118 var panels = Panels; 119 float x = float.MinValue, y = float.MinValue, z = float.MaxValue, w = float.MaxValue; 120 121 // 求所有 UIPanel 剪裁区域的交集 122 for (int i = 0; i < panels.Count; i++) 123 { 124 UIPanel p = panels[i]; 125 Vector4 area = CalcClipArea(p); 126 127 // 求交集啊 128 if (area.x > x) 129 x = area.x; 130 if (area.y > y) 131 y = area.y; 132 if (area.z < z) 133 z = area.z; 134 if (area.w < w) 135 w = area.w; 136 } 137 138 return new Vector4(x, y, z, w); 139 } 140 141 // 计算单个 UIPanel 的剪裁区域 142 Vector4 CalcClipArea(UIPanel p) 143 { 144 float h = 2; 145 float temp = h / ObjUIRoot.activeHeight; 146 Vector3 offsetV3 = new Vector3() 147 { 148 x = p.clipOffset.x * temp, 149 y = p.clipOffset.y * temp, 150 }; 151 Vector3 worldPos = p.transform.position + offsetV3; 152 Vector4 clipRegion = p.finalClipRegion; 153 Vector4 nguiArea = new Vector4() 154 { 155 x = -clipRegion.z / 2, 156 y = -clipRegion.w / 2, 157 z = clipRegion.z / 2, 158 w = clipRegion.w / 2 159 }; 160 161 return new Vector4() 162 { 163 x = worldPos.x + nguiArea.x * temp, 164 y = worldPos.y + nguiArea.y * temp, 165 z = worldPos.x + nguiArea.z * temp, 166 w = worldPos.y + nguiArea.w * temp 167 }; 168 } 169 170 171 } 172 }