如何修复Unity模型导入脚本的MissingReferenceException错误?
Unity AssetPostprocessor 模型导入脚本修复(解决MissingReferenceException)
脚本功能说明
该脚本用于处理Unity模型导入流程,执行以下操作:
- 创建新材质
- 从
Assets/Textures/{model_name}/路径为材质分配对应纹理 - 将材质保存至
Assets/Materials/{model_name}/{model_name}.mat - 为导入模型分配新材质
- 为名称含“collider”的子对象添加MeshCollider并禁用MeshRenderer
错误信息
MissingReferenceException: The object of type 'Transform' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
错误原因
问题出在EditorApplication.delayCall的回调逻辑中:模型导入过程中Unity会创建临时GameObject用于处理导入逻辑,导入完成后该临时对象会被销毁。你在回调中引用了类级别的materialTransform(指向临时对象的Transform),此时引用已失效,因此抛出异常。
修复后的完整代码
using UnityEngine; using UnityEditor; using System.IO; public class ModelImporterProcessor : AssetPostprocessor { void OnPostprocessModel(GameObject obj) { // 保存模型名称和资源路径,用于延迟回调中重新获取正式模型 string modelName = obj.name; string modelAssetPath = assetPath; // 创建新材质 Material newMaterial = new Material(Shader.Find("Standard")); // 加载对应纹理文件夹中的PNG纹理 string textureFolderPath = Path.Combine("Assets/Textures", modelName); if (Directory.Exists(textureFolderPath)) { string[] textureFiles = Directory.GetFiles(textureFolderPath, "*.png"); foreach (string textureFile in textureFiles) { string textureName = Path.GetFileNameWithoutExtension(textureFile); Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(textureFile); if (texture == null) continue; string[] parts = textureName.Split('_'); if (parts.Length > 2 && parts[0] == modelName) { string type = parts[2]; switch (type) { case "Albedo": newMaterial.SetTexture("_MainTex", texture); break; case "Metallic": newMaterial.SetTexture("_MetallicGlossMap", texture); break; case "Normal": case "NormalGL": newMaterial.SetTexture("_BumpMap", texture); break; case "Height": newMaterial.SetTexture("_ParallaxMap", texture); break; case "Occlusion": newMaterial.SetTexture("_OcclusionMap", texture); break; case "Emission": newMaterial.EnableKeyword("_EMISSION"); newMaterial.globalIlluminationFlags = MaterialGlobalIlluminationFlags.BakedEmissive; newMaterial.SetTexture("_EmissionMap", texture); break; } } } } // 创建材质保存目录 string materialDirectory = Path.Combine("Assets/Materials", modelName); if (!Directory.Exists(materialDirectory)) { Directory.CreateDirectory(materialDirectory); } // 处理碰撞体(直接操作临时对象,此时对象未被销毁) ApplyCollider(obj.transform); // 延迟保存材质并应用到正式模型 EditorApplication.delayCall += () => { // 保存材质到指定路径 string materialPath = Path.Combine(materialDirectory, $"{modelName}.mat"); AssetDatabase.CreateAsset(newMaterial, materialPath); AssetDatabase.Refresh(); // 通过资源路径重新加载导入后的正式模型对象 GameObject importedModel = AssetDatabase.LoadAssetAtPath<GameObject>(modelAssetPath); if (importedModel != null) { // 加载保存后的材质实例(确保是Asset中的序列化对象) Material savedMaterial = AssetDatabase.LoadAssetAtPath<Material>(materialPath); if (savedMaterial != null) { ApplyMaterial(importedModel.transform, savedMaterial); } } }; } void ApplyCollider(Transform obj) { if (obj.name.ToLower().Contains("collider")) { obj.gameObject.AddComponent<MeshCollider>(); } foreach (Transform child in obj) { if (child.name.ToLower().Contains("collider")) { MeshRenderer renderer = child.GetComponent<MeshRenderer>(); if (renderer != null) { renderer.enabled = false; } } ApplyCollider(child); } } void ApplyMaterial(Transform obj, Material targetMaterial) { MeshRenderer meshRenderer = obj.GetComponent<MeshRenderer>(); if (meshRenderer != null) { meshRenderer.material = targetMaterial; } foreach (Transform child in obj) { ApplyMaterial(child, targetMaterial); } } }
修复关键点
- 移除类级别Transform引用:不再保存临时模型的Transform,彻底避免引用失效问题
- 记录模型资源路径:利用
AssetPostprocessor自带的assetPath属性,在延迟回调中重新加载正式的模型对象 - 重新加载材质实例:材质保存后,通过资源路径加载Asset中的序列化材质,避免使用临时材质对象的潜在问题
- 增加空值检查:在纹理加载、组件获取环节增加非空判断,提升脚本稳定性
- 简化递归逻辑:移除原代码中冗余的LOD判断,直接递归所有子对象完成材质分配
内容的提问来源于stack exchange,提问作者Karan
相关产品推荐
相关产品推荐

