Files
JJBB/Assets/Editor/Scripts/PurgeAssets.cs

595 lines
21 KiB
C#
Raw Normal View History

2024-08-23 15:49:34 +08:00
using System;
using System.IO;
using System.Linq;
using BundleV2;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
/// <summary>
/// 通用的资源配置清理以及资源修改的类型
/// </summary>
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<AudioClip>(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<Material>(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<GameObject>(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<Component>();
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<ParticleSystemRenderer>();
var particleSystem = root.GetComponent<ParticleSystem>();
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<Material>("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<GameObject>(importer.assetPath);
var rendererArray = prefab.GetComponentsInChildren<Renderer>(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();
}
}
}