using System; using System.IO; using System.Linq; using BundleV2; using UnityEditor; using UnityEngine; using UnityEngine.SceneManagement; using Object = UnityEngine.Object; /// /// 通用的资源配置清理以及资源修改的类型 /// public static class PurgeAssets { [MenuItem("Bundle V2/Purge/Audio")] public static void PurgeAudio() { var audioList = (from assetPath in AssetDatabase.GetAllAssetPaths() where assetPath.StartsWith(AssetConst.nonInternalHeader) where EditorAssetConst.audioExtensions.Contains(Path.GetExtension(assetPath)) let importer = AssetImporter.GetAtPath(assetPath) as AudioImporter where importer select importer).ToArray(); var count = 0; for (var i = 0; i < audioList.Length; i++) { EditorUtility.DisplayProgressBar("Purge Audio", string.Format("{0} / {1}", i + 1, audioList.Length), (float) i / audioList.Length); var audioImporter = audioList[i]; var dirty = false; var defaultSettings = audioImporter.defaultSampleSettings; var audioClip = AssetDatabase.LoadAssetAtPath(audioImporter.assetPath); // 音乐或者环境音效 if (audioClip.length > 10f) { if (defaultSettings.compressionFormat != AudioCompressionFormat.Vorbis) { dirty = true; defaultSettings.compressionFormat = AudioCompressionFormat.Vorbis; } if (defaultSettings.loadType != AudioClipLoadType.Streaming) { dirty = true; defaultSettings.loadType = AudioClipLoadType.Streaming; } } // 持续类技能或者语音 else if (audioClip.length > 3f) { if (defaultSettings.compressionFormat != AudioCompressionFormat.ADPCM) { dirty = true; defaultSettings.compressionFormat = AudioCompressionFormat.ADPCM; } if (defaultSettings.loadType != AudioClipLoadType.CompressedInMemory) { dirty = true; defaultSettings.loadType = AudioClipLoadType.CompressedInMemory; } } // 瞬发技能 else { if (defaultSettings.compressionFormat != AudioCompressionFormat.ADPCM) { dirty = true; defaultSettings.compressionFormat = AudioCompressionFormat.ADPCM; } if (defaultSettings.loadType != AudioClipLoadType.DecompressOnLoad) { dirty = true; defaultSettings.loadType = AudioClipLoadType.DecompressOnLoad; } } if (defaultSettings.quality != 0.7f) { dirty = true; defaultSettings.quality = 0.7f; } if (dirty) { count++; audioImporter.defaultSampleSettings = defaultSettings; audioImporter.SaveAndReimport(); } } EditorUtility.ClearProgressBar(); Debug.LogWarning(string.Format("Purge Audio process on {0} files!", count)); } [MenuItem("ProTool/PurgeAssets/NormalMap", false, 4)] public static void PurgeNormalMaps() { FixRgb(); } [MenuItem("Bundle V2/Purge/Materials")] public static void PurgeMaterials() { var materialPaths = (from materialPath in AssetDatabase.GetAllAssetPaths() where materialPath.IsCommonAssetPath() where ".mat".Equals(Path.GetExtension(materialPath), StringComparison.OrdinalIgnoreCase) select materialPath).ToArray(); for (var i = 0; i < materialPaths.Length; i++) { var materialPath = materialPaths[i]; EditorUtility.DisplayProgressBar("Purge Material " + materialPath, string.Format("{0} / {1}", i, materialPaths.Length), (float) i / materialPaths.Length); var material = AssetDatabase.LoadAssetAtPath(materialPath); if (material != null) PurgeOneMaterial(material); } EditorUtility.ClearProgressBar(); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } [MenuItem("ProTool/PurgeAssets/Components/Start", false, 4)] public static void PurgeComponentsStart() { var action = new PurgeComponentAction(false); action.Start(); } [MenuItem("ProTool/PurgeAssets/Components/Continue", false, 4)] public static void PurgeComponentsDelta() { var action = new PurgeComponentAction(true); action.Start(); } [MenuItem("ProTool/PurgeAssets/PlatformSettings", false, 4)] public static void PurgePlatformSettings() { var action = new PurgeTexturePlatform(); action.Start(); } [MenuItem("ProTool/PurgeAssets/AssetNames", false, 4)] public static void PurgeAssetNames() { var assetPaths = (from assetPath in AssetDatabase.GetAllAssetPaths() where assetPath.IsCommonAssetPath() let extension = Path.GetExtension(assetPath) where !string.IsNullOrEmpty(extension) && !extension.Equals(".cs") let fileName = Path.GetFileName(assetPath) where !string.IsNullOrEmpty(fileName) && fileName.Contains(' ') select fileName).ToArray(); for (var i = 0; i < assetPaths.Length; i++) { var assetPath = assetPaths[i]; var dir = assetPath.MoveUp(); var fileName = assetPath.Substring(dir.Length); fileName = fileName.Replace(' ', '_'); fileName = dir.Open(fileName); AssetDatabase.MoveAsset(assetPath, fileName); } } [MenuItem("Bundle V2/Purge/Mesh Materials")] public static void PurgeMeshMaterials() { var meshMaterialAction = new PurgeMeshMaterialAction(); meshMaterialAction.Start(); } [MenuItem("Scene/Purge Components", false, 4)] public static void PurgeSceneComponents() { var rootObjects = SceneManager.GetActiveScene().GetRootGameObjects(); for (var i = 0; i < rootObjects.Length; i++) { var rootObject = rootObjects[i]; PurgeOneGameObject(rootObject.transform); } } private static bool ParticleHasExtraMaterial(ParticleSystemRenderer particle) { var result = false; for (var i = 2; i < particle.sharedMaterials.Length; i++) if (particle.sharedMaterials[i] == null) { result = true; break; } return result; } private static bool ParticleNotFunctional(ParticleSystemRenderer renderer, ParticleSystem particle) { var result = renderer.sharedMaterial == null; if (result) result = !particle.emission.enabled; return result; } public static bool PurgeOneGameObject(string assetPath) { var result = false; var prefab = AssetDatabase.LoadAssetAtPath(assetPath); if (prefab != null) { var instance = Object.Instantiate(prefab); if (PurgeOneGameObject(instance.transform)) { result = true; Debug.LogWarning("Purge Successful at " + assetPath); PrefabUtility.ReplacePrefab(instance, prefab, ReplacePrefabOptions.ReplaceNameBased); } else { EditorUtility.SetDirty(prefab); } Object.DestroyImmediate(instance); } return result; } public static bool PurgeOneGameObject(Transform root) { var components = root.GetComponents(); var componentDirty = false; for (var i = 0; i < components.Length; i++) if (components[i] == null) { componentDirty = true; break; } if (componentDirty) { var serializedObject = new SerializedObject(root.gameObject); var property = serializedObject.FindProperty("m_Component"); if (property.isArray && property.arraySize == components.Length) { for (var i = components.Length - 1; i >= 0; i--) if (components[i] == null) property.DeleteArrayElementAtIndex(i); } else { Debug.LogError("异常情况 - 序列化组件数目同实际组件不匹配!"); } serializedObject.ApplyModifiedProperties(); } var particleDirty = false; var particleRenderer = root.GetComponent(); var particleSystem = root.GetComponent(); if (particleRenderer != null && particleSystem != null) { if (ParticleNotFunctional(particleRenderer, particleSystem)) { particleDirty = true; Object.DestroyImmediate(particleSystem); } else { if (ParticleHasExtraMaterial(particleRenderer)) { particleDirty = true; var count = Mathf.Min(particleRenderer.sharedMaterials.Length, 2); var array = new Material[count]; for (var j = 0; j < count; j++) array[j] = particleRenderer.sharedMaterials[j]; particleRenderer.sharedMaterials = array; } if (particleRenderer.renderMode != ParticleSystemRenderMode.Mesh && particleRenderer.mesh != null) { particleDirty = true; particleRenderer.mesh = null; } } } var dirty = componentDirty || particleDirty; foreach (Transform child in root) if (PurgeOneGameObject(child)) dirty = true; return dirty; } public static void PurgeOneMaterial(Material material) { if (material.shader != null) { var shaderPropertiesCount = ShaderUtil.GetPropertyCount(material.shader); var shaderProperties = new string[shaderPropertiesCount]; for (var i = 0; i < shaderPropertiesCount; i++) shaderProperties[i] = ShaderUtil.GetPropertyName(material.shader, i); var serializedObject = new SerializedObject(material); // ReSharper disable once ReplaceWithSingleAssignment.False var dirty = false; if (PurgeOneMaterialProperty(serializedObject, shaderProperties, "m_SavedProperties.m_TexEnvs")) dirty = true; if (PurgeOneMaterialProperty(serializedObject, shaderProperties, "m_SavedProperties.m_Floats")) dirty = true; if (PurgeOneMaterialProperty(serializedObject, shaderProperties, "m_SavedProperties.m_Colors")) dirty = true; if (dirty) { Debug.LogWarning("Purge Material " + AssetDatabase.GetAssetPath(material)); serializedObject.ApplyModifiedProperties(); } EditorUtility.SetDirty(material); } } private static bool PurgeOneMaterialProperty(SerializedObject serializedObject, string[] allowProperties, string propertyName) { var dirty = false; var properties = serializedObject.FindProperty(propertyName); if (properties != null && properties.isArray) for (var i = properties.arraySize - 1; i >= 0; i--) { var propName = properties.GetArrayElementAtIndex(i).displayName; var exist = allowProperties.Contain(propName); if (!exist) { dirty = true; properties.DeleteArrayElementAtIndex(i); } } return dirty; } public static void FixRgb() { var pngPaths = (from path in AssetDatabase.GetAllAssetPaths() let extension = Path.GetExtension(path) where !string.IsNullOrEmpty(extension) && extension.Equals(".png", StringComparison.InvariantCultureIgnoreCase) select path).ToArray(); var helper = new DtxNormalFix(pngPaths); helper.Start(); } private class DtxNormalFix { private readonly string[] _pngPaths; private int _index; public DtxNormalFix(string[] paths) { _pngPaths = paths; } public void Start() { _index = 0; EditorApplication.update += OnEditorUpdate; } private void OnEditorUpdate() { try { if (_index < _pngPaths.Length) { var path = _pngPaths[_index]; NormalConvert.ConvertOneNormal(path); _index++; Debug.LogWarning("Finish " + _index + " / " + _pngPaths.Length); } else { Debug.LogWarning("Finish All"); EditorApplication.update = null; } } catch (Exception e) { Debug.LogError(e.ToString()); EditorApplication.update = null; } } } private class PurgeComponentAction : EditorPerFrameActionBase { private const string _deltaKey = "PurgeComponent"; private readonly string[] _assetPaths; private int _index; public PurgeComponentAction(bool useDelta) { _assetPaths = (from assetPath in AssetDatabase.GetAllAssetPaths() where assetPath.IsCommonAssetPath() where ".prefab".Equals(Path.GetExtension(assetPath)) orderby assetPath select assetPath).ToArray(); if (useDelta) _index = PlayerPrefs.GetInt(_deltaKey, default(int)); else PlayerPrefs.SetInt(_deltaKey, _index); } protected override int GetCurrentIndex() { return _index; } protected override int GetTotalCount() { return _assetPaths.Length; } protected override void PerFrameAction() { var assetPath = _assetPaths[_index]; if (PurgeOneGameObject(assetPath)) { AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); // 需要高密度的清空,防止溢出 Resources.UnloadUnusedAssets(); } PlayerPrefs.SetInt(_deltaKey, _index); _index++; } } private class PurgeTexturePlatform : EditorPerFrameActionBase { private readonly string[] _assetPaths; private int _index; public PurgeTexturePlatform() { _assetPaths = (from assetPath in AssetDatabase.GetAllAssetPaths() where assetPath.IsCommonAssetPath() let assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath) where assetType == typeof(Texture2D) || assetType == typeof(Sprite) select assetPath).ToArray(); } protected override int GetCurrentIndex() { return _index; } protected override int GetTotalCount() { return _assetPaths.Length; } protected override void PerFrameAction() { var assetPath = _assetPaths[_index]; _index++; var importer = AssetImporter.GetAtPath(assetPath) as TextureImporter; if (importer != null) { importer.ClearPlatformTextureSettings(PlatformName.GetCurrentPlatformName()); importer.SaveAndReimport(); } } } private class PurgeMeshMaterialAction : EditorPerFrameActionBase { private readonly string[] _assetPaths; private readonly Material _dummy; private int _count; private int _index; public PurgeMeshMaterialAction() { _assetPaths = (from assetPath in AssetDatabase.GetAllAssetPaths() where assetPath.StartsWith(AssetConst.nonInternalHeader) where AssetConst.modelExtension.Equals(Path.GetExtension(assetPath), StringComparison.OrdinalIgnoreCase) select assetPath).ToArray(); _dummy = AssetDatabase.LoadAssetAtPath("Assets/Project/GameRes/Material/shadow1" + AssetConst.materialExtension); } protected override int GetCurrentIndex() { return _index; } protected override int GetTotalCount() { return _assetPaths.Length; } protected override void PerFrameAction() { var assetPath = _assetPaths[_index]; var importer = AssetImporter.GetAtPath(assetPath) as ModelImporter; if (importer && !importer.importMaterials) { _count++; importer.importMaterials = true; importer.materialName = ModelImporterMaterialName.BasedOnMaterialName; importer.materialLocation = ModelImporterMaterialLocation.InPrefab; importer.materialSearch = ModelImporterMaterialSearch.Local; // 刷新数据,以便或者真实的材质名词 importer.SaveAndReimport(); AddRemapMaterials(importer); Debug.LogWarning("Purge Fbx Material from not import for " + assetPath); importer.SaveAndReimport(); Resources.UnloadUnusedAssets(); } // else if (importer.materialLocation == ModelImporterMaterialLocation.InPrefab) // { // var update = false; // if (importer.materialName != ModelImporterMaterialName.BasedOnMaterialName || // importer.materialSearch != ModelImporterMaterialSearch.Local) // { // update = true; // } // else // { // var external = importer.GetExternalObjectMap(); // foreach (var keyValue in external) // if (keyValue.Key.type == typeof(Material)) // { // var material = keyValue.Value as Material; // if (material) // { // if (!material.shader) // { // update = true; // break; // } // // if (material.shader.name == "Standard") // { // update = true; // break; // } // } // } // } // // if (update) // { // importer.materialName = ModelImporterMaterialName.BasedOnMaterialName; // importer.materialSearch = ModelImporterMaterialSearch.Local; // importer.materialLocation = ModelImporterMaterialLocation.InPrefab; // importer.SaveAndReimport(); // AddRemapMaterials(importer); // Debug.LogWarning("Purge Fbx Material from not dummy for " + assetPath); // importer.SaveAndReimport(); // Resources.UnloadUnusedAssets(); // } // } _index++; } private void AddRemapMaterials(ModelImporter importer) { var prefab = AssetDatabase.LoadAssetAtPath(importer.assetPath); var rendererArray = prefab.GetComponentsInChildren(true); foreach (var renderer in rendererArray) { var materials = renderer.sharedMaterials; for (var i = 0; i < materials.Length; i++) if (materials[i]) { var name = materials[i].name; importer.AddRemap( new AssetImporter.SourceAssetIdentifier(typeof(Material), name), _dummy); } } } protected override void OnFinish() { Debug.LogWarning("Purge Fbx Count: " + _count); base.OnFinish(); } } }