using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Xml.Serialization; using UnityEditor; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; using Object = UnityEngine.Object; // 暂时不考虑完全替换内置资源 // 仅仅替换内置Shader,防止Shader.Parse // 其实是替换一些Material和Texture套组 public static class BuiltinAssetBuilder { public const string replacementPath = "Assets/BuiltinAssets"; public const string configPath = replacementPath + "/config.txt"; private const string _texturePath = replacementPath + "/Texture"; private const string _materialPath = replacementPath + "/Material"; private const string _shaderPath = replacementPath + "/Shader"; public static BuiltinFileRecord CopyMaterial(Material material) { var instance = new Material(material); var assetPath = _materialPath.Open(material.name); assetPath = ChangeExtension(assetPath, ".mat"); BuildFilePath(assetPath); AssetDatabase.Refresh(); AssetDatabase.CreateAsset(instance, assetPath); return new BuiltinFileRecord(material, instance); } public static BuiltinFileRecord CopySprite(Sprite sprite) { var importer = SaveTextureToPath(sprite.texture); importer.textureType = TextureImporterType.Sprite; importer.SaveAndReimport(); return new BuiltinFileRecord(sprite, AssetDatabase.LoadAssetAtPath(importer.assetPath)); } public static BuiltinFileRecord CopyTexture2D(Texture2D texture) { var importer = SaveTextureToPath(texture); importer.textureType = TextureImporterType.Default; importer.SaveAndReimport(); return new BuiltinFileRecord(texture, AssetDatabase.LoadAssetAtPath(importer.assetPath)); } public static BuiltinFileRecord GetShaderReplace(List replaceList, Shader shader) { BuiltinFileRecord record = null; for (var i = 0; i < replaceList.Count; i++) { var replace = replaceList[i]; if (replace.name == shader.name) { record = new BuiltinFileRecord(shader, replace); break; } } return record; } private static TextureImporter SaveTextureToPath(Texture2D source) { var renderTex = RenderTexture.GetTemporary(source.width, source.height); Graphics.Blit(source, renderTex); var texture = new Texture2D(renderTex.width, renderTex.height, TextureFormat.RGBA32, false); RenderTexture.active = renderTex; texture.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0); RenderTexture.active = null; RenderTexture.ReleaseTemporary(renderTex); var assetPath = _texturePath.Open(source.name); assetPath = ChangeExtension(assetPath, ".png"); var filePath = BuildFilePath(assetPath); var binary = texture.EncodeToPNG(); Object.DestroyImmediate(texture); File.WriteAllBytes(filePath, binary); AssetDatabase.Refresh(); var importer = AssetImporter.GetAtPath(assetPath) as TextureImporter; return importer; } public static void ExtractBuiltinAssets() { var spriteList = new List(); var materialList = new List(); var textureList = new List(); var shaderList = new List(); var unityAssets = AssetDatabase.LoadAllAssetsAtPath("Resources/unity_builtin_extra"); for (var i = 0; i < unityAssets.Length; i++) { var asset = unityAssets[i]; var sprite = asset as Sprite; if (sprite) { spriteList.Add(sprite); continue; } var material = asset as Material; if (material) { materialList.Add(material); continue; } var texture = asset as Texture2D; if (texture) { textureList.Add(texture); continue; } var shader = asset as Shader; if (shader) { shaderList.Add(shader); } } for (var i = textureList.Count - 1; i >= 0; i--) { var texture = textureList[i]; for (var j = 0; j < spriteList.Count; j++) if (texture.name == spriteList[j].name) { textureList.RemoveAt(i); break; } } var shaderReplaceList = new List(); var shaderReplacePaths = from assetPath in AssetDatabase.GetAllAssetPaths() where assetPath.StartsWith(_shaderPath) where Path.GetExtension(assetPath) == ".shader" select assetPath; foreach (var assetPath in shaderReplacePaths) { var shader = AssetDatabase.LoadAssetAtPath(assetPath); if (shader) shaderReplaceList.Add(shader); } var recordList = new List(); for (var i = 0; i < spriteList.Count; i++) recordList.Add(CopySprite(spriteList[i])); for (var i = 0; i < materialList.Count; i++) recordList.Add(CopyMaterial(materialList[i])); for (var i = 0; i < textureList.Count; i++) recordList.Add(CopyTexture2D(textureList[i])); for (var i = 0; i < shaderList.Count; i++) { var record = GetShaderReplace(shaderReplaceList, shaderList[i]); if (record != null) recordList.Add(record); } var serializer = new XmlSerializer(typeof(List)); var configFilePath = EditorCommonUtility.AssetToFilePath(configPath); using (var fs = File.Create(configFilePath)) { serializer.Serialize(fs, recordList); } UpdateBuiltinMaterial(recordList); } public static void ReplaceForAssetPaths(IList assetPaths) { var config = LoadConfig(); if (config != null) { for (var i = 0; i < assetPaths.Count; i++) { EditorUtility.DisplayProgressBar("替换内置资源中", string.Format("{0} / {1}", i, assetPaths.Count), (float)i / assetPaths.Count); try { var assetPath = assetPaths[i]; var assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath); // Scene在执行分解的流程中进行替换 // if (assetType == typeof(SceneAsset)) // { // var scene = EditorSceneManager.OpenScene(assetPath); // ReplaceForOneScene(scene, config); // } // else if (assetType == typeof(GameObject)) { var extension = Path.GetExtension(assetPath); if (extension == ".prefab") ReplaceForOneGameObject(AssetDatabase.LoadAssetAtPath(assetPath), config); } else if (assetType == typeof(Material)) ReplaceForOneMaterial(AssetDatabase.LoadAssetAtPath(assetPath), config); } catch (Exception e) { Debug.LogError(e.ToString()); } } Debug.LogWarning("Replace Builtin Assets Finish!"); EditorUtility.ClearProgressBar(); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } } public static void ReplaceForPath(string pathHeader) { var assetPaths = (from assetPath in AssetDatabase.GetAllAssetPaths() where assetPath.StartsWith(pathHeader) where !assetPath.StartsWith("Assets/StreamingAssets") select assetPath).ToArray(); ReplaceForAssetPaths(assetPaths); } private static string BuildFilePath(string assetPath) { var filePath = EditorCommonUtility.AssetToFilePath(assetPath); var directory = Path.GetDirectoryName(filePath); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) Directory.CreateDirectory(directory); return filePath; } private static string ChangeExtension(string assetPath, string extension) { var oldExtension = Path.GetExtension(assetPath); if (!string.IsNullOrEmpty(oldExtension)) assetPath = assetPath.Remove(assetPath.Length - oldExtension.Length); return assetPath + extension; } private static void UpdateBuiltinMaterial(IList config) { var materialPaths = from assetPath in AssetDatabase.GetAllAssetPaths() where assetPath.StartsWith(_materialPath) where Path.GetExtension(assetPath) == ".mat" select assetPath; foreach (var materialPath in materialPaths) { var material = AssetDatabase.LoadAssetAtPath(materialPath); if (material != null) ReplaceForOneMaterial(material, config); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } private static bool ReplaceOneRenderer(Renderer component, IList config) { var dirty = false; var materials = component.sharedMaterials; for (var i = 0; i < materials.Length; i++) { if (materials[i] != null) { var replace = GetReplaceAsset(materials[i], config); if (replace != null) { dirty = true; materials[i] = replace; } } } if (dirty) component.sharedMaterials = materials; return dirty; } private static bool ReplaceOneImage(Image image, IList config) { var dirty = ReplaceOneGraphic(image, config); if (image.sprite != null) { var replace = GetReplaceAsset(image.sprite, config); if (replace != null) { dirty = true; image.sprite = replace; } } return dirty; } private static bool ReplaceOneRawImage(RawImage rawImage, IList config) { var dirty = ReplaceOneGraphic(rawImage, config); if (rawImage.texture != null) { var replace = GetReplaceAsset(rawImage.texture, config); if (replace != null) { dirty = true; rawImage.texture = replace; } } return dirty; } private static bool ReplaceOneGraphic(Graphic graphic, IList config) { var dirty = false; if (graphic.material != null) { var replace = GetReplaceAsset(graphic.material, config); if (replace != null) { dirty = true; graphic.material = replace; } } return dirty; } private static void ReplaceForOneMaterial(Material source, IList config) { var dirty = false; var shader = source.shader; var newShader = Shader.Find(shader.name); if (shader != newShader) { dirty = true; source.shader = newShader; } var count = ShaderUtil.GetPropertyCount(source.shader); for (var i = 0; i < count; i++) if (ShaderUtil.GetPropertyType(source.shader, i) == ShaderUtil.ShaderPropertyType.TexEnv) { var propertyName = ShaderUtil.GetPropertyName(source.shader, i); var texture = source.GetTexture(propertyName); if (texture != null) { var texturePath = GetReplacePath(texture, config); if (!string.IsNullOrEmpty(texturePath)) { var replace = AssetDatabase.LoadAssetAtPath(texturePath); if (replace != null) { dirty = true; source.SetTexture(propertyName, replace); } } } } if (dirty) EditorUtility.SetDirty(source); } private static void ReplaceForOneGameObject(GameObject prefab, IList config) { var dirty = false; var renderers = prefab.GetComponentsInChildren(true); for (var i = 0; i < renderers.Length; i++) if (ReplaceOneRenderer(renderers[i], config)) dirty = true; var graphics = prefab.GetComponentsInChildren(true); for (var i = 0; i < graphics.Length; i++) { var image = graphics[i] as Image; if (image) { if (ReplaceOneImage(image, config)) dirty = true; continue; } var rawImage = graphics[i] as RawImage; if (rawImage) { if (ReplaceOneRawImage(rawImage, config)) dirty = true; continue; } if (ReplaceOneGraphic(graphics[i], config)) dirty = true; } if (dirty) EditorUtility.SetDirty(prefab); } public static void ReplaceForOneScene(Scene scene) { var config = LoadConfig(); if (config != null) { var rootObjs = scene.GetRootGameObjects(); for (var i = 0; i < rootObjs.Length; i++) ReplaceForOneGameObject(rootObjs[i], config); } } private static T GetReplaceAsset(T source, IList config) where T : Object { T result = null; var replacePath = GetReplacePath(source, config); if (!string.IsNullOrEmpty(replacePath)) result = AssetDatabase.LoadAssetAtPath(replacePath); return result; } private static string GetReplacePath(Object source, IList config) { var typeString = source.GetType().ToString(); var fileId = BuiltinFileRecord.GetFileId(source); BuiltinFileRecord record = null; for (var i = 0; i < config.Count; i++) if (config[i].assetType == typeString && config[i].sourceId == fileId) { record = config[i]; break; } return record == null ? string.Empty : record.replacePath; } private static List LoadConfig() { List result = null; var filePath = EditorCommonUtility.AssetToFilePath(configPath); if (File.Exists(filePath)) { var serializer = new XmlSerializer(typeof(List)); using (var fs = File.OpenRead(filePath)) { result = serializer.Deserialize(fs) as List; } if (result == null) Debug.LogError("Builtin Assets Config 反序列化失败!"); } else Debug.LogError("Builtin Assets Config 文件未建立!"); return result; } } [Serializable] public class BuiltinFileRecord { private static readonly PropertyInfo _inspectorMode = typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance); public string assetType; public string name; public string replacePath; public long sourceId; // 反序列化接口 public BuiltinFileRecord() { } public BuiltinFileRecord(Object oldAsset, Object replaceAsset) { name = oldAsset.name; assetType = oldAsset.GetType().ToString(); sourceId = GetFileId(oldAsset); replacePath = AssetDatabase.GetAssetPath(replaceAsset); } public static long GetFileId(Object asset) { var serializedObject = new SerializedObject(asset); _inspectorMode.SetValue(serializedObject, InspectorMode.Debug, null); var localIdProp = serializedObject.FindProperty("m_LocalIdentfierInFile"); return localIdProp.longValue; } }