Unity逐GameObject调整材质Tiling出现编辑模式材质泄漏报错如何解决
解决方案
方案1:使用MaterialPropertyBlock(推荐,无材质实例、无泄漏、性能最优)
这个方案完全不需要创建独立材质实例,通过Unity内置的MaterialPropertyBlock为单个Renderer设置独有的纹理缩放参数,所有墙体可以共用同一个sharedMaterial,不会触发警告,也不会有材质泄漏问题,同时支持静态合批,性能远优于创建多份材质实例。
[ExecuteInEditMode] public class AutoTiler : MonoBehaviour { public float xScaleMult = 1; public float yScaleMult = 1; private MaterialPropertyBlock _propBlock; private MeshRenderer _renderer; private Vector3 _lastScale; private static readonly int MainTexScaleId = Shader.PropertyToID("_MainTex_ST"); void OnEnable() { _propBlock ??= new MaterialPropertyBlock(); _renderer ??= GetComponent<MeshRenderer>(); _lastScale = transform.localScale; UpdateTiling(); } void Update() { // 只有缩放变化时才更新,避免每帧无效计算 if (transform.localScale != _lastScale) { _lastScale = transform.localScale; UpdateTiling(); } } void UpdateTiling() { // 从Renderer获取当前的PropertyBlock,避免覆盖其他已设置的参数 _renderer.GetPropertyBlock(_propBlock); Vector2 tiling = new Vector2( Mathf.Max(transform.localScale.x, transform.localScale.z) * xScaleMult, transform.localScale.y * yScaleMult ); // _MainTex_ST的xy是Tiling,zw是Offset,这里Offset保持默认0即可 _propBlock.SetVector(MainTexScaleId, new Vector4(tiling.x, tiling.y, 0, 0)); // 赋值回Renderer _renderer.SetPropertyBlock(_propBlock); } }
该方案完全不会触发编辑模式的材质泄漏警告,不需要管理材质实例生命周期,每个墙体的Tiling参数完全独立,共用同一份原始材质即可。
方案2:主动管理独立材质实例(适用于需要修改更多材质参数的场景)
之前代码报错的核心原因是在编辑模式下访问renderer.material会触发Unity的隐式实例化逻辑,内置的泄漏检查会识别到这个非托管的实例,我们只需要主动管理材质实例的创建和销毁即可规避警告:
[ExecuteInEditMode] public class AutoTiler : MonoBehaviour { public float xScaleMult = 1; public float yScaleMult = 1; [SerializeField, HideInInspector] private Material _customMaterial; private MeshRenderer _renderer; private Vector3 _lastScale; void OnEnable() { _renderer ??= GetComponent<MeshRenderer>(); // 初始化自定义材质 if (_customMaterial == null) { // 主动实例化,不通过.material隐式创建 _customMaterial = new Material(_renderer.sharedMaterial); _customMaterial.name = $"{_renderer.sharedMaterial.name}_AutoTiled_{gameObject.GetInstanceID()}"; // 标记为跟随场景保存,避免编辑模式下丢失 _customMaterial.hideFlags = HideFlags.HideInHierarchy | HideFlags.DontSaveInBuild; // 赋值给sharedMaterial,不会触发隐式实例化警告 _renderer.sharedMaterial = _customMaterial; } _lastScale = transform.localScale; UpdateTiling(); } void OnDestroy() { // 显式销毁材质实例,避免泄漏 if (_customMaterial != null) { DestroyImmediate(_customMaterial); } } void Update() { if (transform.localScale != _lastScale) { _lastScale = transform.localScale; UpdateTiling(); } } void UpdateTiling() { _customMaterial.mainTextureScale = new Vector2( Mathf.Max(transform.localScale.x, transform.localScale.z) * xScaleMult, transform.localScale.y * yScaleMult ); } }
该方案主动控制材质实例的创建、标记、销毁流程,不会触发Unity的内置警告,也不会出现材质泄漏,同时保留了独立材质实例的需求。
内容的提问来源于stack exchange,提问作者schwem00
相关产品推荐
相关产品推荐

