异步加载场景后Unity反向遮罩图片失效问题排查
问题
异步加载场景后,自定义反向遮罩的UI元素无法显示,但禁用再重新启用该对象后就能正常显示。请问为什么自定义的反向遮罩类在异步加载场景后会失效?
对应的自定义反向遮罩图片类代码:
public class CutOutImage : Image { public override Material materialForRendering { get { var _material = new Material(base.materialForRendering); _material.SetInt("_StencilComp", (int)CompareFunction.NotEqual); return _material; } } }
原因与解决方案
核心原因
异步加载场景时,Unity UI的渲染初始化流程和同步加载存在差异:
materialForRendering属性只有在UI元素首次准备渲染时才会被Unity读取一次- 异步加载完成后,这个属性的自定义逻辑没有被触发,导致Unity依旧使用默认的模板对比参数,而非你设置的
NotEqual - 禁用再启用对象时,会强制UI元素重新走一遍渲染准备流程,这时
materialForRendering的自定义逻辑才会执行,模板参数被正确设置,遮罩也就正常生效了
修复方案
方案1:主动触发渲染刷新
在异步加载场景完成的回调里,给所有CutOutImage实例调用SetAllDirty()方法,强制刷新渲染状态:
SceneManager.LoadSceneAsync("你的场景名").completed += operation => { var allCutOutImages = FindObjectsOfType<CutOutImage>(); foreach (var img in allCutOutImages) { img.SetAllDirty(); } };
方案2:优化自定义类的实现
你当前的代码每次获取materialForRendering都会创建新Material,既浪费内存,也容易出现初始化时机问题。可以改成在初始化时就设置好模板参数:
public class CutOutImage : Image { protected override void Awake() { base.Awake(); // 如果是共享材质,先实例化再修改,避免影响其他UI元素 if (material != null && material.IsSharedMaterial) { material = new Material(material); } material?.SetInt("_StencilComp", (int)CompareFunction.NotEqual); } // 保留重写以支持动态调整场景 public override Material materialForRendering { get { var targetMat = base.materialForRendering; targetMat.SetInt("_StencilComp", (int)CompareFunction.NotEqual); return targetMat; } } }
方案3:绑定到对象启用事件
如果不想在场景加载回调里处理,可以给CutOutImage加个事件监听,在对象启用时强制刷新:
public class CutOutImage : Image { private void OnEnable() { SetAllDirty(); } public override Material materialForRendering { get { var _material = new Material(base.materialForRendering); _material.SetInt("_StencilComp", (int)CompareFunction.NotEqual); return _material; } } }
内容的提问来源于stack exchange,提问作者Kureyş Alp Kılıç
相关产品推荐
相关产品推荐

