1722 lines
69 KiB
C#
1722 lines
69 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Reflection;
|
||
using System.Text;
|
||
using System.Text.RegularExpressions;
|
||
using System.Xml.Serialization;
|
||
using BundleV2;
|
||
using Games.GlobeDefine;
|
||
using GCGame.Table;
|
||
using UnityEditor;
|
||
using UnityEditor.Animations;
|
||
using UnityEngine;
|
||
using UnityEngine.Events;
|
||
using UnityEngine.Timeline;
|
||
using Object = UnityEngine.Object;
|
||
|
||
public static class ClassifyBundles
|
||
{
|
||
public const string dummyMaterialPath = "Assets/Project/GameRes/DummyMaterial";
|
||
public const string dependBundleHeader = "depend/";
|
||
public const string tablePath = "Assets/Project3D/BundleData/Tables3D/";
|
||
|
||
private static readonly Regex _bundleRegex = new Regex("(?<=^ assetBundleName: ).*$", RegexOptions.Multiline);
|
||
private static readonly Regex _variantRegex = new Regex("(?<=^ assetBundleVariant: ).*$", RegexOptions.Multiline);
|
||
private static readonly Regex _spriteRegex = new Regex("(?<=^ spritePackingTag: ).*$", RegexOptions.Multiline);
|
||
|
||
private static string uiPermanentBundle
|
||
{
|
||
get { return LoadAssetBundle.uiSpriteBundleHeader.Open("permanent"); }
|
||
}
|
||
|
||
private static void AddBundleDict(Dictionary<string, AssetImporterBundleSetting> bundleDict, string assetPath,
|
||
string bundleName = default(string))
|
||
{
|
||
if (string.IsNullOrEmpty(assetPath))
|
||
{
|
||
Debug.LogError("试图向BundleDict中添加空路径");
|
||
}
|
||
else
|
||
{
|
||
assetPath = assetPath.ToUrl();
|
||
AssetImporterBundleSetting bundleSetting;
|
||
if (!bundleDict.TryGetValue(assetPath, out bundleSetting))
|
||
{
|
||
bundleSetting = AssetImporterBundleSetting.Create(assetPath);
|
||
if (bundleSetting != null)
|
||
bundleDict.Add(assetPath, bundleSetting);
|
||
}
|
||
|
||
if (bundleSetting != null)
|
||
{
|
||
// 不主动制定Bundle名称的视为依赖包,将在展开时再执行配置
|
||
if (!string.IsNullOrEmpty(bundleName))
|
||
{
|
||
bundleName = bundleName.ToLower().ToUrl();
|
||
bundleSetting.bundleName = bundleName;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private static void ClassifyUiPrefabBundles(Dictionary<string, AssetImporterBundleSetting> bundleDic)
|
||
{
|
||
var assetPaths = GetAssetPathForType(LoadAssetBundle.assetPathHeader.Open("UI").Open("Prefab"), ".prefab");
|
||
var uiFileNames = new List<string>();
|
||
var uiPathInfos = typeof(UIInfo).GetFields(BindingFlags.Static | BindingFlags.Public);
|
||
foreach (var uiInfo in uiPathInfos)
|
||
if (uiInfo.FieldType == typeof(UIPathData))
|
||
{
|
||
var uiPathInfo = uiInfo.GetValue(null) as UIPathData;
|
||
if (uiPathInfo != null)
|
||
uiFileNames.Add(uiPathInfo.name);
|
||
}
|
||
|
||
for (var i = 0; i < assetPaths.Count; i++)
|
||
{
|
||
var assetPath = assetPaths[i];
|
||
EditorUtility.DisplayProgressBar("Bundle UI Prefab", assetPath, (float) i / assetPaths.Count);
|
||
var fileName = Path.GetFileNameWithoutExtension(assetPath);
|
||
if (uiFileNames.Contains(fileName))
|
||
{
|
||
var bundleName = GetUiPrefabBundleName(assetPath);
|
||
AddBundleDict(bundleDic, assetPath, bundleName);
|
||
CheckInvalidPath(assetPath, "第五版/通用UI元素/界面二级");
|
||
}
|
||
}
|
||
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
private static void ClassifyUiPrefabMarketingActs(Dictionary<string, AssetImporterBundleSetting> bundleDic)
|
||
{
|
||
var assetPaths =
|
||
GetAssetPathForType(LoadAssetBundle.assetPathHeader.Open("UI").Open("Prefab").Open("MarketingActs"),
|
||
".prefab");
|
||
for (var i = 0; i < assetPaths.Count; i++)
|
||
{
|
||
var assetPath = assetPaths[i];
|
||
EditorUtility.DisplayProgressBar("Bundle UI Marketing Prefab", assetPath, (float) i / assetPaths.Count);
|
||
var bundleName = GetUiPrefabBundleName(assetPath);
|
||
AddBundleDict(bundleDic, assetPath, bundleName);
|
||
CheckInvalidPath(assetPath, "第五版/通用UI元素/界面二级");
|
||
}
|
||
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
private static string GetUiPrefabBundleName(string assetPath)
|
||
{
|
||
var result = assetPath.Contains(LoadAssetBundle.BUNDLE_MAIN_BASE_UI)
|
||
? LoadAssetBundle.BUNDLE_MAIN_BASE_UI
|
||
: Path.GetFileNameWithoutExtension(assetPath);
|
||
result = ("ui/" + result).ToLower();
|
||
return result;
|
||
}
|
||
|
||
private static void ClassifyUiPrefabEmoji(Dictionary<string, AssetImporterBundleSetting> bundleDic)
|
||
{
|
||
var assetPaths = GetAssetPathForType(LoadAssetBundle.assetPathHeader.Open("UI").Open("Prefab").Open("Emoji"),
|
||
".prefab");
|
||
for (var i = 0; i < assetPaths.Count; i++)
|
||
{
|
||
var assetPath = assetPaths[i];
|
||
EditorUtility.DisplayProgressBar("Bundle UI Emoji Prefab", assetPath, (float) i / assetPaths.Count);
|
||
var bundleName = ("ui/emoji/" + Path.GetFileNameWithoutExtension(assetPath)).ToLower();
|
||
AddBundleDict(bundleDic, assetPath, bundleName);
|
||
CheckInvalidPath(assetPath, "第五版/通用UI元素/界面二级");
|
||
}
|
||
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
public static string GetStringHash(string bundlePath)
|
||
{
|
||
return unchecked((uint) Animator.StringToHash(bundlePath)).ToString();
|
||
}
|
||
|
||
private static void ClassifyUiSpriteBundles(Dictionary<string, AssetImporterBundleSetting> bundleDic)
|
||
{
|
||
// 2019.02.28:原“ui/sprite/commonitem”Bundle下资源按文件夹细分到各自Bundle;保持原有调用接口;
|
||
// 因此额外建立一个对比表格来转换原通用Bundle路径为各自路径;
|
||
// 运行时在AssetLoadManager中进行实质Bundle名转换
|
||
var assetPaths = GetAssetPathForType(LoadAssetBundle.uiSpriteAssetPath, string.Empty);
|
||
var pathConvert = new Dictionary<string, string>();
|
||
for (var i = 0; i < assetPaths.Count; i++)
|
||
{
|
||
var assetPath = assetPaths[i];
|
||
EditorUtility.DisplayProgressBar("Bundle UI Sprite", assetPath, (float) i / assetPaths.Count);
|
||
if (AssetDatabase.GetMainAssetTypeAtPath(assetPath) == typeof(Texture2D))
|
||
{
|
||
var bundlePath = Path.GetDirectoryName(assetPath);
|
||
var assetName = Path.GetFileNameWithoutExtension(assetPath);
|
||
System.Diagnostics.Debug.Assert(bundlePath != null, "bundleTail != null");
|
||
System.Diagnostics.Debug.Assert(assetName != null, "assetName != null");
|
||
assetName = assetName.ToLower();
|
||
bundlePath = bundlePath.Substring(LoadAssetBundle.uiSpriteAssetPath.Length + 1).ToLower().ToUrl();
|
||
// 如果包含中文或其他不被AssetBundle支持的名称,统一削减为hash码
|
||
if (EditorCommonUtility.ContainInvalidPath(bundlePath))
|
||
{
|
||
var temp = bundlePath;
|
||
bundlePath = GetStringHash(bundlePath);
|
||
Debug.LogWarning(string.Format("Asset Bundle 路径 {0} 包含非法字符,被校正为{1}"
|
||
, LoadAssetBundle.uiSpriteBundleHeader.Open(temp)
|
||
, LoadAssetBundle.uiSpriteBundleHeader.Open(bundlePath)));
|
||
}
|
||
|
||
bundlePath = LoadAssetBundle.uiSpriteBundleHeader.Open(bundlePath);
|
||
if (pathConvert.ContainsKey(assetName))
|
||
Debug.LogError(string.Format("{0} 拥有重复的资源名称 {1}", LoadAssetBundle.uiCommonSpriteBundle, assetName));
|
||
else
|
||
pathConvert[assetName] = bundlePath;
|
||
AddBundleDict(bundleDic, assetPath, bundlePath);
|
||
}
|
||
}
|
||
|
||
var builder = new StringBuilder();
|
||
// 注:AppendLine在不同平台可能留下不同的换行符,因此避开
|
||
foreach (var keyValue in pathConvert)
|
||
builder.Append(string.Format("{0}\t{1}\n", keyValue.Key, keyValue.Value));
|
||
// 注:暂时只有这一个特殊表,因此不用太多管理;有更多特殊表时,需要建立命名规则或者其他管理手段
|
||
var atlasTable = tablePath.Open(LoadAssetBundle.atlasPathConvertTable) + AssetConst.bytesExtension;
|
||
File.WriteAllText(EditorCommonUtility.AssetToFilePath(atlasTable), builder.ToString());
|
||
AssetDatabase.Refresh();
|
||
AddBundleDict(bundleDic, atlasTable, "tables");
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
private static void ClassifyUiTextureBundles(Dictionary<string, AssetImporterBundleSetting> bundleDic)
|
||
{
|
||
var assetPaths = GetAssetPathForType(LoadAssetBundle.assetPathHeader.Open("UI").Open("Texture"), string.Empty);
|
||
for (var i = 0; i < assetPaths.Count; i++)
|
||
{
|
||
var assetPath = assetPaths[i];
|
||
EditorUtility.DisplayProgressBar("Bundle UI Texture", assetPath, (float) i / assetPaths.Count);
|
||
if (AssetDatabase.GetMainAssetTypeAtPath(assetPath) == typeof(Texture2D))
|
||
{
|
||
var bundleName = GetNoHeaderNoExtensionName(assetPath);
|
||
AddBundleDict(bundleDic, assetPath, bundleName);
|
||
//SetTextureImporter(assetPath);
|
||
}
|
||
}
|
||
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
private static void ClassifySoundsBundles(Dictionary<string, AssetImporterBundleSetting> bundleDic)
|
||
{
|
||
var assetPaths = GetAssetPathForType(LoadAssetBundle.assetPathHeader.Open("Sounds"), string.Empty);
|
||
for (var i = 0; i < assetPaths.Count; i++)
|
||
{
|
||
var assetPath = assetPaths[i];
|
||
EditorUtility.DisplayProgressBar("Bundle Sounds", assetPath, (float) i / assetPaths.Count);
|
||
if (AssetDatabase.GetMainAssetTypeAtPath(assetPath) == typeof(AudioClip))
|
||
{
|
||
var bundleName = GetNoHeaderNoExtensionName(assetPath);
|
||
AddBundleDict(bundleDic, assetPath, bundleName);
|
||
}
|
||
}
|
||
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
private static string GetNoHeaderNoExtensionName(string assetPath)
|
||
{
|
||
var bundleName = assetPath.Substring(LoadAssetBundle.assetPathHeader.Length + 1);
|
||
var extension = Path.GetExtension(bundleName);
|
||
if (!string.IsNullOrEmpty(extension))
|
||
bundleName = bundleName.Remove(bundleName.Length - extension.Length);
|
||
bundleName = bundleName.ToLower();
|
||
return bundleName;
|
||
}
|
||
|
||
private static void ClassifySceneBundles(Dictionary<string, AssetImporterBundleSetting> bundleDic)
|
||
{
|
||
if (_sceneList == null)
|
||
RefreshSceneList();
|
||
System.Diagnostics.Debug.Assert(_sceneList != null, "_sceneList == null");
|
||
for (var i = 0; i < _sceneList.Count; i++)
|
||
{
|
||
var sceneName = _sceneList[i];
|
||
var assetPath = SceneDisassembly.sceneDisassembledPath.Open(sceneName);
|
||
EditorUtility.DisplayProgressBar("Bundle Scene", assetPath, (float) i / (_sceneList.Count + 1));
|
||
var bundleName = ("scene/" + Path.GetFileNameWithoutExtension(assetPath)).ToLower();
|
||
AddBundleDict(bundleDic, assetPath, bundleName);
|
||
var areaPath = SceneDisassembly.sceneDisassembledPath.Open(Path.GetFileNameWithoutExtension(sceneName)) +
|
||
"/";
|
||
var areaPaths = from areaAssetPath in AssetDatabase.GetAllAssetPaths()
|
||
where areaAssetPath.StartsWith(areaPath)
|
||
select areaAssetPath;
|
||
foreach (var areaAsset in areaPaths)
|
||
{
|
||
var subBundleName = ("scene/" + Path.GetFileNameWithoutExtension(areaAsset)).ToLower();
|
||
AddBundleDict(bundleDic, areaAsset, subBundleName);
|
||
}
|
||
}
|
||
|
||
// 2019.03.28 - RoleSelect的SubScene系统已经移除 - 单独标记表格上不配置的RoleSelect场景
|
||
// 2018.12.11 - RoleSelect场景将使用源文件。之前复制流程会导致RoleSelect在Bundle中存在两份
|
||
var roleSelectName = GlobeVar.sceneRole + SceneDisassembly.sceneExtension;
|
||
var roleSelect = (from assetPath in AssetDatabase.GetAllAssetPaths()
|
||
where assetPath.StartsWith(SceneDisassembly.sceneRoot)
|
||
where Path.GetFileName(assetPath) == roleSelectName
|
||
select assetPath).First();
|
||
if (roleSelect == null)
|
||
{
|
||
Debug.LogError(string.Format("Cannot find role select scene {0} in path {1}", roleSelectName,
|
||
SceneDisassembly.sceneRoot));
|
||
}
|
||
else
|
||
{
|
||
EditorUtility.DisplayProgressBar("Bundle Scene", roleSelect, 1f);
|
||
AddBundleDict(bundleDic, roleSelect, "scene/" + GlobeVar.sceneRole.ToLower());
|
||
}
|
||
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
private static void ClassifyModelBundles(Dictionary<string, AssetImporterBundleSetting> bundleDic)
|
||
{
|
||
var assetPaths = GetAssetPathForType(LoadAssetBundle.assetPathHeader.Open("Model"), AssetConst.prefabExtension);
|
||
for (var i = 0; i < assetPaths.Count; i++)
|
||
{
|
||
var assetPath = assetPaths[i];
|
||
EditorUtility.DisplayProgressBar("Bundle Model", assetPath, (float) i / assetPaths.Count);
|
||
var bundleName = ("model/" + Path.GetFileNameWithoutExtension(assetPath)).ToLower();
|
||
AddBundleDict(bundleDic, assetPath, bundleName);
|
||
}
|
||
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
private static void ClassifyEffectBundles(Dictionary<string, AssetImporterBundleSetting> bundleDic)
|
||
{
|
||
var assetPaths = GetAssetPathForType(LoadAssetBundle.assetPathHeader.Open("Effect"), AssetConst.prefabExtension);
|
||
for (var i = 0; i < assetPaths.Count; i++)
|
||
{
|
||
var assetPath = assetPaths[i];
|
||
EditorUtility.DisplayProgressBar("Bundle Effect", assetPath, (float) i / assetPaths.Count);
|
||
var bundleName = ("effect/" + Path.GetFileNameWithoutExtension(assetPath)).ToLower();
|
||
AddBundleDict(bundleDic, assetPath, bundleName);
|
||
}
|
||
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
private static void ClassifyOtherBundles(Dictionary<string, AssetImporterBundleSetting> bundleDic)
|
||
{
|
||
var assetPaths = GetAssetPathForType(LoadAssetBundle.assetPathHeader.Open("Other"), string.Empty);
|
||
for (var i = 0; i < assetPaths.Count; i++)
|
||
{
|
||
var assetPath = assetPaths[i];
|
||
if (!string.IsNullOrEmpty(Path.GetExtension(assetPath)))
|
||
{
|
||
EditorUtility.DisplayProgressBar("Bundle Other", assetPath, (float) i / assetPaths.Count);
|
||
var bundleName = ("other/" + Path.GetFileNameWithoutExtension(assetPath)).ToLower();
|
||
AddBundleDict(bundleDic, assetPath, bundleName);
|
||
}
|
||
}
|
||
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
private static void ClassifyGameRes(Dictionary<string, AssetImporterBundleSetting> bundleDic)
|
||
{
|
||
var assetPaths = GetAssetPathForType("Assets/Project/GameRes/", string.Empty);
|
||
for (var i = 0; i < assetPaths.Count; i++)
|
||
{
|
||
var assetPath = assetPaths[i];
|
||
if (!assetPath.StartsWith(dummyMaterialPath))
|
||
{
|
||
var extension = Path.GetExtension(assetPath);
|
||
if (!string.IsNullOrEmpty(extension) &&
|
||
!extension.Equals(".cginc", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
EditorUtility.DisplayProgressBar("Bundle GameRes", assetPath, (float) i / assetPaths.Count);
|
||
if (!string.IsNullOrEmpty(Path.GetExtension(assetPath)))
|
||
AddBundleDict(bundleDic, assetPath, LoadAssetBundle.gameResBundleName);
|
||
}
|
||
}
|
||
}
|
||
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
private static void ClassifyTable(Dictionary<string, AssetImporterBundleSetting> bundleDic)
|
||
{
|
||
var assetPaths = GetAssetPathForType(LoadAssetBundle.tablePath, ".txt");
|
||
for (var i = 0; i < assetPaths.Count; i++)
|
||
{
|
||
var assetPath = assetPaths[i];
|
||
EditorUtility.DisplayProgressBar("Bundle Table", assetPath, (float) i / assetPaths.Count);
|
||
AddBundleDict(bundleDic, assetPath, "tables");
|
||
}
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
private static void ClassifyScript(Dictionary<string, AssetImporterBundleSetting> bundleDic)
|
||
{
|
||
var assetPaths = GetAssetPathForType("Assets".Open("Project").Open("Script").Open("LuaScripts"), ".txt");
|
||
for (var i = 0; i < assetPaths.Count; i++)
|
||
{
|
||
var assetPath = assetPaths[i];
|
||
EditorUtility.DisplayProgressBar("Bundle Scripts", assetPath, (float) i / assetPaths.Count);
|
||
AddBundleDict(bundleDic, assetPath, "script");
|
||
}
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
private static List<string> GetAssetsInBundles()
|
||
{
|
||
AssetDatabase.RemoveUnusedAssetBundleNames();
|
||
var result = new List<string>();
|
||
var bundleNames = AssetDatabase.GetAllAssetBundleNames();
|
||
for (var i = 0; i < bundleNames.Length; i++)
|
||
{
|
||
var bundleAssets = AssetDatabase.GetAssetPathsFromAssetBundle(bundleNames[i]);
|
||
for (var j = 0; j < bundleAssets.Length; j++)
|
||
if (!result.Contains(bundleAssets[j]))
|
||
result.Add(bundleAssets[j]);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
// 剔除内置资源的关联
|
||
private static void TrimCSAssets(IDictionary<string, AssetImporterBundleSetting> bundleDict)
|
||
{
|
||
// 注:简单复制Key列表
|
||
var keys = bundleDict.Keys.ToArray();
|
||
foreach (var key in keys)
|
||
if (key.StartsWith(AssetConst.csAssetHeader))
|
||
bundleDict.Remove(key);
|
||
}
|
||
|
||
private static void TrimUnusedAssetBundleName(IDictionary<string, AssetImporterBundleSetting> bundleDict)
|
||
{
|
||
var usingAssets = GetAssetsInBundles();
|
||
var trimList = new List<string>();
|
||
for (var i = 0; i < usingAssets.Count; i++)
|
||
{
|
||
var assetPath = usingAssets[i];
|
||
if (!assetPath.StartsWith(dummyMaterialPath) && !bundleDict.ContainsKey(assetPath))
|
||
trimList.Add(assetPath);
|
||
}
|
||
|
||
for (var i = 0; i < trimList.Count; i++)
|
||
{
|
||
EditorUtility.DisplayProgressBar("Remove Asset from Bundle", trimList[i], (float) i / trimList.Count);
|
||
UpdateBundleName(trimList[i], string.Empty);
|
||
UpdateSpriteTag(trimList[i], string.Empty);
|
||
}
|
||
}
|
||
|
||
// 注:Unity支持一个Bundle中有不同类型的同名资源,因此这个方法现在仅仅报警,不会强制修改资源BundleName
|
||
private static void ConflictBundleRes()
|
||
{
|
||
var bundles = AssetDatabase.GetAllAssetBundleNames();
|
||
foreach (var bundle in bundles)
|
||
{
|
||
var assets = AssetDatabase.GetAssetPathsFromAssetBundle(bundle);
|
||
var assetRefs = new BundleAssetRef[assets.Length];
|
||
for (var i = 0; i < assetRefs.Length; i++)
|
||
assetRefs[i] = new BundleAssetRef(assets[i]);
|
||
for (var i = 0; i < assetRefs.Length; i++)
|
||
{
|
||
var source = assetRefs[i];
|
||
for (var j = i + 1; j < assetRefs.Length; j++)
|
||
{
|
||
var target = assetRefs[j];
|
||
if (source.assetName == target.assetName && source.assetType == target.assetType)
|
||
Debug.LogError(bundle + " 包含同类重名资源 " + source.assetPath + " 以及 " + target.assetPath +
|
||
" ,AssetBundle将无法编译!");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private static void UpdateBundleName(string assetPath, string bundleName)
|
||
{
|
||
string variant;
|
||
if (string.IsNullOrEmpty(bundleName))
|
||
{
|
||
bundleName = string.Empty;
|
||
variant = string.Empty;
|
||
}
|
||
else
|
||
variant = AssetConst.bundleVariant.Substring(1);
|
||
var filePath = EditorCommonUtility.AssetToFilePath(assetPath + AssetConst.metaExtension);
|
||
var text = File.ReadAllText(filePath);
|
||
text = _variantRegex.Replace(_bundleRegex.Replace(text, bundleName), variant);
|
||
File.WriteAllText(filePath, text);
|
||
}
|
||
|
||
private static void UpdateSpriteTag(string assetPath, string spriteTag)
|
||
{
|
||
var filePath = EditorCommonUtility.AssetToFilePath(assetPath + AssetConst.metaExtension);
|
||
var text = File.ReadAllText(filePath);
|
||
text = _spriteRegex.Replace(text, spriteTag);
|
||
File.WriteAllText(filePath, text);
|
||
}
|
||
|
||
private class BundleAssetRef
|
||
{
|
||
public readonly string assetName;
|
||
public readonly string assetPath;
|
||
public readonly Type assetType;
|
||
|
||
public BundleAssetRef(string assetPath)
|
||
{
|
||
this.assetPath = assetPath;
|
||
assetName = Path.GetFileNameWithoutExtension(assetPath);
|
||
assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
|
||
}
|
||
}
|
||
|
||
public class AssetImporterBundleSetting : ListItemBase<string>
|
||
{
|
||
public string bundleName;
|
||
public readonly HashSet<string> refSet = new HashSet<string>();
|
||
private readonly AssetImporter _importer;
|
||
|
||
public static AssetImporterBundleSetting Create(string assetPath)
|
||
{
|
||
var importer = AssetImporter.GetAtPath(assetPath);
|
||
var result = importer ? new AssetImporterBundleSetting(importer) : null;
|
||
if (result == null)
|
||
Debug.LogError(string.Format("Unable to get AssetImporter at path {0}!", assetPath));
|
||
return result;
|
||
}
|
||
|
||
private AssetImporterBundleSetting(AssetImporter importer)
|
||
{
|
||
id = importer.assetPath;
|
||
_importer = importer;
|
||
}
|
||
|
||
public Type SetAssetImporter()
|
||
{
|
||
var assetType = AssetDatabase.GetMainAssetTypeAtPath(id);
|
||
// 特殊处理贴图类型的资源
|
||
if (assetType == typeof(Texture2D))
|
||
SetTextureImporter();
|
||
else if (assetType == typeof(AudioClip))
|
||
SetImporter();
|
||
else if (assetType == typeof(Material))
|
||
SetImporter();
|
||
else if (assetType == typeof(TimelineAsset))
|
||
SetImporter();
|
||
// 特殊处理Shader类型的资源,统一推送到GameRes挂住
|
||
else if (assetType == typeof(Shader))
|
||
{
|
||
bundleName = LoadAssetBundle.gameResBundleName;
|
||
SetImporter();
|
||
}
|
||
else if (assetType == typeof(LightingDataAsset))
|
||
SetLightingDataImporter();
|
||
else
|
||
SetImporter();
|
||
return assetType;
|
||
}
|
||
|
||
private void SetImporter()
|
||
{
|
||
if (string.IsNullOrEmpty(bundleName) && refSet.Count > 1)
|
||
bundleName = GetDependencyBundleName(id);
|
||
var oldName = AssetDatabase.GetImplicitAssetBundleName(id);
|
||
if (oldName != bundleName)
|
||
SetImporterBundleName();
|
||
}
|
||
|
||
private void SetTextureImporter()
|
||
{
|
||
var textureImporter = _importer as TextureImporter;
|
||
if (textureImporter == null)
|
||
{
|
||
SetImporter();
|
||
Debug.LogError(id + "的资源不是贴图,但是被判定为贴图!");
|
||
}
|
||
else
|
||
{
|
||
SetTextureImporter(textureImporter);
|
||
}
|
||
}
|
||
|
||
// SpriteTag策略 - 指定Tag的情况下,使用指定的SpriteTag;不指定Tag的情况下,使用所在文件夹路径的Hash值;
|
||
private void SetTextureImporter(TextureImporter textureImporter)
|
||
{
|
||
// 跳过Lightmap类型,光照贴图由LightingDataAsset直接标记
|
||
if (textureImporter.textureType == TextureImporterType.Lightmap)
|
||
return;
|
||
var spriteTag = string.Empty;
|
||
// 特殊处理Sprite系的BundleName,使其可以和同路径下的其他Sprite形成图集
|
||
if (textureImporter.textureType == TextureImporterType.Sprite)
|
||
{
|
||
if (textureImporter.assetPath.StartsWith(EditorAssetConst.emojiSpritePath))
|
||
{
|
||
bundleName = "emoji";
|
||
spriteTag = bundleName;
|
||
}
|
||
else if (string.IsNullOrEmpty(bundleName))
|
||
{
|
||
bundleName = GetDependencyBundleName(Path.GetDirectoryName(textureImporter.assetPath));
|
||
bundleName = LoadAssetBundle.uiSpriteBundleHeader + bundleName;
|
||
spriteTag = Path.GetFileNameWithoutExtension(bundleName);
|
||
}
|
||
else
|
||
{
|
||
spriteTag = Path.GetFileNameWithoutExtension(bundleName);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (string.IsNullOrEmpty(bundleName))
|
||
if (refSet.Count > 1)
|
||
bundleName = GetDependencyBundleName(textureImporter.assetPath);
|
||
}
|
||
SetImporterBundleName();
|
||
if (textureImporter.spritePackingTag != spriteTag)
|
||
UpdateSpriteTag(textureImporter.assetPath, spriteTag);
|
||
}
|
||
|
||
// 注:特殊处理LightingDataAsset - 这个编辑器类型不能打包到AssetBundle中,因此其包含的Lightmap文件需要独立标记
|
||
// Lightmap系资源不会被标记两次 - SetTextureImporter会直接跳过Lightmap类型
|
||
private void SetLightingDataImporter()
|
||
{
|
||
if (string.IsNullOrEmpty(bundleName) && refSet.Count > 1)
|
||
bundleName = GetDependencyBundleName(id);
|
||
SetImporterBundleName();
|
||
var dependencies = AssetDatabase.GetDependencies(id);
|
||
for (var i = 0; i < dependencies.Length; i++)
|
||
{
|
||
var textureImporter = AssetImporter.GetAtPath(dependencies[i]) as TextureImporter;
|
||
if (textureImporter != null && textureImporter.textureType == TextureImporterType.Lightmap)
|
||
UpdateBundleName(dependencies[i], string.Empty);
|
||
}
|
||
}
|
||
|
||
private void SetImporterBundleName()
|
||
{
|
||
if (_importer.assetBundleName != bundleName)
|
||
UpdateBundleName(_importer.assetPath, bundleName);
|
||
}
|
||
}
|
||
|
||
#region texture import modify
|
||
|
||
private static string GetDependencyBundleName(string assetPath)
|
||
{
|
||
// 增加一段减少单路径下资源数
|
||
var hash = GetStringHash(assetPath);
|
||
return dependBundleHeader + string.Format("{0}/{1}", hash[0], hash);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 2018.08.13 Scene Bundle
|
||
|
||
private static void CollectBundleDependencies(Dictionary<string, AssetImporterBundleSetting> dictionary)
|
||
{
|
||
var activeItems = dictionary.Values.ToArray();
|
||
for (var i = 0; i < activeItems.Length; i++)
|
||
{
|
||
var activeItem = activeItems[i];
|
||
EditorUtility.DisplayProgressBar("Collect Dependency", activeItem.id, (float) i / activeItems.Length);
|
||
var extension = Path.GetExtension(activeItem.id);
|
||
// 注:场景的依赖将在ClassifyScene的流程中用特殊方式进行扫描
|
||
if (!string.IsNullOrEmpty(extension))
|
||
AddDependenciesForOneAsset(dictionary, activeItem.id);
|
||
}
|
||
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
// 注:可能资源自己依赖自己路径内的子资源,但是这个情况会在装入字典时被过滤,因此没有问题
|
||
private static void AddDependenciesForOneAsset(IDictionary<string, AssetImporterBundleSetting> bundleDict,
|
||
string assetPath)
|
||
{
|
||
var dependencies = AssetDatabase.GetDependencies(assetPath, false);
|
||
var uniquePathList = new List<string>();
|
||
for (var i = 0; i < dependencies.Length; i++)
|
||
if (!dependencies[i].Equals(assetPath))
|
||
{
|
||
var assetType = AssetDatabase.GetMainAssetTypeAtPath(dependencies[i]);
|
||
// 注:依赖包仅仅允许以下类型的资源
|
||
if (assetType == typeof(Texture2D) ||
|
||
assetType == typeof(GameObject) ||
|
||
assetType == typeof(Shader) ||
|
||
assetType == typeof(AnimatorController) ||
|
||
assetType == typeof(RuntimeAnimatorController) ||
|
||
assetType == typeof(AnimationClip) ||
|
||
assetType == typeof(Material) ||
|
||
assetType == typeof(AudioClip) ||
|
||
assetType == typeof(Font) ||
|
||
// 注:忽略LightingDataAsset 是Editor Only 资源之类的警告
|
||
// 不独立标记LightingDataAsset 会导致共享的Lightmap贴图被复制到多个Bundle中
|
||
assetType == typeof(LightingDataAsset) ||
|
||
// 实际应该会被GameObject包含
|
||
assetType == typeof(Mesh))
|
||
{
|
||
AssetImporterBundleSetting record;
|
||
if (!bundleDict.TryGetValue(dependencies[i], out record))
|
||
{
|
||
record = AssetImporterBundleSetting.Create(dependencies[i]);
|
||
bundleDict.Add(record.id, record);
|
||
uniquePathList.Add(record.id);
|
||
}
|
||
record.refSet.Add(assetPath);
|
||
}
|
||
}
|
||
|
||
for (var i = 0; i < uniquePathList.Count; i++)
|
||
AddDependenciesForOneAsset(bundleDict, uniquePathList[i]);
|
||
}
|
||
|
||
public static void SetBundleNameOnImporters(Dictionary<string, AssetImporterBundleSetting> dictionary)
|
||
{
|
||
var shaderList = new List<string>();
|
||
var assetImporterSettings = dictionary.Values.ToArray();
|
||
for (var i = 0; i < assetImporterSettings.Length; i++)
|
||
{
|
||
var importerSetting = assetImporterSettings[i];
|
||
EditorUtility.DisplayProgressBar("Reimport Assets", importerSetting.id,
|
||
(float) i / assetImporterSettings.Length);
|
||
if (importerSetting.SetAssetImporter() == typeof(Shader))
|
||
shaderList.Add(assetImporterSettings[i].id);
|
||
}
|
||
|
||
EditorUtility.DisplayProgressBar("Create Shader Collection", "Total Shaders: " + shaderList.Count, 1f);
|
||
ShaderVariantTool.CreateAllDummyMaterials(shaderList, LoadAssetBundle.BUNDLE_PATH_DUMMY);
|
||
AssetDatabase.SaveAssets();
|
||
AssetDatabase.Refresh();
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
private static void CheckInvalidPath(string assetPath, params string[] invalidPathParts)
|
||
{
|
||
var asset = AssetDatabase.LoadMainAssetAtPath(assetPath);
|
||
if (asset == null)
|
||
{
|
||
Debug.LogError(string.Format("无法加载资源于路径{0}", assetPath));
|
||
}
|
||
else
|
||
{
|
||
var items = EditorUtility.CollectDependencies(new[] {asset});
|
||
var uniquePathList = new List<string>();
|
||
for (var i = 0; i < items.Length; i++)
|
||
{
|
||
var itemPath = AssetDatabase.GetAssetPath(items[i]);
|
||
if (itemPath.IsCommonAssetPath() && !uniquePathList.Contains(itemPath))
|
||
uniquePathList.Add(itemPath);
|
||
}
|
||
|
||
for (var i = 0; i < uniquePathList.Count; i++)
|
||
{
|
||
var uniquePath = uniquePathList[i];
|
||
for (var j = 0; j < invalidPathParts.Length; j++)
|
||
if (uniquePath.Contains(invalidPathParts[j], StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
Debug.LogError(string.Format("物品{0}引用过时资源{1}", assetPath, uniquePath));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
[MenuItem("ProTool/ClassifyBundles/AllChange/Rebuild Atlas", false, 1)]
|
||
public static void ClassifyAllChange_RebuildAtlas()
|
||
{
|
||
_spriteAdditive = false;
|
||
_saveSpriteData = true;
|
||
if (EditorUtility.DisplayDialog("完全重建图集", "这次操作将会放弃之前精灵图集数据,重建所有图集!\n这会导致大量资源包修改,仅仅用于大版本更新。", "确认", "取消"))
|
||
StartClassifyAll();
|
||
}
|
||
|
||
[MenuItem("ProTool/ClassifyBundles/AllChange/Add Sprite", false, 2)]
|
||
public static void ClassifyAllChange_Additive()
|
||
{
|
||
_spriteAdditive = true;
|
||
_saveSpriteData = true;
|
||
StartClassifyAll();
|
||
}
|
||
|
||
[MenuItem("ProTool/ClassifyBundles/AllChange/Test Build", false, 3)]
|
||
public static void ClassifyAllChange_AdditiveNoSave()
|
||
{
|
||
_spriteAdditive = true;
|
||
_saveSpriteData = false;
|
||
if (EditorUtility.DisplayDialog("测试编译", "这次操作不会保存精灵图集数据!\n这会导致下次编译不继承精灵数据,仅仅用于测试版本。", "确认", "取消"))
|
||
StartClassifyAll();
|
||
}
|
||
|
||
private static void StartClassifyAll()
|
||
{
|
||
Debug.LogWarning("Classify All Start at " + DateTime.Now);
|
||
DispatchScene(ClassifyAllBundles);
|
||
}
|
||
|
||
// [MenuItem("ProTool/ClassifyBundles/Classify Only", false, 5)]
|
||
// public static void ClassifyWithoutScene()
|
||
// {
|
||
// ClassifyAllBundles(true, null);
|
||
// }
|
||
|
||
private static void ClassifyAllBundles(bool success, DisassembleAllScenesAction action)
|
||
{
|
||
if (success)
|
||
{
|
||
var bundleDict = new Dictionary<string, AssetImporterBundleSetting>();
|
||
ClassifySceneBundles(bundleDict);
|
||
ClassifyModelBundles(bundleDict);
|
||
ClassifyUiPrefabBundles(bundleDict);
|
||
ClassifyEffectBundles(bundleDict);
|
||
ClassifyUiPrefabMarketingActs(bundleDict);
|
||
ClassifyUiPrefabEmoji(bundleDict);
|
||
|
||
ClassifyOtherBundles(bundleDict);
|
||
ClassifyUiTextureBundles(bundleDict);
|
||
ClassifyUiSpriteBundles(bundleDict);
|
||
ClassifySoundsBundles(bundleDict);
|
||
|
||
ClassifyScript(bundleDict);
|
||
ClassifyGameRes(bundleDict);
|
||
ClassifyTable(bundleDict);
|
||
CollectBundleDependencies(bundleDict);
|
||
|
||
SetSpriteTag(bundleDict, OnSpriteGatherFinish);
|
||
}
|
||
}
|
||
|
||
private static void OnSpriteGatherFinish(Dictionary<string, AssetImporterBundleSetting> bundleDict)
|
||
{
|
||
var action = new PurgeSpriteAction(bundleDict);
|
||
action.onFinish += OnSpriteMarkFinish;
|
||
action.Start();
|
||
}
|
||
|
||
private static void OnSpriteMarkFinish(bool success, EditorPerFrameActionBase action)
|
||
{
|
||
var purgeAction = action as PurgeSpriteAction;
|
||
if (purgeAction != null)
|
||
{
|
||
var bundleDict = purgeAction.bundleDict;
|
||
TrimCSAssets(bundleDict);
|
||
TrimUnusedAssetBundleName(bundleDict);
|
||
SetBundleNameOnImporters(bundleDict);
|
||
AssetDatabase.RemoveUnusedAssetBundleNames();
|
||
AssetDatabase.Refresh();
|
||
ConflictBundleRes();
|
||
Debug.LogWarning("Classify All Success at " + DateTime.Now);
|
||
}
|
||
}
|
||
|
||
// private static string GetPlatformName()
|
||
// {
|
||
// #if UNITY_IOS || UNITY_IPHONE
|
||
// const string platform = "iOS";
|
||
// #else
|
||
// const string platform = "Android";
|
||
// #endif
|
||
// return platform;
|
||
// }
|
||
//
|
||
// [MenuItem("ProTool/Copy Bundles for Current", false, 2)]
|
||
// public static void CopyCurrentPlatformBundles()
|
||
// {
|
||
// var toMove = new HashSet<string>();
|
||
// var platform = GetPlatformName();
|
||
// var core = platform + LoadAssetBundle.bundleFileExtension;
|
||
// const string dummyBundle = LoadAssetBundle.BUNDLE_PATH_DUMMY + LoadAssetBundle.bundleFileExtension;
|
||
// var root = Application.dataPath.MoveUp().Open("AssetBundles/" + platform);
|
||
// toMove.Add(core);
|
||
// var manifestBundle = AssetBundle.LoadFromFile(root.Open(core));
|
||
// var manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
|
||
// var allBundles = manifest.GetAllAssetBundles();
|
||
// manifestBundle.Unload(false);
|
||
// foreach (var bundle in allBundles)
|
||
// if (!bundle.Equals(dummyBundle, StringComparison.Ordinal))
|
||
// toMove.Add(bundle);
|
||
// var moveTarget = root.MoveUp().Open("ValidBundles");
|
||
// if (Directory.Exists(moveTarget))
|
||
// Directory.Delete(moveTarget, true);
|
||
// foreach (var path in toMove)
|
||
// {
|
||
// var source = root.Open(path);
|
||
// var target = moveTarget.Open(path);
|
||
// var dir = Path.GetDirectoryName(target);
|
||
// if (!Directory.Exists(dir))
|
||
// Directory.CreateDirectory(dir);
|
||
// File.Copy(source, target, true);
|
||
// }
|
||
// }
|
||
|
||
//private static void CopyItemsWithExtension(string sourceDir, string targetDir, string extension)
|
||
//{
|
||
// var itemPaths = Directory.GetFiles(sourceDir, "*" + extension, SearchOption.AllDirectories);
|
||
// foreach (var itemPath in itemPaths)
|
||
// {
|
||
// var targetPath = targetDir.Open(itemPath.Substring(sourceDir.Length));
|
||
// if (File.Exists(targetPath))
|
||
// File.Delete(targetPath);
|
||
// CopyOneAssetBundle(itemPath, targetPath);
|
||
// }
|
||
//}
|
||
// private static void CopyOneAssetBundle(string sourcePath, string targetPath)
|
||
// {
|
||
// var targetDir = Path.GetDirectoryName(targetPath);
|
||
// if (!Directory.Exists(targetDir))
|
||
// Directory.CreateDirectory(targetDir);
|
||
// File.Copy(sourcePath, targetPath);
|
||
// // 不拷贝Manifest文件
|
||
// // const string manifestExtension = ".manifest";
|
||
// // File.Copy(sourcePath + manifestExtension, targetPath + manifestExtension);
|
||
// }
|
||
// 最后一次扫描时的有效场景记录
|
||
// 临时使用这种流程来实现场景扫描和Classify流程的分离
|
||
// 列表为空表示场景文件列表未初始化
|
||
private static List<string> _sceneList;
|
||
|
||
private static void RefreshSceneList()
|
||
{
|
||
_sceneList = new List<string>();
|
||
var assetPaths = AssetDatabase.GetAllAssetPaths();
|
||
var sceneDataList = EditorTableManager.GetTable<Tab_SceneClass>();
|
||
for (var i = 0; i < assetPaths.Length; i++)
|
||
if (assetPaths[i].StartsWith(SceneDisassembly.sceneRoot))
|
||
{
|
||
var extension = Path.GetExtension(assetPaths[i]);
|
||
if (!string.IsNullOrEmpty(extension) && extension.Equals(SceneDisassembly.sceneExtension,
|
||
StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
var sceneName = Path.GetFileNameWithoutExtension(assetPaths[i]);
|
||
if (!string.IsNullOrEmpty(sceneName) &&
|
||
!sceneName.Equals(GlobeVar.sceneLogin, StringComparison.OrdinalIgnoreCase) &&
|
||
!sceneName.Equals(GlobeVar.sceneLoading, StringComparison.OrdinalIgnoreCase) &&
|
||
!sceneName.Equals(GlobeVar.sceneRole, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
var sceneData = sceneDataList.Find(a => a.ResName == sceneName);
|
||
if (sceneData == null)
|
||
Debug.LogError(string.Format("场景{0}数据不存在!", sceneName));
|
||
else
|
||
// 移除文件名前的路径
|
||
_sceneList.Add(assetPaths[i].Substring(SceneDisassembly.sceneRoot.Length + 1));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
[MenuItem("ProTool/ClassifyBundles/DispatchScene", false, 4)]
|
||
public static void DispatchScene()
|
||
{
|
||
DispatchScene(null);
|
||
}
|
||
|
||
[MenuItem("Bundle V2/BuiltinAssets/CopyBuiltinAssets")]
|
||
public static void CopyBuiltinAssets()
|
||
{
|
||
BuiltinAssetBuilder.ExtractBuiltinAssets();
|
||
}
|
||
|
||
[MenuItem("Bundle V2/BuiltinAssets/ReplaceWithBuiltinAssets")]
|
||
public static void ReplaceBuiltInAssets()
|
||
{
|
||
BuiltinAssetBuilder.ReplaceForPath("Assets/");
|
||
}
|
||
|
||
// 分解全部场景到场景路径
|
||
// 注:已知RoleSelect没有执行替换操作
|
||
public static void DispatchScene(UnityAction<bool, DisassembleAllScenesAction> onFinish)
|
||
{
|
||
RefreshSceneList();
|
||
var action = new DisassembleAllScenesAction(_sceneList);
|
||
if (onFinish != null)
|
||
action.onFinish += onFinish;
|
||
action.Start();
|
||
}
|
||
|
||
public static List<string> GetAssetPathForType(string headPath, string extension)
|
||
{
|
||
var result = new List<string>();
|
||
var allAssetPaths = AssetDatabase.GetAllAssetPaths();
|
||
for (var i = 0; i < allAssetPaths.Length; i++)
|
||
{
|
||
var assetPath = allAssetPaths[i];
|
||
if (assetPath.StartsWith(headPath) &&
|
||
(string.IsNullOrEmpty(extension) ||
|
||
extension.Equals(Path.GetExtension(assetPath),
|
||
StringComparison.OrdinalIgnoreCase)))
|
||
result.Add(assetPath);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region single menus
|
||
|
||
// Single Menu仅仅提供增量处理,如果在运行一次全局之前直接使用Single Menu,可能产生不可预知的问题
|
||
// 这个操作可能会把部分有编号的Bundle洗为依赖包,慎用
|
||
private static void ClassifySingleMenu(UnityAction<Dictionary<string, AssetImporterBundleSetting>> action)
|
||
{
|
||
var bundleDict = new Dictionary<string, AssetImporterBundleSetting>();
|
||
action.Invoke(bundleDict);
|
||
CollectBundleDependencies(bundleDict);
|
||
SetBundleNameOnImporters(bundleDict);
|
||
ConflictBundleRes();
|
||
}
|
||
|
||
[MenuItem("ProTool/ClassifyBundles/Table")]
|
||
public static void ClassifyTable()
|
||
{
|
||
ClassifySingleMenu(ClassifyTable);
|
||
}
|
||
|
||
[MenuItem("ProTool/ClassifyBundles/Script")]
|
||
public static void ClassifyScript()
|
||
{
|
||
ClassifySingleMenu(ClassifyScript);
|
||
}
|
||
|
||
// [MenuItem("ProTool/ClassifyBundles/UI/Prefab")]
|
||
// public static void ClassifyUIPrefab()
|
||
// {
|
||
// ClassifySingleMenu(ClassifyUiPrefabBundles);
|
||
// }
|
||
//
|
||
// [MenuItem("ProTool/ClassifyBundles/GameRes")]
|
||
// public static void ClassifyGameRes()
|
||
// {
|
||
// ClassifySingleMenu(ClassifyGameRes);
|
||
// }
|
||
//
|
||
// [MenuItem("ProTool/ClassifyBundles/UI/MarketingActs")]
|
||
// public static void ClassifyUIMarketingActPrefab()
|
||
// {
|
||
// ClassifySingleMenu(ClassifyUiPrefabMarketingActs);
|
||
// }
|
||
//
|
||
// [MenuItem("ProTool/ClassifyBundles/UI/Emoji")]
|
||
// public static void ClassifyUIEmoji()
|
||
// {
|
||
// ClassifySingleMenu(ClassifyUiPrefabEmoji);
|
||
// }
|
||
//
|
||
// [MenuItem("ProTool/ClassifyBundles/UI/Sprite")]
|
||
// public static void ClassifyUISprite()
|
||
// {
|
||
// ClassifySingleMenu(ClassifyUiSpriteBundles);
|
||
// }
|
||
//
|
||
// [MenuItem("ProTool/ClassifyBundles/UI/Texture")]
|
||
// public static void ClassifyUITexture()
|
||
// {
|
||
// ClassifySingleMenu(ClassifyUiTextureBundles);
|
||
// }
|
||
//
|
||
// [MenuItem("ProTool/ClassifyBundles/Sounds")]
|
||
// public static void ClassifySound()
|
||
// {
|
||
// ClassifySingleMenu(ClassifySoundsBundles);
|
||
// }
|
||
//
|
||
// [MenuItem("ProTool/ClassifyBundles/Model")]
|
||
// public static void ClassifyModel()
|
||
// {
|
||
// ClassifySingleMenu(ClassifyModelBundles);
|
||
// }
|
||
//
|
||
// [MenuItem("ProTool/ClassifyBundles/Effect")]
|
||
// public static void ClassifyEffect()
|
||
// {
|
||
// ClassifySingleMenu(ClassifyEffectBundles);
|
||
// }
|
||
// [MenuItem("ProTool/ClassifyBundles/Other")]
|
||
// public static void ClassifyOther()
|
||
// {
|
||
// ClassifySingleMenu(ClassifyOtherBundles);
|
||
// }
|
||
|
||
#endregion
|
||
|
||
#region Improved Sprite Collector
|
||
|
||
// ****** 永久Ui元素 - 这些物体的Sprite将会强制输出到同一个Atlas上 ******
|
||
// 拥有这个Bundle名的 UiInfo中的记录,均记录为Permanent
|
||
public const string permanentUiPath = "ui/mainbase";
|
||
|
||
// 除以上元素外,额外需要加入的部分
|
||
public static readonly UIPathData[] permanentUiPaths =
|
||
{
|
||
UIInfo.PlayerHeadInfo,
|
||
UIInfo.DropItemHeadInfo,
|
||
UIInfo.FellowHeadInfo,
|
||
UIInfo.HarryHeadInfo,
|
||
UIInfo.NPCHeadInfo,
|
||
UIInfo.OtherPlayerHeadInfo,
|
||
UIInfo.DamageBoard,
|
||
UIInfo.AnchoredText,
|
||
UIInfo.ExtraFunTipRoot,
|
||
UIInfo.GuideRoot,
|
||
UIInfo.LoadingTips,
|
||
UIInfo.MarketingActsRoot,
|
||
UIInfo.MissionClickPanel,
|
||
UIInfo.RollNotice,
|
||
UIInfo.SceneItemUseTip,
|
||
UIInfo.VipIdoitTips
|
||
};
|
||
|
||
// 数据的路径
|
||
public const string spriteDataPath = "Assets/Editor/Config/AtlasData.bytes";
|
||
|
||
public static string spriteDataFilePath
|
||
{
|
||
get { return Application.dataPath.MoveUp().Open(spriteDataPath); }
|
||
}
|
||
|
||
/// <summary>
|
||
/// Atlas填充率
|
||
/// </summary>
|
||
public const float atlasFillRatio = 0.9f;
|
||
|
||
/// <summary>
|
||
/// Altas最大边长
|
||
/// </summary>
|
||
public const float atlasWidth = 2048;
|
||
|
||
/// <summary>
|
||
/// Altas最大尺寸
|
||
/// </summary>
|
||
public const float atlasMaxSize = atlasWidth * atlasWidth * atlasFillRatio;
|
||
|
||
// 暂时使用这种标记位来记录启动数据
|
||
// 是否采用增量分配Sprite Atlas
|
||
private static bool _spriteAdditive;
|
||
|
||
// 是否保存Sprite Atlas分配数据
|
||
private static bool _saveSpriteData;
|
||
|
||
private static void SetSpriteTag(Dictionary<string, AssetImporterBundleSetting> bundleDict,
|
||
UnityAction<Dictionary<string, AssetImporterBundleSetting>> afterAction)
|
||
{
|
||
var prefabs = from assetPath in bundleDict.Keys
|
||
where ".prefab".Equals(Path.GetExtension(assetPath), StringComparison.OrdinalIgnoreCase)
|
||
select assetPath;
|
||
var action = new GatherSpriteAction(prefabs.ToArray(), bundleDict, afterAction);
|
||
action.onFinish += OnGatherSpriteFinish;
|
||
action.Start();
|
||
}
|
||
|
||
private static void OnGatherSpriteFinish(bool isSuccess, EditorPerFrameActionBase action)
|
||
{
|
||
if (isSuccess)
|
||
{
|
||
var spriteAction = action as GatherSpriteAction;
|
||
if (spriteAction == null)
|
||
{
|
||
Debug.LogError("Error on gather sprite action listening!");
|
||
}
|
||
else
|
||
{
|
||
var bundleDict = spriteAction.bundleDict;
|
||
// 裁剪CommonItem类型的Sprite套组
|
||
var permanentList = spriteAction.permanentSpritePaths
|
||
.Where(a => !a.StartsWith(LoadAssetBundle.uiSpriteAssetPath)).ToArray();
|
||
foreach (var spriteId in permanentList)
|
||
{
|
||
AssetImporterBundleSetting setting;
|
||
if (bundleDict.TryGetValue(spriteId, out setting))
|
||
setting.bundleName = uiPermanentBundle;
|
||
}
|
||
|
||
// 裁剪CommonItem类型的Sprite套组
|
||
var spriteList = spriteAction.spriteReferenceList;
|
||
for (var i = spriteList.Count - 1; i >= 0; i--)
|
||
if (spriteList[i].id.StartsWith(LoadAssetBundle.uiSpriteAssetPath))
|
||
spriteList.RemoveAt(i);
|
||
Debug.LogWarning(string.Format("Sprite Distribution, Permanent {0}, Normal {1}", permanentList.Length,
|
||
spriteList.Count));
|
||
// 按照引用位置整理Sprite列表
|
||
List<SpriteReferenceGroupData> stableGroupList = null;
|
||
// 采用叠加模式打包时,预先载入原有Sprite分配流程
|
||
if (_spriteAdditive)
|
||
if (File.Exists(spriteDataFilePath))
|
||
using (var fs = File.OpenRead(spriteDataFilePath))
|
||
{
|
||
try
|
||
{
|
||
var xmlSerializer = new XmlSerializer(typeof(List<SpriteReferenceGroupData>));
|
||
stableGroupList = xmlSerializer.Deserialize(fs) as List<SpriteReferenceGroupData>;
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError(e.ToString());
|
||
stableGroupList = null;
|
||
}
|
||
}
|
||
|
||
var groupListA = new List<SpriteReferenceGroupData>();
|
||
if (stableGroupList != null)
|
||
{
|
||
var allAssetPaths = (from assetPath in AssetDatabase.GetAllAssetPaths()
|
||
where assetPath.IsCommonAssetPath()
|
||
select assetPath).ToArray();
|
||
// Sprite增量打包流程
|
||
for (var i = stableGroupList.Count - 1; i >= 0; i--)
|
||
{
|
||
stableGroupList[i].PopulateGroupData(spriteList, allAssetPaths);
|
||
// 需要重新组合的Atlas,则执行一次重组
|
||
if (stableGroupList[i].willRebuild)
|
||
{
|
||
groupListA.Add(stableGroupList[i]);
|
||
stableGroupList.RemoveAt(i);
|
||
}
|
||
}
|
||
}
|
||
|
||
foreach (var reference in spriteAction.spriteReferenceList)
|
||
{
|
||
reference.referenceList.Sort(StringComparer.OrdinalIgnoreCase);
|
||
var add = true;
|
||
for (var i = 0; i < groupListA.Count; i++)
|
||
if (groupListA[i].TryAddData(reference))
|
||
{
|
||
add = false;
|
||
break;
|
||
}
|
||
|
||
if (add)
|
||
groupListA.Add(new SpriteReferenceGroupData(reference));
|
||
}
|
||
|
||
for (var i = 0; i < groupListA.Count; i++)
|
||
groupListA[i].SortList();
|
||
//var groupListB = new List<SpriteReferenceGroupData>();
|
||
// #1 试图合并距离最小的引用组 - 仅仅合并距离差距在1的组
|
||
// 执行两次,实际合并到差距为2
|
||
MergeAtlasByDistance(groupListA, 1);
|
||
MergeAtlasByDistance(groupListA, 1);
|
||
if (stableGroupList != null)
|
||
for (var i = 0; i < stableGroupList.Count; i++)
|
||
groupListA.Add(stableGroupList[i]);
|
||
for (var i = 0; i < groupListA.Count; i++)
|
||
groupListA[i].SetSpriteTags(bundleDict);
|
||
if (_saveSpriteData)
|
||
{
|
||
var directory = Path.GetDirectoryName(spriteDataFilePath);
|
||
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
|
||
Directory.CreateDirectory(directory);
|
||
using (var fs = File.Create(spriteDataFilePath))
|
||
{
|
||
var xmlSerializer = new XmlSerializer(typeof(List<SpriteReferenceGroupData>));
|
||
xmlSerializer.Serialize(fs, groupListA);
|
||
}
|
||
}
|
||
|
||
if (spriteAction.afterAction != null)
|
||
spriteAction.afterAction.Invoke(bundleDict);
|
||
}
|
||
}
|
||
}
|
||
|
||
private static void MergeAtlasByDistance(List<SpriteReferenceGroupData> groupList, int maxDist)
|
||
{
|
||
var mergeList = new List<SpriteReferenceDistance>();
|
||
for (var i = 0; i < groupList.Count - 1; i++)
|
||
for (var j = i + 1; j < groupList.Count; j++)
|
||
{
|
||
var merge = new SpriteReferenceDistance(groupList[i], groupList[j]);
|
||
if (merge.distance <= maxDist && merge.size < atlasMaxSize)
|
||
mergeList.Add(merge);
|
||
}
|
||
|
||
mergeList.Sort();
|
||
while (mergeList.Count > 0)
|
||
if (mergeList[0].size > atlasMaxSize)
|
||
{
|
||
mergeList.RemoveAt(0);
|
||
}
|
||
else
|
||
{
|
||
var merge = mergeList[0];
|
||
mergeList.RemoveAt(0);
|
||
merge.a.MergeGroup(merge.b);
|
||
groupList.Remove(merge.b);
|
||
for (var i = mergeList.Count - 1; i >= 0; i--)
|
||
if (mergeList[i].Contains(merge.a) || mergeList[i].Contains(merge.b))
|
||
mergeList.RemoveAt(i);
|
||
}
|
||
}
|
||
|
||
private class PurgeSpriteAction : EditorPerFrameActionBase
|
||
{
|
||
private readonly string[] _assetPaths;
|
||
public readonly Dictionary<string, AssetImporterBundleSetting> bundleDict;
|
||
private int _index;
|
||
|
||
public PurgeSpriteAction(Dictionary<string, AssetImporterBundleSetting> bundleDict)
|
||
{
|
||
this.bundleDict = bundleDict;
|
||
var assetPaths = from assetPath in AssetDatabase.GetAllAssetPaths()
|
||
where assetPath.IsCommonAssetPath()
|
||
let extension = Path.GetExtension(assetPath)
|
||
where ".png".Equals(extension, StringComparison.OrdinalIgnoreCase) ||
|
||
".jpg".Equals(extension, StringComparison.OrdinalIgnoreCase) ||
|
||
".tga".Equals(extension, StringComparison.OrdinalIgnoreCase)
|
||
where !bundleDict.ContainsKey(assetPath)
|
||
select assetPath;
|
||
_assetPaths = assetPaths.ToArray();
|
||
}
|
||
|
||
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 TextureImporter;
|
||
if (importer != null && !string.IsNullOrEmpty(importer.spritePackingTag))
|
||
{
|
||
importer.spritePackingTag = string.Empty;
|
||
importer.SaveAndReimport();
|
||
}
|
||
|
||
_index++;
|
||
if (_index % 100 == 0)
|
||
Resources.UnloadUnusedAssets();
|
||
}
|
||
}
|
||
|
||
private class GatherSpriteAction : EditorPerFrameActionBase
|
||
{
|
||
// 永久存在的Ui窗口 - 将强制输出到同一张Atlas上
|
||
private readonly string[] _permanentPrefabPaths;
|
||
|
||
// 非永久存在的Ui窗口
|
||
private readonly string[] _prefabPaths;
|
||
|
||
public readonly UnityAction<Dictionary<string, AssetImporterBundleSetting>> afterAction;
|
||
public readonly Dictionary<string, AssetImporterBundleSetting> bundleDict;
|
||
public readonly HashSet<string> permanentSpritePaths = new HashSet<string>();
|
||
public readonly List<SpriteReferenceData> spriteReferenceList = new List<SpriteReferenceData>();
|
||
|
||
private int _index;
|
||
|
||
public GatherSpriteAction(string[] assetPaths, Dictionary<string, AssetImporterBundleSetting> bundleDict,
|
||
UnityAction<Dictionary<string, AssetImporterBundleSetting>> afterAction)
|
||
{
|
||
var permanentNames = new HashSet<string>();
|
||
foreach (var uiPath in permanentUiPaths)
|
||
permanentNames.Add(uiPath.name);
|
||
var fields = typeof(UIInfo).GetFields(BindingFlags.Public | BindingFlags.Static);
|
||
for (var i = 0; i < fields.Length; i++)
|
||
{
|
||
var pathData = fields[i].GetValue(null) as UIPathData;
|
||
if (pathData != null && pathData.path.ToLower() == permanentUiPath)
|
||
permanentNames.Add(pathData.name);
|
||
}
|
||
_permanentPrefabPaths = (from assetPath in assetPaths
|
||
let fileName = Path.GetFileNameWithoutExtension(assetPath)
|
||
where permanentNames.Contains(fileName)
|
||
select assetPath).ToArray();
|
||
_prefabPaths = assetPaths.Except(_permanentPrefabPaths).Where(a => !a.Contains("/UI/Prefab/Emoji/")).ToArray();
|
||
Debug.LogWarning(string.Format("Total Ui Prefab Count {0}, Permanent Prefab Count {1}, Normal Count {2}",
|
||
assetPaths.Length, _permanentPrefabPaths.Length, _prefabPaths.Length));
|
||
this.bundleDict = bundleDict;
|
||
this.afterAction = afterAction;
|
||
}
|
||
|
||
protected override int GetCurrentIndex()
|
||
{
|
||
return _index;
|
||
}
|
||
|
||
protected override int GetTotalCount()
|
||
{
|
||
return _prefabPaths.Length + _permanentPrefabPaths.Length;
|
||
}
|
||
|
||
protected override void PerFrameAction()
|
||
{
|
||
string assetPath;
|
||
bool isPermanent;
|
||
if (_index < _permanentPrefabPaths.Length)
|
||
{
|
||
assetPath = _permanentPrefabPaths[_index];
|
||
isPermanent = true;
|
||
}
|
||
else
|
||
{
|
||
assetPath = _prefabPaths[_index - _permanentPrefabPaths.Length];
|
||
isPermanent = false;
|
||
}
|
||
|
||
_index++;
|
||
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
|
||
var dependencies = EditorUtility.CollectDependencies(new Object[] {prefab});
|
||
for (var i = 0; i < dependencies.Length; i++)
|
||
{
|
||
var texture = dependencies[i] as Texture2D;
|
||
if (texture != null)
|
||
{
|
||
var texturePath = AssetDatabase.GetAssetPath(texture);
|
||
var textureImporter = AssetImporter.GetAtPath(texturePath) as TextureImporter;
|
||
if (textureImporter != null && textureImporter.textureType == TextureImporterType.Sprite)
|
||
if (isPermanent)
|
||
{
|
||
permanentSpritePaths.Add(texturePath);
|
||
}
|
||
else if (!permanentSpritePaths.Contains(texturePath))
|
||
{
|
||
var data = spriteReferenceList.GetItem(texturePath, ListGetMode.create);
|
||
data.width = texture.width;
|
||
data.height = texture.height;
|
||
if (!data.referenceList.Contains(assetPath))
|
||
data.referenceList.Add(assetPath);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (_index % 100 == 0)
|
||
Resources.UnloadUnusedAssets();
|
||
}
|
||
}
|
||
|
||
public class SpriteReferenceData : ListItemBase<string>
|
||
{
|
||
public readonly List<string> referenceList = new List<string>();
|
||
public int height;
|
||
public int width;
|
||
}
|
||
|
||
[Serializable]
|
||
public class SpriteReferenceGroupData
|
||
{
|
||
public List<string> referenceList;
|
||
[XmlIgnore] public float size;
|
||
public List<string> spriteList;
|
||
[XmlIgnore] public bool willRebuild;
|
||
[XmlIgnore] private List<string> _spriteNameList;
|
||
public uint? atlasHash;
|
||
|
||
public SpriteReferenceGroupData()
|
||
{
|
||
}
|
||
|
||
public SpriteReferenceGroupData(SpriteReferenceData firstData)
|
||
{
|
||
spriteList = new List<string>();
|
||
referenceList = firstData.referenceList;
|
||
AddData(firstData);
|
||
willRebuild = true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化引用组的数据
|
||
/// </summary>
|
||
public void PopulateGroupData(List<SpriteReferenceData> allSpriteList, string[] allAssetPaths)
|
||
{
|
||
for (var i = spriteList.Count - 1; i >= 0; i--)
|
||
{
|
||
var valid = false;
|
||
var sprite = spriteList[i];
|
||
// 仅仅需要Sprite存在,不试图移除旧Sprite
|
||
if (allAssetPaths.Contains(sprite))
|
||
{
|
||
var importer = AssetImporter.GetAtPath(sprite) as TextureImporter;
|
||
if (importer != null && importer.textureType == TextureImporterType.Sprite)
|
||
{
|
||
var args = new object[2];
|
||
var mi = typeof(TextureImporter).GetMethod("GetWidthAndHeight",
|
||
BindingFlags.NonPublic | BindingFlags.Instance);
|
||
if (mi != null)
|
||
mi.Invoke(importer, args);
|
||
// 粗略计算Atlas尺寸
|
||
if (args[0] == null || args[1] == null)
|
||
{
|
||
Debug.LogError("无法获得Sprite尺寸,于" + importer.assetPath);
|
||
}
|
||
else
|
||
{
|
||
size += (int) args[0] * (int) args[1];
|
||
valid = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (valid)
|
||
{
|
||
var index = allSpriteList.GetIndex(sprite);
|
||
// 如果列表中的Sprite不再存在,则Atlas需要重建
|
||
if (index >= 0)
|
||
allSpriteList.RemoveAt(index);
|
||
}
|
||
else
|
||
{
|
||
// 如果Sprite不正常,allSpriteList必然也没有记录
|
||
willRebuild = true;
|
||
spriteList.RemoveAt(i);
|
||
}
|
||
}
|
||
|
||
if (willRebuild)
|
||
Debug.LogWarning("Will Rebuild Atlas " + (atlasHash == null ? "Null" : atlasHash.Value.ToString()));
|
||
}
|
||
|
||
public void MergeGroup(SpriteReferenceGroupData groupData)
|
||
{
|
||
for (var i = 0; i < groupData.referenceList.Count; i++)
|
||
{
|
||
var reference = groupData.referenceList[i];
|
||
if (!referenceList.Contains(reference))
|
||
referenceList.Add(reference);
|
||
}
|
||
|
||
for (var i = 0; i < groupData.spriteList.Count; i++)
|
||
{
|
||
var sprite = groupData.spriteList[i];
|
||
spriteList.Add(sprite);
|
||
}
|
||
|
||
size += groupData.size;
|
||
}
|
||
|
||
public bool TryAddData(SpriteReferenceData data)
|
||
{
|
||
var result = MatchList(data.referenceList);
|
||
if (result)
|
||
AddData(data);
|
||
return result;
|
||
}
|
||
|
||
private void AddData(SpriteReferenceData data)
|
||
{
|
||
spriteList.Add(data.id);
|
||
size += data.width * data.height;
|
||
}
|
||
|
||
private bool MatchList(IList<string> list)
|
||
{
|
||
var result = true;
|
||
if (list.Count != referenceList.Count)
|
||
result = false;
|
||
else
|
||
for (var i = 0; i < list.Count; i++)
|
||
if (list[i] != referenceList[i])
|
||
{
|
||
result = false;
|
||
break;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
public bool Contain(SpriteReferenceGroupData other)
|
||
{
|
||
var index = 0;
|
||
var contain = false;
|
||
for (var i = 0; i < other.referenceList.Count; i++)
|
||
{
|
||
contain = false;
|
||
while (index < referenceList.Count)
|
||
if (referenceList[index] == other.referenceList[i])
|
||
{
|
||
index++;
|
||
contain = true;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
index++;
|
||
}
|
||
|
||
if (!contain)
|
||
break;
|
||
}
|
||
|
||
return contain;
|
||
}
|
||
|
||
public bool CheckSpriteName(SpriteReferenceGroupData other)
|
||
{
|
||
var index = 0;
|
||
var otherIndex = 0;
|
||
var result = true;
|
||
while (index < _spriteNameList.Count && otherIndex < other._spriteNameList.Count)
|
||
{
|
||
var compare = string.CompareOrdinal(_spriteNameList[index], other._spriteNameList[otherIndex]);
|
||
if (compare < 0)
|
||
{
|
||
index++;
|
||
}
|
||
else if (compare > 0)
|
||
{
|
||
otherIndex++;
|
||
}
|
||
else
|
||
{
|
||
result = false;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
public void SortList()
|
||
{
|
||
spriteList.Sort(StringComparer.Ordinal);
|
||
referenceList.Sort(StringComparer.Ordinal);
|
||
_spriteNameList = new List<string>();
|
||
for (var i = 0; i < spriteList.Count; i++)
|
||
_spriteNameList.Add(Path.GetFileNameWithoutExtension(spriteList[i]));
|
||
_spriteNameList.Sort(StringComparer.Ordinal);
|
||
}
|
||
|
||
public bool Clamp(SpriteReferenceGroupData other)
|
||
{
|
||
var intersect = referenceList.Intersect(other.referenceList);
|
||
return intersect.Any();
|
||
}
|
||
|
||
public void SetSpriteTags(Dictionary<string, AssetImporterBundleSetting> bundleDict)
|
||
{
|
||
if (atlasHash == null)
|
||
{
|
||
var hash = 0;
|
||
foreach (var item in referenceList)
|
||
unchecked
|
||
{
|
||
hash += Animator.StringToHash(item);
|
||
}
|
||
|
||
atlasHash = unchecked((uint) hash);
|
||
}
|
||
|
||
var bundleName = atlasHash.Value.ToString();
|
||
bundleName = LoadAssetBundle.uiSpriteBundleHeader + bundleName;
|
||
// 试图重命名名称重复的Sprite
|
||
var list = new List<MyTuple<string, string>>();
|
||
for (var i = 0; i < spriteList.Count; i++)
|
||
list.Add(new MyTuple<string, string>(Path.GetFileNameWithoutExtension(spriteList[i]), spriteList[i]));
|
||
for (var i = 0; i < list.Count; i++)
|
||
{
|
||
EditorUtility.DisplayProgressBar("Valid sprite for atlas " + bundleName,
|
||
string.Format("{0} / {1}", i, list.Count), (float) i / list.Count);
|
||
// 注:默认文件系统是不可能有重复项的
|
||
if (DuplicateInList(list, list[i].first) > 1)
|
||
{
|
||
var assetPath = list[i].second;
|
||
var folder = assetPath.MoveUp();
|
||
var extension = Path.GetExtension(assetPath);
|
||
var fileName = Path.GetFileNameWithoutExtension(assetPath);
|
||
System.Diagnostics.Debug.Assert(fileName != null, "fileName != null");
|
||
var index = fileName.LastIndexOf('(');
|
||
if (index >= 0)
|
||
fileName = fileName.Remove(index);
|
||
var count = 1;
|
||
string newFileName;
|
||
string newPath;
|
||
do
|
||
{
|
||
newFileName = string.Format("{0}({1})", fileName, count);
|
||
newPath = folder.Open(newFileName) + extension;
|
||
if (DuplicateInList(list, newFileName) < 1)
|
||
{
|
||
var assets = from path in AssetDatabase.GetAllAssetPaths()
|
||
where path.StartsWith(folder)
|
||
select path;
|
||
if (!assets.Contains(newPath))
|
||
break;
|
||
}
|
||
|
||
count++;
|
||
} while (true);
|
||
|
||
list[i].first = newFileName;
|
||
list[i].second = newPath;
|
||
RenameAsset(bundleDict, assetPath, newPath);
|
||
}
|
||
}
|
||
|
||
EditorUtility.ClearProgressBar();
|
||
for (var i = 0; i < list.Count; i++)
|
||
{
|
||
AssetImporterBundleSetting bundleSetting;
|
||
if (bundleDict.TryGetValue(list[i].second, out bundleSetting))
|
||
bundleSetting.bundleName = bundleName;
|
||
}
|
||
}
|
||
|
||
private int DuplicateInList(List<MyTuple<string, string>> list, string fileName)
|
||
{
|
||
var count = 0;
|
||
for (var i = 0; i < list.Count; i++)
|
||
if (list[i].first == fileName)
|
||
count++;
|
||
return count;
|
||
}
|
||
|
||
private void RenameAsset(Dictionary<string, AssetImporterBundleSetting> bundleDict, string oldPath,
|
||
string newPath)
|
||
{
|
||
AssetDatabase.MoveAsset(oldPath, newPath);
|
||
AssetImporterBundleSetting bundleSetting;
|
||
if (bundleDict.TryGetValue(oldPath, out bundleSetting))
|
||
{
|
||
bundleDict.Remove(oldPath);
|
||
bundleSetting.id = newPath;
|
||
bundleDict.Add(newPath, bundleSetting);
|
||
}
|
||
|
||
Debug.LogWarning(string.Format("Rename sprite {0} to {1}", oldPath, newPath));
|
||
}
|
||
}
|
||
|
||
public class SpriteReferenceDistance : IComparable<SpriteReferenceDistance>
|
||
{
|
||
public readonly SpriteReferenceGroupData a;
|
||
public readonly SpriteReferenceGroupData b;
|
||
public readonly int count;
|
||
public readonly int distance;
|
||
public readonly float size;
|
||
|
||
public SpriteReferenceDistance(SpriteReferenceGroupData a, SpriteReferenceGroupData b)
|
||
{
|
||
this.a = a;
|
||
this.b = b;
|
||
var temp = a.referenceList.Union(b.referenceList);
|
||
count = temp.Count();
|
||
// 注:距离按照最大可能附带的额外Sprite计算,因此使用Max而不是加法
|
||
distance = Mathf.Max(count - a.referenceList.Count, count - b.referenceList.Count);
|
||
size = a.size + b.size;
|
||
}
|
||
|
||
public int CompareTo(SpriteReferenceDistance other)
|
||
{
|
||
var result = distance.CompareTo(other.distance);
|
||
// 尽量合并所需合并次数最低的Sprite
|
||
if (result == 0)
|
||
result = -size.CompareTo(other.size);
|
||
if (result == 0)
|
||
result = -count.CompareTo(other.count);
|
||
return result;
|
||
}
|
||
|
||
public bool Contains(SpriteReferenceGroupData data)
|
||
{
|
||
return a == data || b == data;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
} |