类似下面这样的层级结构,白色区域为ScrollView可见区域,RectMask2D添加在ScrollView上。
可以看到Canvas下的Image1没被裁剪掉,不在Canvas下的Image2裁剪掉了。
【原因分析】
RectMask2D内部有一个裁剪对象列表,只有在这个列表中的对象才会被裁剪,这边就是Image1没被添加到这个列表中造成的。
ugui的实现逻辑就是,如果MaskableGraphic对象和RectMask2D层级间,如果出现了Canvas,那这个对象就不会添加进去。
具体的实现可以查看:MaskableGraphic.UpdateClipParent,在这个函数中确定被哪个RectMask2D裁剪(添加到它的裁剪对象列表中)
【解决思路】
添加一个代理类,这个类对象会被添加到RectMask2D的裁剪对象列表中,然后在收到裁剪调用的时候,我们把调用转发到Image1去。
1 [DisallowMultipleComponent] 2 public class ClipProxy : MaskableGraphic 3 { 4 //手动设定转发给哪几个MaskableGraphic, 如果不指定将获取所有子孙上的MaskableGraphic组件 5 [SerializeField] private MaskableGraphic[] _manualMaskables; 6 private List<MaskableGraphic> _maskableList; 7 8 public class ClippableEvent : UnityEvent<Rect, bool> {} 9 10 private ClippableEvent _OnCull = new ClippableEvent(); 11 public ClippableEvent onCull 12 { 13 get { return _OnCull; } 14 set { _OnCull = value; } 15 } 16 17 private ClippableEvent _OnSetClipRect = new ClippableEvent(); 18 public ClippableEvent onSetClipRect 19 { 20 get { return _OnSetClipRect; } 21 set { _OnSetClipRect = value; } 22 } 23 24 #region Empty4Raycast的功能 25 26 protected ClipProxy() 27 { 28 useLegacyMeshGeneration = false; 29 } 30 protected override void OnPopulateMesh(VertexHelper toFill) 31 { 32 toFill.Clear(); 33 } 34 35 #endregion 36 37 38 protected override void Awake() 39 { 40 base.Awake(); 41 42 if (null == _manualMaskables || 0 == _manualMaskables.Length) 43 { 44 _maskableList = new List<MaskableGraphic>(); 45 GetComponentsInChildren(false, _maskableList); 46 } 47 else 48 { 49 _maskableList = new List<MaskableGraphic>(_manualMaskables); 50 } 51 } 52 53 public override void Cull(Rect clipRect, bool validRect) 54 { 55 base.Cull(clipRect, validRect); 56 if (null != _maskableList) 57 { 58 for (var j = 0; j < _maskableList.Count; ++j) 59 { 60 var maskable = _maskableList[j]; 61 if (null != maskable & maskable != this) 62 maskable.Cull(clipRect, validRect); 63 } 64 } 65 } 66 67 public override void SetClipRect(Rect value, bool validRect) 68 { 69 base.SetClipRect(value, validRect); 70 if (null != _maskableList) 71 { 72 for (var j = 0; j < _maskableList.Count; ++j) 73 { 74 var maskable = _maskableList[j]; 75 if (null != maskable && maskable != this) 76 maskable.SetClipRect(value, validRect); 77 } 78 } 79 } 80 81 }
最终效果:
【关于IClippable接口】
void Cull(Rect clipRect, bool validRect)
void SetClipRect(Rect value, bool validRect)
这两个函数的参数类似,可能容易产生混淆。
Cull用于检测是否要把自己从渲染中剔除的,比如:可以在这边判断是否不在ScrollView的可见区域内,不在时可以从渲染中剔除,一定程度上提高渲染性能。
SetClipRect用于设置自己的裁剪区域,比如裁剪区域变大时,重新调整裁剪区域。或者裁剪区域没了,关掉裁剪区域。