diff --git a/Assets/YooAsset.meta b/Assets/YooAsset.meta
new file mode 100644
index 0000000..0c86392
--- /dev/null
+++ b/Assets/YooAsset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3ac1aba04265f9143957a5b474de973d
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor.meta b/Assets/YooAsset/Editor.meta
new file mode 100644
index 0000000..22c2889
--- /dev/null
+++ b/Assets/YooAsset/Editor.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: ac093515ae764b94aa07be91d4ba978b
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBrowser.meta b/Assets/YooAsset/Editor/AssetBundleBrowser.meta
new file mode 100644
index 0000000..19ed378
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBrowser.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 197364393c72852419af32ef010600bf
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBrowser/AssetBundleBrowserWindow.cs b/Assets/YooAsset/Editor/AssetBundleBrowser/AssetBundleBrowserWindow.cs
new file mode 100644
index 0000000..7c1fed9
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBrowser/AssetBundleBrowserWindow.cs
@@ -0,0 +1,8 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public class AssetBundleBrowserWindow
+{
+
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBrowser/AssetBundleBrowserWindow.cs.meta b/Assets/YooAsset/Editor/AssetBundleBrowser/AssetBundleBrowserWindow.cs.meta
new file mode 100644
index 0000000..8d8023c
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBrowser/AssetBundleBrowserWindow.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d6938e4c868d3f34f97d3bb0dbd9d509
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder.meta b/Assets/YooAsset/Editor/AssetBundleBuilder.meta
new file mode 100644
index 0000000..c1b5f4b
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 1fdecc5500229d44887425ce619352fc
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilder.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilder.cs
new file mode 100644
index 0000000..611d48b
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilder.cs
@@ -0,0 +1,197 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+using YooAsset.Utility;
+
+namespace YooAsset.Editor
+{
+ public class AssetBundleBuilder
+ {
+ ///
+ /// 构建参数
+ ///
+ public class BuildParameters
+ {
+ ///
+ /// 是否验证构建结果
+ ///
+ public bool IsVerifyBuildingResult = false;
+
+ ///
+ /// 输出的根目录
+ ///
+ public string OutputRoot;
+
+ ///
+ /// 构建的平台
+ ///
+ public BuildTarget BuildTarget;
+
+ ///
+ /// 构建的版本(资源版本号)
+ ///
+ public int BuildVersion;
+
+ ///
+ /// 是否允许冗余机制
+ /// 说明:冗余机制可以帮助我们减少包体数量
+ ///
+ public bool ApplyRedundancy = false;
+
+ ///
+ /// 是否附加上文件扩展名
+ ///
+ public bool AppendFileExtension = false;
+
+
+ ///
+ /// 压缩选项
+ ///
+ public ECompressOption CompressOption;
+
+ ///
+ /// 是否强制重新构建整个项目,如果为FALSE则是增量打包
+ ///
+ public bool IsForceRebuild;
+
+ ///
+ /// 内置资源的标记列表
+ /// 注意:分号为分隔符
+ ///
+ public string BuildinTags;
+
+ #region 高级选项
+ ///
+ /// 文件名附加上哈希值
+ ///
+ public bool IsAppendHash = false;
+
+ ///
+ /// 禁止写入类型树结构(可以降低包体和内存并提高加载效率)
+ ///
+ public bool IsDisableWriteTypeTree = false;
+
+ ///
+ /// 忽略类型树变化
+ ///
+ public bool IsIgnoreTypeTreeChanges = true;
+
+ ///
+ /// 禁用名称查找资源(可以降内存并提高加载效率)
+ ///
+ public bool IsDisableLoadAssetByFileName = false;
+ #endregion
+
+
+ ///
+ /// 获取内置标记列表
+ ///
+ public List GetBuildinTags()
+ {
+ return StringUtility.StringToStringList(BuildinTags, ';');
+ }
+ }
+
+ ///
+ /// 构建参数环境
+ ///
+ public class BuildParametersContext : IContextObject
+ {
+ ///
+ /// 构建参数
+ ///
+ public BuildParameters Parameters { private set; get; }
+
+ ///
+ /// 构建管线的输出目录
+ ///
+ public string PipelineOutputDirectory { private set; get; }
+
+
+ public BuildParametersContext(BuildParameters parameters)
+ {
+ Parameters = parameters;
+ PipelineOutputDirectory = AssetBundleBuilderHelper.MakePipelineOutputDirectory(parameters.OutputRoot, parameters.BuildTarget);
+ }
+
+ ///
+ /// 获取本次构建的补丁目录
+ ///
+ public string GetPackageDirectory()
+ {
+ return $"{Parameters.OutputRoot}/{Parameters.BuildTarget}/{Parameters.BuildVersion}";
+ }
+
+ ///
+ /// 获取构建选项
+ ///
+ public BuildAssetBundleOptions GetPipelineBuildOptions()
+ {
+ // For the new build system, unity always need BuildAssetBundleOptions.CollectDependencies and BuildAssetBundleOptions.DeterministicAssetBundle
+ // 除非设置ForceRebuildAssetBundle标记,否则会进行增量打包
+
+ BuildAssetBundleOptions opt = BuildAssetBundleOptions.None;
+ opt |= BuildAssetBundleOptions.StrictMode; //Do not allow the build to succeed if any errors are reporting during it.
+
+ if (Parameters.CompressOption == ECompressOption.Uncompressed)
+ opt |= BuildAssetBundleOptions.UncompressedAssetBundle;
+ else if (Parameters.CompressOption == ECompressOption.LZ4)
+ opt |= BuildAssetBundleOptions.ChunkBasedCompression;
+
+ if (Parameters.IsForceRebuild)
+ opt |= BuildAssetBundleOptions.ForceRebuildAssetBundle; //Force rebuild the asset bundles
+ if (Parameters.IsAppendHash)
+ opt |= BuildAssetBundleOptions.AppendHashToAssetBundleName; //Append the hash to the assetBundle name
+ if (Parameters.IsDisableWriteTypeTree)
+ opt |= BuildAssetBundleOptions.DisableWriteTypeTree; //Do not include type information within the asset bundle (don't write type tree).
+ if (Parameters.IsIgnoreTypeTreeChanges)
+ opt |= BuildAssetBundleOptions.IgnoreTypeTreeChanges; //Ignore the type tree changes when doing the incremental build check.
+ if (Parameters.IsDisableLoadAssetByFileName)
+ {
+ opt |= BuildAssetBundleOptions.DisableLoadAssetByFileName; //Disables Asset Bundle LoadAsset by file name.
+ opt |= BuildAssetBundleOptions.DisableLoadAssetByFileNameWithExtension; //Disables Asset Bundle LoadAsset by file name with extension.
+ }
+
+ return opt;
+ }
+ }
+
+
+ private readonly BuildContext _buildContext = new BuildContext();
+
+ ///
+ /// 开始构建
+ ///
+ public bool Run(BuildParameters buildParameters)
+ {
+ // 清空旧数据
+ _buildContext.ClearAllContext();
+
+ // 构建参数
+ var buildParametersContext = new BuildParametersContext(buildParameters);
+ _buildContext.SetContextObject(buildParametersContext);
+
+ // 执行构建流程
+ List pipeline = new List
+ {
+ new TaskPrepare(), //前期准备工作
+ new TaskGetBuildMap(), //获取构建列表
+ new TaskBuilding(), //开始执行构建
+ new TaskEncryption(), //加密资源文件
+ new TaskCreatePatchManifest(), //创建清单文件
+ new TaskCreateReadme(), //创建说明文件
+ new TaskCreatePatchPackage(), //制作补丁包
+ new TaskCopyBuildinFiles(), //拷贝内置文件
+ };
+
+ bool succeed = BuildRunner.Run(pipeline, _buildContext);
+ if (succeed)
+ Debug.Log($"构建成功!");
+ else
+ Debug.LogWarning($"构建失败!");
+ return succeed;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilder.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilder.cs.meta
new file mode 100644
index 0000000..8060114
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilder.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: de7563040250b4e4a835d1fc90238e38
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderHelper.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderHelper.cs
new file mode 100644
index 0000000..98900c4
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderHelper.cs
@@ -0,0 +1,140 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using UnityEngine;
+using UnityEditor;
+using YooAsset.Utility;
+
+namespace YooAsset.Editor
+{
+ public static class AssetBundleBuilderHelper
+ {
+ ///
+ /// 获取默认的输出根路录
+ ///
+ public static string GetDefaultOutputRoot()
+ {
+ string projectPath = EditorTools.GetProjectPath();
+ return $"{projectPath}/Bundles";
+ }
+
+ ///
+ /// 获取构建管线的输出目录
+ ///
+ public static string MakePipelineOutputDirectory(string outputRoot, BuildTarget buildTarget)
+ {
+ return $"{outputRoot}/{buildTarget}/{ResourceSettingData.Setting.UnityManifestFileName}";
+ }
+
+ ///
+ /// 制作AssetBundle的完整名称
+ /// 注意:名称为全部小写并且包含后缀名
+ ///
+ public static string MakeBundleName(string bundleLabel, string bundleVariant)
+ {
+ if (string.IsNullOrEmpty(bundleVariant))
+ return bundleLabel.ToLower();
+ else
+ return $"{bundleLabel}.{bundleVariant}".ToLower();
+ }
+
+
+ ///
+ /// 清空流文件夹
+ ///
+ public static void ClearStreamingAssetsFolder()
+ {
+ string streamingPath = Application.dataPath + "/StreamingAssets";
+ EditorTools.ClearFolder(streamingPath);
+ }
+
+ ///
+ /// 删除流文件夹内无关的文件
+ /// 删除.manifest文件和.meta文件
+ ///
+ public static void DeleteStreamingAssetsIgnoreFiles()
+ {
+ string streamingPath = Application.dataPath + "/StreamingAssets";
+ if (Directory.Exists(streamingPath))
+ {
+ string[] files = Directory.GetFiles(streamingPath, "*.manifest", SearchOption.AllDirectories);
+ foreach (var file in files)
+ {
+ FileInfo info = new FileInfo(file);
+ info.Delete();
+ }
+
+ files = Directory.GetFiles(streamingPath, "*.meta", SearchOption.AllDirectories);
+ foreach (var item in files)
+ {
+ FileInfo info = new FileInfo(item);
+ info.Delete();
+ }
+ }
+ }
+
+
+ ///
+ /// 获取所有补丁包版本列表
+ /// 注意:列表会按照版本号从小到大排序
+ ///
+ private static List GetPackageVersionList(BuildTarget buildTarget, string outputRoot)
+ {
+ List versionList = new List();
+
+ string parentPath = $"{outputRoot}/{buildTarget}";
+ if (Directory.Exists(parentPath) == false)
+ return versionList;
+
+ // 获取所有补丁包文件夹
+ string[] allFolders = Directory.GetDirectories(parentPath);
+ for (int i = 0; i < allFolders.Length; i++)
+ {
+ string folderName = Path.GetFileNameWithoutExtension(allFolders[i]);
+ if (int.TryParse(folderName, out int version))
+ versionList.Add(version);
+ }
+
+ // 从小到大排序
+ versionList.Sort();
+ return versionList;
+ }
+
+ ///
+ /// 获取当前最大的补丁包版本号
+ ///
+ /// 如果没有任何补丁版本,那么返回-1
+ public static int GetMaxPackageVersion(BuildTarget buildTarget, string outputRoot)
+ {
+ List versionList = GetPackageVersionList(buildTarget, outputRoot);
+ if (versionList.Count == 0)
+ return -1;
+ return versionList[versionList.Count - 1];
+ }
+
+ ///
+ /// 是否存在任何补丁包版本
+ ///
+ public static bool HasAnyPackageVersion(BuildTarget buildTarget, string outputRoot)
+ {
+ List versionList = GetPackageVersionList(buildTarget, outputRoot);
+ return versionList.Count > 0;
+ }
+
+
+ ///
+ /// 从输出目录加载补丁清单文件
+ ///
+ public static PatchManifest LoadPatchManifestFile(string fileDirectory)
+ {
+ string filePath = $"{fileDirectory}/{ResourceSettingData.Setting.PatchManifestFileName}";
+ if (File.Exists(filePath) == false)
+ {
+ throw new System.Exception($"Not found patch manifest file : {filePath}");
+ }
+
+ string jsonData = FileUtility.ReadFile(filePath);
+ return PatchManifest.Deserialize(jsonData);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderHelper.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderHelper.cs.meta
new file mode 100644
index 0000000..2cbe160
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderHelper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f48abdec05f0dbe438a83e181fe6bc93
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderTools.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderTools.cs
new file mode 100644
index 0000000..d37faa4
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderTools.cs
@@ -0,0 +1,101 @@
+using System;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+using UnityEditor.Animations;
+
+namespace YooAsset.Editor
+{
+ public static class AssetBundleBuilderTools
+ {
+ ///
+ /// 检测所有损坏的预制体文件
+ ///
+ public static void CheckCorruptionPrefab(List searchDirectorys)
+ {
+ if (searchDirectorys.Count == 0)
+ throw new Exception("路径列表不能为空!");
+
+ // 获取所有资源列表
+ int checkCount = 0;
+ int invalidCount = 0;
+ string[] findAssets = EditorTools.FindAssets(EAssetSearchType.Prefab, searchDirectorys.ToArray());
+ foreach (string assetPath in findAssets)
+ {
+ UnityEngine.Object prefab = AssetDatabase.LoadAssetAtPath(assetPath, typeof(UnityEngine.Object));
+ if (prefab == null)
+ {
+ invalidCount++;
+ Debug.LogError($"发现损坏预制件:{assetPath}");
+ }
+ EditorTools.DisplayProgressBar("检测预制件文件是否损坏", ++checkCount, findAssets.Length);
+ }
+ EditorTools.ClearProgressBar();
+
+ if (invalidCount == 0)
+ Debug.Log($"没有发现损坏预制件");
+ }
+
+ ///
+ /// 检测所有动画控制器的冗余状态
+ ///
+ public static void FindRedundantAnimationState(List searchDirectorys)
+ {
+ if (searchDirectorys.Count == 0)
+ throw new Exception("路径列表不能为空!");
+
+ // 获取所有资源列表
+ int checkCount = 0;
+ int findCount = 0;
+ string[] findAssets = EditorTools.FindAssets(EAssetSearchType.RuntimeAnimatorController, searchDirectorys.ToArray());
+ foreach (string assetPath in findAssets)
+ {
+ AnimatorController animator= AssetDatabase.LoadAssetAtPath(assetPath);
+ if (EditorTools.FindRedundantAnimationState(animator))
+ {
+ findCount++;
+ Debug.LogWarning($"发现冗余的动画控制器:{assetPath}");
+ }
+ EditorTools.DisplayProgressBar("检测冗余的动画控制器", ++checkCount, findAssets.Length);
+ }
+ EditorTools.ClearProgressBar();
+
+ if (findCount == 0)
+ Debug.Log($"没有发现冗余的动画控制器");
+ else
+ AssetDatabase.SaveAssets();
+ }
+
+ ///
+ /// 清理所有材质球的冗余属性
+ ///
+ public static void ClearMaterialUnusedProperty(List searchDirectorys)
+ {
+ if (searchDirectorys.Count == 0)
+ throw new Exception("路径列表不能为空!");
+
+ // 获取所有资源列表
+ int checkCount = 0;
+ int removedCount = 0;
+ string[] findAssets = EditorTools.FindAssets(EAssetSearchType.Material, searchDirectorys.ToArray());
+ foreach (string assetPath in findAssets)
+ {
+ Material mat = AssetDatabase.LoadAssetAtPath(assetPath);
+ if (EditorTools.ClearMaterialUnusedProperty(mat))
+ {
+ removedCount++;
+ Debug.LogWarning($"材质球已被处理:{assetPath}");
+ }
+ EditorTools.DisplayProgressBar("清理冗余的材质球", ++checkCount, findAssets.Length);
+ }
+ EditorTools.ClearProgressBar();
+
+ if (removedCount == 0)
+ Debug.Log($"没有发现冗余的材质球");
+ else
+ AssetDatabase.SaveAssets();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderTools.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderTools.cs.meta
new file mode 100644
index 0000000..769e38e
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderTools.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: fe50795c51a46884088139b840c1557f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderWindow.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderWindow.cs
new file mode 100644
index 0000000..9731285
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderWindow.cs
@@ -0,0 +1,158 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+
+namespace YooAsset.Editor
+{
+ public class AssetBundleBuilderWindow : EditorWindow
+ {
+ static AssetBundleBuilderWindow _thisInstance;
+
+ [MenuItem("YooAsset/AssetBundle Builder", false, 102)]
+ static void ShowWindow()
+ {
+ if (_thisInstance == null)
+ {
+ _thisInstance = EditorWindow.GetWindow(typeof(AssetBundleBuilderWindow), false, "资源包构建工具", true) as AssetBundleBuilderWindow;
+ _thisInstance.minSize = new Vector2(800, 600);
+ }
+ _thisInstance.Show();
+ }
+
+ // 构建器
+ private readonly AssetBundleBuilder _assetBuilder = new AssetBundleBuilder();
+
+ // 构建参数
+ private int _buildVersion;
+ private BuildTarget _buildTarget;
+ private ECompressOption _compressOption = ECompressOption.Uncompressed;
+ private bool _isAppendExtension = false;
+ private bool _isForceRebuild = false;
+ private string _buildinTags = string.Empty;
+
+ // GUI相关
+ private bool _isInit = false;
+ private GUIStyle _centerStyle;
+ private GUIStyle _leftStyle;
+
+
+ private void OnGUI()
+ {
+ InitInternal();
+
+ // 标题
+ EditorGUILayout.LabelField("Build setup", _centerStyle);
+ EditorGUILayout.Space();
+
+ // 输出路径
+ string defaultOutputRoot = AssetBundleBuilderHelper.GetDefaultOutputRoot();
+ string pipelineOutputDirectory = AssetBundleBuilderHelper.MakePipelineOutputDirectory(defaultOutputRoot, _buildTarget);
+ EditorGUILayout.LabelField("Build Output", pipelineOutputDirectory);
+
+ // 构建参数
+ _buildVersion = EditorGUILayout.IntField("Build Version", _buildVersion, GUILayout.MaxWidth(250));
+ _compressOption = (ECompressOption)EditorGUILayout.EnumPopup("Compression", _compressOption, GUILayout.MaxWidth(250));
+ _isAppendExtension = GUILayout.Toggle(_isAppendExtension, "Append Extension", GUILayout.MaxWidth(120));
+ _isForceRebuild = GUILayout.Toggle(_isForceRebuild, "Force Rebuild", GUILayout.MaxWidth(120));
+ if (_isForceRebuild)
+ _buildinTags = EditorGUILayout.TextField("Buildin Tags", _buildinTags);
+
+ // 构建按钮
+ EditorGUILayout.Space();
+ if (GUILayout.Button("Build", GUILayout.MaxHeight(40)))
+ {
+ string title;
+ string content;
+ if (_isForceRebuild)
+ {
+ title = "警告";
+ content = "确定开始强制构建吗,这样会删除所有已有构建的文件";
+ }
+ else
+ {
+ title = "提示";
+ content = "确定开始增量构建吗";
+ }
+ if (EditorUtility.DisplayDialog(title, content, "Yes", "No"))
+ {
+ SaveSettingsToPlayerPrefs();
+ EditorTools.ClearUnityConsole();
+ EditorApplication.delayCall += ExecuteBuild;
+ }
+ else
+ {
+ Debug.LogWarning("[Build] 打包已经取消");
+ }
+ }
+ }
+ private void InitInternal()
+ {
+ if (_isInit)
+ return;
+ _isInit = true;
+
+ // GUI相关
+ _centerStyle = new GUIStyle(GUI.skin.GetStyle("Label"));
+ _centerStyle.alignment = TextAnchor.UpperCenter;
+ _leftStyle = new GUIStyle(GUI.skin.GetStyle("Label"));
+ _leftStyle.alignment = TextAnchor.MiddleLeft;
+
+ // 构建参数
+ var appVersion = new Version(Application.version);
+ _buildVersion = appVersion.Revision;
+ _buildTarget = EditorUserBuildSettings.activeBuildTarget;
+
+ // 读取配置
+ LoadSettingsFromPlayerPrefs();
+ }
+
+ ///
+ /// 执行构建
+ ///
+ private void ExecuteBuild()
+ {
+ string defaultOutputRoot = AssetBundleBuilderHelper.GetDefaultOutputRoot();
+ AssetBundleBuilder.BuildParameters buildParameters = new AssetBundleBuilder.BuildParameters();
+ buildParameters.IsVerifyBuildingResult = true;
+ buildParameters.OutputRoot = defaultOutputRoot;
+ buildParameters.BuildTarget = _buildTarget;
+ buildParameters.BuildVersion = _buildVersion;
+ buildParameters.CompressOption = _compressOption;
+ buildParameters.AppendFileExtension = _isAppendExtension;
+ buildParameters.IsForceRebuild = _isForceRebuild;
+ buildParameters.BuildinTags = _buildinTags;
+ _assetBuilder.Run(buildParameters);
+ }
+
+ #region 配置相关
+ private const string StrEditorCompressOption = "StrEditorCompressOption";
+ private const string StrEditorIsAppendExtension = "StrEditorIsAppendExtension";
+ private const string StrEditorIsForceRebuild = "StrEditorIsForceRebuild";
+ private const string StrEditorBuildinTags = "StrEditorBuildinTags";
+
+ ///
+ /// 存储配置
+ ///
+ private void SaveSettingsToPlayerPrefs()
+ {
+ EditorTools.PlayerSetEnum(StrEditorCompressOption, _compressOption);
+ EditorPrefs.SetBool(StrEditorIsAppendExtension, _isAppendExtension);
+ EditorPrefs.SetBool(StrEditorIsForceRebuild, _isForceRebuild);
+ EditorPrefs.SetString(StrEditorBuildinTags, _buildinTags);
+ }
+
+ ///
+ /// 读取配置
+ ///
+ private void LoadSettingsFromPlayerPrefs()
+ {
+ _compressOption = EditorTools.PlayerGetEnum(StrEditorCompressOption, ECompressOption.Uncompressed);
+ _isAppendExtension = EditorPrefs.GetBool(StrEditorIsAppendExtension, false);
+ _isForceRebuild = EditorPrefs.GetBool(StrEditorIsForceRebuild, false);
+ _buildinTags = EditorPrefs.GetString(StrEditorBuildinTags, string.Empty);
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderWindow.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderWindow.cs.meta
new file mode 100644
index 0000000..2e33fe9
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/AssetBundleBuilderWindow.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 66e43420e95cd0b4bae8803a31e9817b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildAssetInfo.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildAssetInfo.cs
new file mode 100644
index 0000000..fa15038
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildAssetInfo.cs
@@ -0,0 +1,117 @@
+using System.Collections;
+using System.Collections.Generic;
+
+namespace YooAsset.Editor
+{
+ ///
+ /// 构建的资源信息类
+ ///
+ public class BuildAssetInfo
+ {
+ ///
+ /// 资源路径
+ ///
+ public string AssetPath { private set; get; }
+
+ ///
+ /// 资源包标签
+ ///
+ public string BundleLabel { private set; get; }
+
+ ///
+ /// 资源包文件格式
+ ///
+ public string BundleVariant { private set; get; }
+
+ ///
+ /// 是否为原生资源
+ ///
+ public bool IsRawAsset = false;
+
+ ///
+ /// 是否为主动收集资源
+ ///
+ public bool IsCollectAsset = false;
+
+ ///
+ /// 资源标记列表
+ ///
+ public List AssetTags = new List();
+
+ ///
+ /// 被依赖次数
+ ///
+ public int DependCount = 0;
+
+ ///
+ /// 依赖的所有资源信息
+ /// 注意:包括零依赖资源(零依赖资源的资源包名无效)
+ ///
+ public List AllDependAssetInfos { private set; get; } = null;
+
+
+ public BuildAssetInfo(string assetPath)
+ {
+ AssetPath = assetPath;
+ }
+
+ ///
+ /// 设置所有依赖的资源
+ ///
+ public void SetAllDependAssetInfos(List dependAssetInfos)
+ {
+ if (AllDependAssetInfos != null)
+ throw new System.Exception("Should never get here !");
+
+ AllDependAssetInfos = dependAssetInfos;
+ }
+
+ ///
+ /// 设置资源包的标签和文件格式
+ ///
+ public void SetBundleLabelAndVariant(string bundleLabel, string bundleVariant)
+ {
+ if (string.IsNullOrEmpty(BundleLabel) == false || string.IsNullOrEmpty(BundleVariant) == false)
+ throw new System.Exception("Should never get here !");
+
+ BundleLabel = bundleLabel;
+ BundleVariant = bundleVariant;
+ }
+
+ ///
+ /// 添加资源标记
+ ///
+ public void AddAssetTags(List tags)
+ {
+ foreach (var tag in tags)
+ {
+ if (AssetTags.Contains(tag) == false)
+ {
+ AssetTags.Add(tag);
+ }
+ }
+ }
+
+ ///
+ /// 获取资源包的完整名称
+ ///
+ public string GetBundleName()
+ {
+ if (string.IsNullOrEmpty(BundleLabel) || string.IsNullOrEmpty(BundleVariant))
+ throw new System.ArgumentNullException();
+
+ return AssetBundleBuilderHelper.MakeBundleName(BundleLabel, BundleVariant);
+ }
+
+ ///
+ /// 检测资源包名是否有效
+ ///
+ public bool CheckBundleNameValid()
+ {
+ if (string.IsNullOrEmpty(BundleLabel) == false && string.IsNullOrEmpty(BundleVariant) == false)
+ return true;
+ else
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildAssetInfo.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildAssetInfo.cs.meta
new file mode 100644
index 0000000..d7e1149
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildAssetInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 447008dd110b8d746aafbe88c78bee5d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildBundleInfo.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildBundleInfo.cs
new file mode 100644
index 0000000..96eff3e
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildBundleInfo.cs
@@ -0,0 +1,148 @@
+using System.Linq;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEditor;
+
+namespace YooAsset.Editor
+{
+ ///
+ /// 构建的资源包信息类
+ ///
+ public class BuildBundleInfo
+ {
+ ///
+ /// 资源包完整名称
+ ///
+ public string BundleName { private set; get; }
+
+ ///
+ /// 资源包标签名
+ ///
+ public string BundleLabel { private set; get; }
+
+ ///
+ /// 资源包文件格式
+ ///
+ public string BundleVariant { private set; get; }
+
+ ///
+ /// 包含的资源列表
+ ///
+ public readonly List Assets = new List();
+
+ ///
+ /// 是否为原生文件
+ ///
+ public bool IsRawFile
+ {
+ get
+ {
+ foreach (var asset in Assets)
+ {
+ if (asset.IsRawAsset)
+ return true;
+ }
+ return false;
+ }
+ }
+
+
+ public BuildBundleInfo(string bundleLabel, string bundleVariant)
+ {
+ BundleLabel = bundleLabel;
+ BundleVariant = bundleVariant;
+ BundleName = AssetBundleBuilderHelper.MakeBundleName(bundleLabel, bundleVariant);
+ }
+
+ ///
+ /// 是否包含指定资源
+ ///
+ public bool IsContainsAsset(string assetPath)
+ {
+ foreach (var assetInfo in Assets)
+ {
+ if (assetInfo.AssetPath == assetPath)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// 添加一个打包资源
+ ///
+ public void PackAsset(BuildAssetInfo assetInfo)
+ {
+ if (IsContainsAsset(assetInfo.AssetPath))
+ throw new System.Exception($"Asset is existed : {assetInfo.AssetPath}");
+
+ Assets.Add(assetInfo);
+ }
+
+ ///
+ /// 获取文件的扩展名
+ ///
+ public string GetAppendExtension()
+ {
+ if (IsRawFile)
+ return $".{ResourceSettingData.Setting.RawFileVariant}";
+ else
+ return $".{ResourceSettingData.Setting.AssetBundleFileVariant}";
+ }
+
+ ///
+ /// 获取资源标记列表
+ ///
+ public string[] GetAssetTags()
+ {
+ List result = new List(Assets.Count);
+ foreach (var assetInfo in Assets)
+ {
+ foreach (var assetTag in assetInfo.AssetTags)
+ {
+ if (result.Contains(assetTag) == false)
+ result.Add(assetTag);
+ }
+ }
+ return result.ToArray();
+ }
+
+ ///
+ /// 获取主动收集的资源路径列表
+ ///
+ public string[] GetCollectAssetPaths()
+ {
+ return Assets.Where(t => t.IsCollectAsset).Select(t => t.AssetPath).ToArray();
+ }
+
+ ///
+ /// 获取构建的资源路径列表
+ ///
+ public string[] GetBuildinAssetPaths()
+ {
+ return Assets.Select(t => t.AssetPath).ToArray();
+ }
+
+ ///
+ /// 获取主动收集的资源信息列表
+ ///
+ public BuildAssetInfo[] GetCollectAssetInfos()
+ {
+ return Assets.Where(t => t.IsCollectAsset).ToArray();
+ }
+
+ ///
+ /// 创建AssetBundleBuild类
+ ///
+ public UnityEditor.AssetBundleBuild CreatePipelineBuild()
+ {
+ // 注意:我们不在支持AssetBundle的变种机制
+ AssetBundleBuild build = new AssetBundleBuild();
+ build.assetBundleName = BundleName;
+ build.assetBundleVariant = string.Empty;
+ build.assetNames = GetBuildinAssetPaths();
+ return build;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildBundleInfo.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildBundleInfo.cs.meta
new file mode 100644
index 0000000..d9e3552
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildBundleInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 826a9d7b4de0eba40b5c39b33747c011
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem.meta
new file mode 100644
index 0000000..59ef26f
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2875232ad7f87c148b752d432f3a54f2
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildContext.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildContext.cs
new file mode 100644
index 0000000..5d80adf
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildContext.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace YooAsset.Editor
+{
+ public class BuildContext
+ {
+ private readonly Dictionary _contextObjects = new Dictionary();
+
+ ///
+ /// 清空所有情景对象
+ ///
+ public void ClearAllContext()
+ {
+ _contextObjects.Clear();
+ }
+
+ ///
+ /// 设置情景对象
+ ///
+ public void SetContextObject(IContextObject contextObject)
+ {
+ if (contextObject == null)
+ throw new ArgumentNullException("contextObject");
+
+ var type = contextObject.GetType();
+ if (_contextObjects.ContainsKey(type))
+ throw new Exception($"Context object {type} is already existed.");
+
+ _contextObjects.Add(type, contextObject);
+ }
+
+ ///
+ /// 获取情景对象
+ ///
+ public T GetContextObject() where T : IContextObject
+ {
+ var type = typeof(T);
+ if (_contextObjects.TryGetValue(type, out IContextObject contextObject))
+ {
+ return (T)contextObject;
+ }
+ else
+ {
+ throw new Exception($"Not found context object : {type}");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildContext.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildContext.cs.meta
new file mode 100644
index 0000000..d2fa01b
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildContext.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6a232601f99c4634ea17fca4979f80ab
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildRunner.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildRunner.cs
new file mode 100644
index 0000000..f259af3
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildRunner.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace YooAsset.Editor
+{
+ public class BuildRunner
+ {
+ ///
+ /// 执行构建流程
+ ///
+ /// 如果成功返回TRUE,否则返回FALSE
+ public static bool Run(List pipeline, BuildContext context)
+ {
+ if (pipeline == null)
+ throw new ArgumentNullException("pipeline");
+ if (context == null)
+ throw new ArgumentNullException("context");
+
+ bool succeed = true;
+ for (int i = 0; i < pipeline.Count; i++)
+ {
+ IBuildTask task = pipeline[i];
+ try
+ {
+ task.Run(context);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"Build task {task.GetType().Name} failed : {e}");
+ succeed = false;
+ break;
+ }
+ }
+
+ // 返回运行结果
+ return succeed;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildRunner.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildRunner.cs.meta
new file mode 100644
index 0000000..ad8b244
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/BuildRunner.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 79014124da678684388454d6ea892722
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IBuildTask.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IBuildTask.cs
new file mode 100644
index 0000000..7158143
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IBuildTask.cs
@@ -0,0 +1,8 @@
+
+namespace YooAsset.Editor
+{
+ public interface IBuildTask
+ {
+ void Run(BuildContext context);
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IBuildTask.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IBuildTask.cs.meta
new file mode 100644
index 0000000..f6b7e53
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IBuildTask.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8888657c45e12c646a8349bac6bf0326
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IContextObject.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IContextObject.cs
new file mode 100644
index 0000000..9845cce
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IContextObject.cs
@@ -0,0 +1,7 @@
+
+namespace YooAsset.Editor
+{
+ public interface IContextObject
+ {
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IContextObject.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IContextObject.cs.meta
new file mode 100644
index 0000000..c51328c
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildSystem/IContextObject.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 151a4acd5ad1c2046be20e080f9bdad4
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks.meta
new file mode 100644
index 0000000..863df44
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 254f430e0264bd84387878f8d7280e44
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskBuilding.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskBuilding.cs
new file mode 100644
index 0000000..78c945a
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskBuilding.cs
@@ -0,0 +1,194 @@
+using System;
+using System.Linq;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+
+namespace YooAsset.Editor
+{
+ public class TaskBuilding : IBuildTask
+ {
+ public class UnityManifestContext : IContextObject
+ {
+ public AssetBundleManifest UnityManifest;
+ }
+
+ void IBuildTask.Run(BuildContext context)
+ {
+ var buildParametersContext = context.GetContextObject();
+ var buildMapContext = context.GetContextObject();
+
+ Debug.Log($"开始构建......");
+ BuildAssetBundleOptions opt = buildParametersContext.GetPipelineBuildOptions();
+ AssetBundleManifest unityManifest = BuildPipeline.BuildAssetBundles(buildParametersContext.PipelineOutputDirectory, buildMapContext.GetPipelineBuilds(), opt, buildParametersContext.Parameters.BuildTarget);
+ if (unityManifest == null)
+ throw new Exception("构建过程中发生错误!");
+
+ UnityManifestContext unityManifestContext = new UnityManifestContext();
+ unityManifestContext.UnityManifest = unityManifest;
+ context.SetContextObject(unityManifestContext);
+
+ // 拷贝原生文件
+ foreach (var bundleInfo in buildMapContext.BundleInfos)
+ {
+ if (bundleInfo.IsRawFile)
+ {
+ string dest = $"{buildParametersContext.PipelineOutputDirectory}/{bundleInfo.BundleName}";
+ foreach(var buildAsset in bundleInfo.Assets)
+ {
+ if(buildAsset.IsRawAsset)
+ EditorTools.CopyFile(buildAsset.AssetPath, dest, true);
+ }
+ }
+ }
+
+ // 验证构建结果
+ if (buildParametersContext.Parameters.IsVerifyBuildingResult)
+ {
+ VerifyingBuildingResult(context, unityManifest);
+ }
+ }
+
+ ///
+ /// 验证构建结果
+ ///
+ private void VerifyingBuildingResult(BuildContext context, AssetBundleManifest unityManifest)
+ {
+ var buildParameters = context.GetContextObject();
+ var buildMapContext = context.GetContextObject();
+ string[] buildedBundles = unityManifest.GetAllAssetBundles();
+
+ // 1. 过滤掉原生Bundle
+ List expectBundles = new List(buildedBundles.Length);
+ foreach(var bundleInfo in buildMapContext.BundleInfos)
+ {
+ if (bundleInfo.IsRawFile == false)
+ expectBundles.Add(bundleInfo);
+ }
+
+ // 2. 验证数量
+ if (buildedBundles.Length != expectBundles.Count)
+ {
+ Debug.LogWarning($"构建过程中可能存在无效的资源,导致和预期构建的Bundle数量不一致!");
+ }
+
+ // 3. 正向验证Bundle
+ foreach (var bundleName in buildedBundles)
+ {
+ if (buildMapContext.IsContainsBundle(bundleName) == false)
+ {
+ throw new Exception($"Should never get here !");
+ }
+ }
+
+ // 4. 反向验证Bundle
+ bool isPass = true;
+ foreach (var expectBundle in expectBundles)
+ {
+ bool isMatch = false;
+ foreach (var buildedBundle in buildedBundles)
+ {
+ if (buildedBundle == expectBundle.BundleName)
+ {
+ isMatch = true;
+ break;
+ }
+ }
+ if (isMatch == false)
+ {
+ isPass = false;
+ Debug.LogWarning($"没有找到预期构建的Bundle文件 : {expectBundle.BundleName}");
+ }
+ }
+ if(isPass == false)
+ {
+ throw new Exception("构建结果验证没有通过,请参考警告日志!");
+ }
+
+ // 5. 验证Asset
+ int progressValue = 0;
+ foreach (var buildedBundle in buildedBundles)
+ {
+ string filePath = $"{buildParameters.PipelineOutputDirectory}/{buildedBundle}";
+ string[] allBuildinAssetPaths = GetAssetBundleAllAssets(filePath);
+ string[] expectBuildinAssetPaths = buildMapContext.GetBuildinAssetPaths(buildedBundle);
+ if (expectBuildinAssetPaths.Length != allBuildinAssetPaths.Length)
+ {
+ Debug.LogWarning($"构建的Bundle文件内的资源对象数量和预期不匹配 : {buildedBundle}");
+ isPass = false;
+ continue;
+ }
+
+ foreach (var buildinAssetPath in allBuildinAssetPaths)
+ {
+ var guid = AssetDatabase.AssetPathToGUID(buildinAssetPath);
+ if (string.IsNullOrEmpty(guid))
+ {
+ Debug.LogWarning($"无效的资源路径,请检查路径是否带有特殊符号或中文:{buildinAssetPath}");
+ isPass = false;
+ continue;
+ }
+
+ bool isMatch = false;
+ foreach (var exceptBuildAssetPath in expectBuildinAssetPaths)
+ {
+ var guidExcept = AssetDatabase.AssetPathToGUID(exceptBuildAssetPath);
+ if (guid == guidExcept)
+ {
+ isMatch = true;
+ break;
+ }
+ }
+ if (isMatch == false)
+ {
+ Debug.LogWarning($"在构建的Bundle文件里发现了没有匹配的资源对象:{buildinAssetPath}");
+ isPass = false;
+ continue;
+ }
+ }
+
+ EditorTools.DisplayProgressBar("验证构建结果", ++progressValue, buildedBundles.Length);
+ }
+ EditorTools.ClearProgressBar();
+ if (isPass == false)
+ {
+ throw new Exception("构建结果验证没有通过,请参考警告日志!");
+ }
+
+ // 卸载所有加载的Bundle
+ Debug.Log("构建结果验证成功!");
+ }
+
+ ///
+ /// 解析.manifest文件并获取资源列表
+ ///
+ private string[] GetAssetBundleAllAssets(string filePath)
+ {
+ string manifestFilePath = $"{filePath}.manifest";
+ List assetLines = new List();
+ using (StreamReader reader = File.OpenText(manifestFilePath))
+ {
+ string content;
+ bool findTarget = false;
+ while (null != (content = reader.ReadLine()))
+ {
+ if (content.StartsWith("Dependencies:"))
+ break;
+ if (findTarget == false && content.StartsWith("Assets:"))
+ findTarget = true;
+ if (findTarget)
+ {
+ if (content.StartsWith("- "))
+ {
+ string assetPath = content.TrimStart("- ".ToCharArray());
+ assetLines.Add(assetPath);
+ }
+ }
+ }
+ }
+ return assetLines.ToArray();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskBuilding.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskBuilding.cs.meta
new file mode 100644
index 0000000..c4d1e7c
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskBuilding.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9466e826c135e994c84961e4b921ca5f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCopyBuildinFiles.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCopyBuildinFiles.cs
new file mode 100644
index 0000000..faa917a
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCopyBuildinFiles.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+
+namespace YooAsset.Editor
+{
+ ///
+ /// 拷贝内置文件到StreamingAssets
+ ///
+ public class TaskCopyBuildinFiles : IBuildTask
+ {
+ void IBuildTask.Run(BuildContext context)
+ {
+ // 注意:我们只有在强制重建的时候才会拷贝
+ var buildParameters = context.GetContextObject();
+ if(buildParameters.Parameters.IsForceRebuild)
+ {
+ // 清空流目录
+ AssetBundleBuilderHelper.ClearStreamingAssetsFolder();
+
+ // 拷贝内置文件
+ var pipelineOutputDirectory = buildParameters.PipelineOutputDirectory;
+ CopyBuildinFilesToStreaming(pipelineOutputDirectory);
+ }
+ }
+
+ private void CopyBuildinFilesToStreaming(string pipelineOutputDirectory)
+ {
+ // 加载补丁清单
+ PatchManifest patchManifest = AssetBundleBuilderHelper.LoadPatchManifestFile(pipelineOutputDirectory);
+
+ // 拷贝文件列表
+ foreach (var patchBundle in patchManifest.BundleList)
+ {
+ if (patchBundle.IsBuildin == false)
+ continue;
+
+ string sourcePath = $"{pipelineOutputDirectory}/{patchBundle.BundleName}";
+ string destPath = $"{Application.dataPath}/StreamingAssets/{patchBundle.Hash}";
+ Debug.Log($"拷贝内置文件到流目录:{patchBundle.BundleName}");
+ EditorTools.CopyFile(sourcePath, destPath, true);
+ }
+
+ // 拷贝清单文件
+ {
+ string sourcePath = $"{pipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestFileName}";
+ string destPath = $"{Application.dataPath}/StreamingAssets/{ResourceSettingData.Setting.PatchManifestFileName}";
+ EditorTools.CopyFile(sourcePath, destPath, true);
+ }
+
+ // 拷贝清单哈希文件
+ {
+ string sourcePath = $"{pipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestHashFileName}";
+ string destPath = $"{Application.dataPath}/StreamingAssets/{ResourceSettingData.Setting.PatchManifestHashFileName}";
+ EditorTools.CopyFile(sourcePath, destPath, true);
+ }
+
+ // 刷新目录
+ AssetDatabase.Refresh();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCopyBuildinFiles.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCopyBuildinFiles.cs.meta
new file mode 100644
index 0000000..6ac8176
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCopyBuildinFiles.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5c77e17c3a3a57548a218f1cd26f5a55
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchManifest.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchManifest.cs
new file mode 100644
index 0000000..18a0bbd
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchManifest.cs
@@ -0,0 +1,155 @@
+using System;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using YooAsset.Utility;
+
+namespace YooAsset.Editor
+{
+ ///
+ /// 创建补丁清单文件
+ ///
+ public class TaskCreatePatchManifest : IBuildTask
+ {
+ void IBuildTask.Run(BuildContext context)
+ {
+ var buildParameters = context.GetContextObject();
+ var encryptionContext = context.GetContextObject();
+ var buildMapContext = context.GetContextObject();
+ CreatePatchManifestFile(buildParameters, buildMapContext, encryptionContext);
+ }
+
+ ///
+ /// 创建补丁清单文件到输出目录
+ ///
+ private void CreatePatchManifestFile(AssetBundleBuilder.BuildParametersContext buildParameters,
+ TaskGetBuildMap.BuildMapContext buildMapContext, TaskEncryption.EncryptionContext encryptionContext)
+ {
+ // 创建新补丁清单
+ PatchManifest patchManifest = new PatchManifest();
+ patchManifest.ResourceVersion = buildParameters.Parameters.BuildVersion;
+ patchManifest.BuildinTags = buildParameters.Parameters.BuildinTags;
+ patchManifest.BundleList = GetAllPatchBundle(buildParameters, buildMapContext, encryptionContext);
+ patchManifest.AssetList = GetAllPatchAsset(buildMapContext, patchManifest.BundleList);
+
+ // 创建补丁清单文件
+ string manifestFilePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestFileName}";
+ UnityEngine.Debug.Log($"创建补丁清单文件:{manifestFilePath}");
+ PatchManifest.Serialize(manifestFilePath, patchManifest);
+
+ // 创建补丁清单哈希文件
+ string manifestHashFilePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestHashFileName}";
+ string manifestHash = HashUtility.FileMD5(manifestFilePath);
+ UnityEngine.Debug.Log($"创建补丁清单哈希文件:{manifestHashFilePath}");
+ FileUtility.CreateFile(manifestHashFilePath, manifestHash);
+ }
+
+ ///
+ /// 获取资源包列表
+ ///
+ private List GetAllPatchBundle(AssetBundleBuilder.BuildParametersContext buildParameters,
+ TaskGetBuildMap.BuildMapContext buildMapContext, TaskEncryption.EncryptionContext encryptionContext)
+ {
+ List result = new List(1000);
+
+ // 内置标记列表
+ List buildinTags = buildParameters.Parameters.GetBuildinTags();
+
+ // 加载旧补丁清单
+ PatchManifest oldPatchManifest = null;
+ if (buildParameters.Parameters.IsForceRebuild == false)
+ {
+ oldPatchManifest = AssetBundleBuilderHelper.LoadPatchManifestFile(buildParameters.PipelineOutputDirectory);
+ }
+
+ foreach (var bundleInfo in buildMapContext.BundleInfos)
+ {
+ var bundleName = bundleInfo.BundleName;
+ string filePath = $"{buildParameters.PipelineOutputDirectory}/{bundleName}";
+ string hash = HashUtility.FileMD5(filePath);
+ string crc = HashUtility.FileCRC32(filePath);
+ long size = FileUtility.GetFileSize(filePath);
+ int version = buildParameters.Parameters.BuildVersion;
+ string[] tags = buildMapContext.GetAssetTags(bundleName);
+ bool isEncrypted = encryptionContext.IsEncryptFile(bundleName);
+ bool isBuildin = IsBuildinBundle(tags, buildinTags);
+ bool isRawFile = bundleInfo.IsRawFile;
+
+ // 附加文件扩展名
+ if (buildParameters.Parameters.AppendFileExtension)
+ {
+ hash += bundleInfo.GetAppendExtension();
+ }
+
+ // 注意:如果文件没有变化使用旧版本号
+ if (oldPatchManifest != null && oldPatchManifest.Bundles.TryGetValue(bundleName, out PatchBundle value))
+ {
+ if (value.Hash == hash)
+ version = value.Version;
+ }
+
+ PatchBundle patchBundle = new PatchBundle(bundleName, hash, crc, size, version, tags);
+ patchBundle.SetFlagsValue(isEncrypted, isBuildin, isRawFile);
+ result.Add(patchBundle);
+ }
+
+ return result;
+ }
+ private bool IsBuildinBundle(string[] bundleTags, List buildinTags)
+ {
+ // 注意:没有任何标记的Bundle文件默认为内置文件
+ if (bundleTags.Length == 0)
+ return true;
+
+ foreach (var tag in bundleTags)
+ {
+ if (buildinTags.Contains(tag))
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// 获取资源列表
+ ///
+ private List GetAllPatchAsset(TaskGetBuildMap.BuildMapContext buildMapContext, List bundleList)
+ {
+ List result = new List(1000);
+ foreach (var bundleInfo in buildMapContext.BundleInfos)
+ {
+ var assetInfos = bundleInfo.GetCollectAssetInfos();
+ foreach (var assetInfo in assetInfos)
+ {
+ PatchAsset patchAsset = new PatchAsset();
+ patchAsset.AssetPath = assetInfo.AssetPath;
+ patchAsset.BundleID = GetAssetBundleID(assetInfo.GetBundleName(), bundleList);
+ patchAsset.DependIDs = GetAssetBundleDependIDs(assetInfo, bundleList);
+ result.Add(patchAsset);
+ }
+ }
+ return result;
+ }
+ private int[] GetAssetBundleDependIDs(BuildAssetInfo assetInfo, List bundleList)
+ {
+ List result = new List();
+ foreach (var dependAssetInfo in assetInfo.AllDependAssetInfos)
+ {
+ if (dependAssetInfo.CheckBundleNameValid() == false)
+ continue;
+ int bundleID = GetAssetBundleID(dependAssetInfo.GetBundleName(), bundleList);
+ if (result.Contains(bundleID) == false)
+ result.Add(bundleID);
+ }
+ return result.ToArray();
+ }
+ private int GetAssetBundleID(string bundleName, List bundleList)
+ {
+ for (int index = 0; index < bundleList.Count; index++)
+ {
+ if (bundleList[index].BundleName == bundleName)
+ return index;
+ }
+ throw new Exception($"Not found bundle name : {bundleName}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchManifest.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchManifest.cs.meta
new file mode 100644
index 0000000..53cf77f
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchManifest.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 566c37f5a59f2e84397a9527981a7310
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchPackage.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchPackage.cs
new file mode 100644
index 0000000..a4b8b6f
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchPackage.cs
@@ -0,0 +1,83 @@
+using System.Collections;
+using System.Collections.Generic;
+
+namespace YooAsset.Editor
+{
+ ///
+ /// 制作补丁包
+ ///
+ public class TaskCreatePatchPackage : IBuildTask
+ {
+ void IBuildTask.Run(BuildContext context)
+ {
+ var buildParameters = context.GetContextObject();
+ CopyPatchFiles(buildParameters);
+ }
+
+ ///
+ /// 拷贝补丁文件到补丁包目录
+ ///
+ private void CopyPatchFiles(AssetBundleBuilder.BuildParametersContext buildParameters)
+ {
+ string packageDirectory = buildParameters.GetPackageDirectory();
+ UnityEngine.Debug.Log($"开始拷贝补丁文件到补丁包目录:{packageDirectory}");
+
+ // 拷贝Readme文件
+ {
+ string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.ReadmeFileName}";
+ string destPath = $"{packageDirectory}/{ResourceSettingData.Setting.ReadmeFileName}";
+ EditorTools.CopyFile(sourcePath, destPath, true);
+ UnityEngine.Debug.Log($"拷贝Readme文件到:{destPath}");
+ }
+
+ // 拷贝PatchManifest文件
+ {
+ string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestFileName}";
+ string destPath = $"{packageDirectory}/{ResourceSettingData.Setting.PatchManifestFileName}";
+ EditorTools.CopyFile(sourcePath, destPath, true);
+ UnityEngine.Debug.Log($"拷贝PatchManifest文件到:{destPath}");
+ }
+
+ // 拷贝PatchManifest哈希文件
+ {
+ string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.PatchManifestHashFileName}";
+ string destPath = $"{packageDirectory}/{ResourceSettingData.Setting.PatchManifestHashFileName}";
+ EditorTools.CopyFile(sourcePath, destPath, true);
+ UnityEngine.Debug.Log($"拷贝PatchManifest哈希文件到:{destPath}");
+ }
+
+ // 拷贝UnityManifest序列化文件
+ {
+ string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.UnityManifestFileName}";
+ string destPath = $"{packageDirectory}/{ResourceSettingData.Setting.UnityManifestFileName}";
+ EditorTools.CopyFile(sourcePath, destPath, true);
+ UnityEngine.Debug.Log($"拷贝UnityManifest文件到:{destPath}");
+ }
+
+ // 拷贝UnityManifest文本文件
+ {
+ string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.UnityManifestFileName}.manifest";
+ string destPath = $"{packageDirectory}/{ResourceSettingData.Setting.UnityManifestFileName}.manifest";
+ EditorTools.CopyFile(sourcePath, destPath, true);
+ }
+
+ // 拷贝所有补丁文件
+ // 注意:拷贝的补丁文件都是需要玩家热更新的文件
+ int progressValue = 0;
+ PatchManifest patchManifest = AssetBundleBuilderHelper.LoadPatchManifestFile(buildParameters.PipelineOutputDirectory);
+ int patchFileTotalCount = patchManifest.BundleList.Count;
+ foreach (var patchBundle in patchManifest.BundleList)
+ {
+ if (patchBundle.Version == buildParameters.Parameters.BuildVersion)
+ {
+ string sourcePath = $"{buildParameters.PipelineOutputDirectory}/{patchBundle.BundleName}";
+ string destPath = $"{packageDirectory}/{patchBundle.Hash}";
+ EditorTools.CopyFile(sourcePath, destPath, true);
+ UnityEngine.Debug.Log($"拷贝补丁文件到补丁包:{patchBundle.BundleName}");
+ EditorTools.DisplayProgressBar("拷贝补丁文件", ++progressValue, patchFileTotalCount);
+ }
+ }
+ EditorTools.ClearProgressBar();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchPackage.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchPackage.cs.meta
new file mode 100644
index 0000000..0f45229
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreatePatchPackage.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d110092f543e3fe40a4d3882e1a718e8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreateReadme.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreateReadme.cs
new file mode 100644
index 0000000..14e4108
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreateReadme.cs
@@ -0,0 +1,205 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace YooAsset.Editor
+{
+ ///
+ /// 创建说明文件
+ ///
+ public class TaskCreateReadme : IBuildTask
+ {
+ void IBuildTask.Run(BuildContext context)
+ {
+ var buildParameters = context.GetContextObject();
+ var buildMapContext = context.GetContextObject();
+ CreateReadmeFile(buildParameters, buildMapContext);
+ }
+
+ ///
+ /// 创建Readme文件到输出目录
+ ///
+ private void CreateReadmeFile(AssetBundleBuilder.BuildParametersContext buildParameters, TaskGetBuildMap.BuildMapContext buildMapContext)
+ {
+ PatchManifest patchManifest = AssetBundleBuilderHelper.LoadPatchManifestFile(buildParameters.PipelineOutputDirectory);
+
+ // 删除旧文件
+ string filePath = $"{buildParameters.PipelineOutputDirectory}/{ResourceSettingData.Setting.ReadmeFileName}";
+ if (File.Exists(filePath))
+ File.Delete(filePath);
+
+ UnityEngine.Debug.Log($"创建说明文件:{filePath}");
+
+ StringBuilder content = new StringBuilder();
+ AppendData(content, $"构建时间:{DateTime.Now}");
+ AppendData(content, $"构建平台:{buildParameters.Parameters.BuildTarget}");
+ AppendData(content, $"构建版本:{buildParameters.Parameters.BuildVersion}");
+ AppendData(content, $"冗余机制:{buildParameters.Parameters.ApplyRedundancy}");
+
+ AppendData(content, "");
+ AppendData(content, $"--着色器--");
+ AppendData(content, $"IsCollectAllShaders:{AssetBundleCollectorSettingData.Setting.IsCollectAllShaders}");
+ AppendData(content, $"ShadersBundleName:{AssetBundleCollectorSettingData.Setting.ShadersBundleName}");
+
+ AppendData(content, "");
+ AppendData(content, $"--配置信息--");
+ for (int i = 0; i < AssetBundleCollectorSettingData.Setting.Collectors.Count; i++)
+ {
+ AssetBundleCollectorSetting.Collector wrapper = AssetBundleCollectorSettingData.Setting.Collectors[i];
+ AppendData(content, wrapper.ToString());
+ }
+
+ AppendData(content, "");
+ AppendData(content, $"--构建参数--");
+ AppendData(content, $"CompressOption:{buildParameters.Parameters.CompressOption}");
+ AppendData(content, $"IsForceRebuild:{buildParameters.Parameters.IsForceRebuild}");
+ AppendData(content, $"BuildinTags:{buildParameters.Parameters.BuildinTags}");
+ AppendData(content, $"IsAppendHash:{buildParameters.Parameters.IsAppendHash}");
+ AppendData(content, $"IsDisableWriteTypeTree:{buildParameters.Parameters.IsDisableWriteTypeTree}");
+ AppendData(content, $"IsIgnoreTypeTreeChanges:{buildParameters.Parameters.IsIgnoreTypeTreeChanges}");
+ AppendData(content, $"IsDisableLoadAssetByFileName : {buildParameters.Parameters.IsDisableLoadAssetByFileName}");
+
+ AppendData(content, "");
+ AppendData(content, $"--构建信息--");
+ AppendData(content, $"参与构建的资源总数:{buildMapContext.GetAllAssets().Count}");
+ GetBundleFileCountAndTotalSize(patchManifest, out int fileCount1, out long fileTotalSize1);
+ AppendData(content, $"构建的资源包总数:{fileCount1} 文件总大小:{fileTotalSize1 / (1024 * 1024)}MB");
+ GetBuildinFileCountAndTotalSize(patchManifest, out int fileCount2, out long fileTotalSize2);
+ AppendData(content, $"内置的资源包总数:{fileCount2} 文件总大小:{fileTotalSize2 / (1024 * 1024)}MB");
+ GetNotBuildinFileCountAndTotalSize(patchManifest, out int fileCount3, out long fileTotalSize3);
+ AppendData(content, $"非内置的资源包总数:{fileCount3} 文件总大小:{fileTotalSize3 / (1024 * 1024)}MB");
+ GetEncryptedFileCountAndTotalSize(patchManifest, out int fileCount4, out long fileTotalSize4);
+ AppendData(content, $"加密的资源包总数:{fileCount4} 文件总大小:{fileTotalSize4 / (1024 * 1024)}MB");
+ GetRawFileCountAndTotalSize(patchManifest, out int fileCount5, out long fileTotalSize5);
+ AppendData(content, $"原生的资源包总数:{fileCount5} 文件总大小:{fileTotalSize5 / (1024 * 1024)}MB");
+
+ AppendData(content, "");
+ AppendData(content, $"--冗余列表--");
+ for (int i = 0; i < buildMapContext.RedundancyList.Count; i++)
+ {
+ string redundancyAssetPath = buildMapContext.RedundancyList[i];
+ AppendData(content, redundancyAssetPath);
+ }
+
+ AppendData(content, "");
+ AppendData(content, $"--构建列表--");
+ for (int i = 0; i < buildMapContext.BundleInfos.Count; i++)
+ {
+ string bundleName = buildMapContext.BundleInfos[i].BundleName;
+ AppendData(content, bundleName);
+ }
+
+ AppendData(content, "");
+ AppendData(content, $"--内置文件列表--");
+ foreach (var patchBundle in patchManifest.BundleList)
+ {
+ if (patchBundle.IsBuildin)
+ {
+ AppendData(content, patchBundle.BundleName);
+ }
+ }
+
+ AppendData(content, "");
+ AppendData(content, $"--非内置文件列表--");
+ foreach (var patchBundle in patchManifest.BundleList)
+ {
+ if (patchBundle.IsBuildin == false)
+ {
+ AppendData(content, patchBundle.BundleName);
+ }
+ }
+
+ AppendData(content, "");
+ AppendData(content, $"--加密文件列表--");
+ foreach (var patchBundle in patchManifest.BundleList)
+ {
+ if (patchBundle.IsEncrypted)
+ {
+ AppendData(content, patchBundle.BundleName);
+ }
+ }
+
+ AppendData(content, "");
+ AppendData(content, $"--原生文件列表--");
+ foreach (var patchBundle in patchManifest.BundleList)
+ {
+ if (patchBundle.IsRawFile)
+ {
+ AppendData(content, patchBundle.BundleName);
+ }
+ }
+
+ // 创建新文件
+ File.WriteAllText(filePath, content.ToString(), Encoding.UTF8);
+ }
+ private void AppendData(StringBuilder sb, string data)
+ {
+ sb.Append(data);
+ sb.Append("\r\n");
+ }
+
+ private void GetBundleFileCountAndTotalSize(PatchManifest patchManifest, out int fileCount, out long fileBytes)
+ {
+ fileCount = patchManifest.BundleList.Count;
+ fileBytes = 0;
+ foreach (var patchBundle in patchManifest.BundleList)
+ {
+ fileBytes += patchBundle.SizeBytes;
+ }
+ }
+ private void GetBuildinFileCountAndTotalSize(PatchManifest patchManifest, out int fileCount, out long fileBytes)
+ {
+ fileCount = 0;
+ fileBytes = 0;
+ foreach (var patchBundle in patchManifest.BundleList)
+ {
+ if (patchBundle.IsBuildin)
+ {
+ fileCount++;
+ fileBytes += patchBundle.SizeBytes;
+ }
+ }
+ }
+ private void GetNotBuildinFileCountAndTotalSize(PatchManifest patchManifest, out int fileCount, out long fileBytes)
+ {
+ fileCount = 0;
+ fileBytes = 0;
+ foreach (var patchBundle in patchManifest.BundleList)
+ {
+ if (patchBundle.IsBuildin == false)
+ {
+ fileCount++;
+ fileBytes += patchBundle.SizeBytes;
+ }
+ }
+ }
+ private void GetEncryptedFileCountAndTotalSize(PatchManifest patchManifest, out int fileCount, out long fileBytes)
+ {
+ fileCount = 0;
+ fileBytes = 0;
+ foreach (var patchBundle in patchManifest.BundleList)
+ {
+ if (patchBundle.IsEncrypted)
+ {
+ fileCount++;
+ fileBytes += patchBundle.SizeBytes;
+ }
+ }
+ }
+ private void GetRawFileCountAndTotalSize(PatchManifest patchManifest, out int fileCount, out long fileBytes)
+ {
+ fileCount = 0;
+ fileBytes = 0;
+ foreach (var patchBundle in patchManifest.BundleList)
+ {
+ if (patchBundle.IsRawFile)
+ {
+ fileCount++;
+ fileBytes += patchBundle.SizeBytes;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreateReadme.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreateReadme.cs.meta
new file mode 100644
index 0000000..2687bdc
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskCreateReadme.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1660a082a6449f4429fcca15e4383f0b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskEncryption.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskEncryption.cs
new file mode 100644
index 0000000..7085c57
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskEncryption.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Linq;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using YooAsset.Utility;
+
+namespace YooAsset.Editor
+{
+ public class TaskEncryption : IBuildTask
+ {
+ public class EncryptionContext : IContextObject
+ {
+ public List EncryptList;
+
+ ///
+ /// 检测是否为加密文件
+ ///
+ public bool IsEncryptFile(string bundleName)
+ {
+ return EncryptList.Contains(bundleName);
+ }
+ }
+
+ void IBuildTask.Run(BuildContext context)
+ {
+ var buildParameters = context.GetContextObject();
+ var buildMapContext = context.GetContextObject();
+
+ var encrypter = CreateAssetEncrypter();
+ List encryptList = EncryptFiles(encrypter, buildParameters, buildMapContext);
+
+ EncryptionContext encryptionContext = new EncryptionContext();
+ encryptionContext.EncryptList = encryptList;
+ context.SetContextObject(encryptionContext);
+ }
+
+ ///
+ /// 创建加密类
+ ///
+ /// 如果没有定义类型,则返回NULL
+ private IAssetEncrypter CreateAssetEncrypter()
+ {
+ var types = AssemblyUtility.GetAssignableTypes(AssemblyUtility.UnityDefaultAssemblyEditorName, typeof(IAssetEncrypter));
+ if (types.Count == 0)
+ return null;
+ if (types.Count != 1)
+ throw new Exception($"Found more {nameof(IAssetEncrypter)} types. We only support one.");
+
+ UnityEngine.Debug.Log($"创建实例类 : {types[0].FullName}");
+ return (IAssetEncrypter)Activator.CreateInstance(types[0]);
+ }
+
+ ///
+ /// 加密文件
+ ///
+ private List EncryptFiles(IAssetEncrypter encrypter, AssetBundleBuilder.BuildParametersContext buildParameters, TaskGetBuildMap.BuildMapContext buildMapContext)
+ {
+ // 加密资源列表
+ List encryptList = new List();
+
+ // 如果没有设置加密类
+ if (encrypter == null)
+ return encryptList;
+
+ UnityEngine.Debug.Log($"开始加密资源文件");
+ int progressValue = 0;
+ foreach (var bundleInfo in buildMapContext.BundleInfos)
+ {
+ var bundleName = bundleInfo.BundleName;
+ string filePath = $"{buildParameters.PipelineOutputDirectory}/{bundleName}";
+ if (encrypter.Check(filePath))
+ {
+ encryptList.Add(bundleName);
+
+ // 注意:通过判断文件合法性,规避重复加密一个文件
+ byte[] fileData = File.ReadAllBytes(filePath);
+ if (EditorTools.CheckBundleFileValid(fileData))
+ {
+ byte[] bytes = encrypter.Encrypt(fileData);
+ File.WriteAllBytes(filePath, bytes);
+ UnityEngine.Debug.Log($"文件加密完成:{filePath}");
+ }
+ }
+
+ // 进度条
+ EditorTools.DisplayProgressBar("加密资源包", ++progressValue, buildMapContext.BundleInfos.Count);
+ }
+ EditorTools.ClearProgressBar();
+
+ return encryptList;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskEncryption.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskEncryption.cs.meta
new file mode 100644
index 0000000..6561a79
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskEncryption.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c78d471226b9c8d429a2d962370f480b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskGetBuildMap.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskGetBuildMap.cs
new file mode 100644
index 0000000..fd3163b
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskGetBuildMap.cs
@@ -0,0 +1,331 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEditor;
+using YooAsset.Utility;
+
+namespace YooAsset.Editor
+{
+ public class TaskGetBuildMap : IBuildTask
+ {
+ public class BuildMapContext : IContextObject
+ {
+ ///
+ /// 资源包列表
+ ///
+ public readonly List BundleInfos = new List(1000);
+
+ ///
+ /// 冗余的资源列表
+ ///
+ public readonly List RedundancyList = new List(1000);
+
+
+ ///
+ /// 添加一个打包资源
+ ///
+ public void PackAsset(BuildAssetInfo assetInfo)
+ {
+ if (TryGetBundleInfo(assetInfo.GetBundleName(), out BuildBundleInfo bundleInfo))
+ {
+ bundleInfo.PackAsset(assetInfo);
+ }
+ else
+ {
+ BuildBundleInfo newBundleInfo = new BuildBundleInfo(assetInfo.BundleLabel, assetInfo.BundleVariant);
+ newBundleInfo.PackAsset(assetInfo);
+ BundleInfos.Add(newBundleInfo);
+ }
+ }
+
+ ///
+ /// 获取所有的打包资源
+ ///
+ public List GetAllAssets()
+ {
+ List result = new List(BundleInfos.Count);
+ foreach (var bundleInfo in BundleInfos)
+ {
+ result.AddRange(bundleInfo.Assets);
+ }
+ return result;
+ }
+
+ ///
+ /// 获取AssetBundle内包含的标记列表
+ ///
+ public string[] GetAssetTags(string bundleFullName)
+ {
+ if (TryGetBundleInfo(bundleFullName, out BuildBundleInfo bundleInfo))
+ {
+ return bundleInfo.GetAssetTags();
+ }
+ throw new Exception($"Not found {nameof(BuildBundleInfo)} : {bundleFullName}");
+ }
+
+ ///
+ /// 获取AssetBundle内收集的资源路径列表
+ ///
+ public string[] GetCollectAssetPaths(string bundleFullName)
+ {
+ if (TryGetBundleInfo(bundleFullName, out BuildBundleInfo bundleInfo))
+ {
+ return bundleInfo.GetCollectAssetPaths();
+ }
+ throw new Exception($"Not found {nameof(BuildBundleInfo)} : {bundleFullName}");
+ }
+
+ ///
+ /// 获取AssetBundle内构建的资源路径列表
+ ///
+ public string[] GetBuildinAssetPaths(string bundleFullName)
+ {
+ if (TryGetBundleInfo(bundleFullName, out BuildBundleInfo bundleInfo))
+ {
+ return bundleInfo.GetBuildinAssetPaths();
+ }
+ throw new Exception($"Not found {nameof(BuildBundleInfo)} : {bundleFullName}");
+ }
+
+ ///
+ /// 获取构建管线里需要的数据
+ ///
+ public UnityEditor.AssetBundleBuild[] GetPipelineBuilds()
+ {
+ List builds = new List(BundleInfos.Count);
+ foreach (var bundleInfo in BundleInfos)
+ {
+ if (bundleInfo.IsRawFile == false)
+ builds.Add(bundleInfo.CreatePipelineBuild());
+ }
+ return builds.ToArray();
+ }
+
+ ///
+ /// 检测是否包含BundleName
+ ///
+ public bool IsContainsBundle(string bundleFullName)
+ {
+ return TryGetBundleInfo(bundleFullName, out BuildBundleInfo bundleInfo);
+ }
+
+ private bool TryGetBundleInfo(string bundleFullName, out BuildBundleInfo result)
+ {
+ foreach (var bundleInfo in BundleInfos)
+ {
+ if (bundleInfo.BundleName == bundleFullName)
+ {
+ result = bundleInfo;
+ return true;
+ }
+ }
+ result = null;
+ return false;
+ }
+ }
+
+
+ void IBuildTask.Run(BuildContext context)
+ {
+ var buildParametersContext = context.GetContextObject();
+ BuildMapContext buildMapContext = new BuildMapContext();
+ context.SetContextObject(buildMapContext);
+ SetupBuildMap(buildMapContext, buildParametersContext);
+
+ // 检测构建结果
+ CheckBuildMapContent(buildMapContext);
+ }
+
+ ///
+ /// 组织构建的资源包
+ ///
+ private void SetupBuildMap(BuildMapContext buildMapContext, AssetBundleBuilder.BuildParametersContext buildParameters)
+ {
+ Dictionary buildAssets = new Dictionary();
+
+ // 1. 获取主动收集的资源
+ List allCollectInfos = AssetBundleCollectorSettingData.GetAllCollectAssets();
+
+ // 2. 对收集的资源进行依赖分析
+ int progressValue = 0;
+ foreach (AssetCollectInfo collectInfo in allCollectInfos)
+ {
+ string mainAssetPath = collectInfo.AssetPath;
+
+ // 获取所有依赖资源
+ List depends = GetAllDependencies(mainAssetPath);
+ for (int i = 0; i < depends.Count; i++)
+ {
+ string assetPath = depends[i].AssetPath;
+
+ // 如果已经存在,则增加该资源的依赖计数
+ if (buildAssets.ContainsKey(assetPath))
+ {
+ buildAssets[assetPath].DependCount++;
+ }
+ else
+ {
+ buildAssets.Add(assetPath, depends[i]);
+ }
+
+ // 添加资源标记
+ buildAssets[assetPath].AddAssetTags(collectInfo.AssetTags);
+
+ // 注意:检测是否为主动收集资源
+ if (assetPath == mainAssetPath)
+ {
+ buildAssets[mainAssetPath].IsCollectAsset = true;
+ buildAssets[mainAssetPath].IsRawAsset = collectInfo.IsRawAsset;
+ }
+ }
+
+ // 添加所有的依赖资源列表
+ // 注意:不包括自己
+ var allDependAssetInfos = new List(depends.Count);
+ for (int i = 0; i < depends.Count; i++)
+ {
+ string assetPath = depends[i].AssetPath;
+ if (assetPath != mainAssetPath)
+ allDependAssetInfos.Add(buildAssets[assetPath]);
+ }
+ buildAssets[mainAssetPath].SetAllDependAssetInfos(allDependAssetInfos);
+
+ EditorTools.DisplayProgressBar("依赖文件分析", ++progressValue, allCollectInfos.Count);
+ }
+ EditorTools.ClearProgressBar();
+
+ // 3. 移除零依赖的资源
+ var redundancy = CreateAssetRedundancy();
+ List undependentAssets = new List();
+ foreach (KeyValuePair pair in buildAssets)
+ {
+ var buildAssetInfo = pair.Value;
+ if (buildAssetInfo.IsCollectAsset)
+ continue;
+
+ if (buildAssetInfo.DependCount == 0)
+ {
+ undependentAssets.Add(buildAssetInfo);
+ continue;
+ }
+
+ // 冗余扩展
+ if(redundancy != null)
+ {
+ if(redundancy.Check(buildAssetInfo.AssetPath))
+ {
+ undependentAssets.Add(buildAssetInfo);
+ buildMapContext.RedundancyList.Add(buildAssetInfo.AssetPath);
+ continue;
+ }
+ }
+
+ // 冗余机制
+ if (buildParameters.Parameters.ApplyRedundancy)
+ {
+ if (AssetBundleCollectorSettingData.HasCollector(buildAssetInfo.AssetPath) == false)
+ {
+ undependentAssets.Add(buildAssetInfo);
+ buildMapContext.RedundancyList.Add(buildAssetInfo.AssetPath);
+ }
+ }
+ }
+ foreach (var assetInfo in undependentAssets)
+ {
+ buildAssets.Remove(assetInfo.AssetPath);
+ }
+
+ // 4. 设置资源包名
+ progressValue = 0;
+ foreach (KeyValuePair pair in buildAssets)
+ {
+ var assetInfo = pair.Value;
+ var bundleLabel = AssetBundleCollectorSettingData.GetBundleLabel(assetInfo.AssetPath);
+ if (assetInfo.IsRawAsset)
+ assetInfo.SetBundleLabelAndVariant(bundleLabel, ResourceSettingData.Setting.RawFileVariant);
+ else
+ assetInfo.SetBundleLabelAndVariant(bundleLabel, ResourceSettingData.Setting.AssetBundleFileVariant);
+ EditorTools.DisplayProgressBar("设置资源包名", ++progressValue, buildAssets.Count);
+ }
+ EditorTools.ClearProgressBar();
+
+ // 4. 构建资源包
+ var allAssets = buildAssets.Values.ToList();
+ if (allAssets.Count == 0)
+ throw new Exception("构建的资源列表不能为空");
+ UnityEngine.Debug.Log($"构建的资源列表里总共有{allAssets.Count}个资源");
+ foreach (var assetInfo in allAssets)
+ {
+ buildMapContext.PackAsset(assetInfo);
+ }
+ }
+
+ ///
+ /// 获取指定资源依赖的所有资源列表
+ /// 注意:返回列表里已经包括主资源自己
+ ///
+ private List GetAllDependencies(string mainAssetPath)
+ {
+ List result = new List();
+ string[] depends = AssetDatabase.GetDependencies(mainAssetPath, true);
+ foreach (string assetPath in depends)
+ {
+ if (AssetBundleCollectorSettingData.IsValidateAsset(assetPath))
+ {
+ BuildAssetInfo assetInfo = new BuildAssetInfo(assetPath);
+ result.Add(assetInfo);
+ }
+ }
+ return result;
+ }
+
+ ///
+ /// 检测构建结果
+ ///
+ private void CheckBuildMapContent(BuildMapContext buildMapContext)
+ {
+ foreach (var bundleInfo in buildMapContext.BundleInfos)
+ {
+ // 注意:原生文件资源包只能包含一个原生文件
+ bool isRawFile = bundleInfo.IsRawFile;
+ if (isRawFile)
+ {
+ if (bundleInfo.Assets.Count != 1)
+ throw new Exception("The bundle does not support multiple raw asset : {bundleInfo.BundleName}");
+ continue;
+ }
+
+ // 注意:原生文件不能被其它资源文件依赖
+ foreach (var assetInfo in bundleInfo.Assets)
+ {
+ if (assetInfo.AllDependAssetInfos != null)
+ {
+ foreach (var dependAssetInfo in assetInfo.AllDependAssetInfos)
+ {
+ if (dependAssetInfo.IsRawAsset)
+ throw new Exception($"{assetInfo.AssetPath} can not depend raw asset : {dependAssetInfo.AssetPath}");
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// 创建冗余类
+ ///
+ /// 如果没有定义类型,则返回NULL
+ private IAssetRedundancy CreateAssetRedundancy()
+ {
+ var types = AssemblyUtility.GetAssignableTypes(AssemblyUtility.UnityDefaultAssemblyEditorName, typeof(IAssetRedundancy));
+ if (types.Count == 0)
+ return null;
+ if (types.Count != 1)
+ throw new Exception($"Found more {nameof(IAssetRedundancy)} types. We only support one.");
+
+ UnityEngine.Debug.Log($"创建实例类 : {types[0].FullName}");
+ return (IAssetRedundancy)Activator.CreateInstance(types[0]);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskGetBuildMap.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskGetBuildMap.cs.meta
new file mode 100644
index 0000000..9ec942f
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskGetBuildMap.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: baee18c5c7b76584b90413bf20fdae9a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskPrepare.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskPrepare.cs
new file mode 100644
index 0000000..8bba665
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskPrepare.cs
@@ -0,0 +1,72 @@
+using System;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEditor;
+
+namespace YooAsset.Editor
+{
+ public class TaskPrepare : IBuildTask
+ {
+ void IBuildTask.Run(BuildContext context)
+ {
+ var buildParameters = context.GetContextObject();
+
+ // 检测构建平台是否合法
+ if (buildParameters.Parameters.BuildTarget == BuildTarget.NoTarget)
+ throw new Exception("请选择目标平台");
+
+ // 检测构建版本是否合法
+ if (buildParameters.Parameters.BuildVersion <= 0)
+ throw new Exception("请先设置版本号");
+
+ // 检测输出目录是否为空
+ if (string.IsNullOrEmpty(buildParameters.PipelineOutputDirectory))
+ throw new Exception("输出目录不能为空");
+
+ // 检测资源收集配置文件
+ if (AssetBundleCollectorSettingData.GetCollecterCount() == 0)
+ throw new Exception("配置的资源收集路径为空");
+
+ // 增量更新时候的必要检测
+ if (buildParameters.Parameters.IsForceRebuild == false)
+ {
+ // 检测历史版本是否存在
+ if (AssetBundleBuilderHelper.HasAnyPackageVersion(buildParameters.Parameters.BuildTarget, buildParameters.Parameters.OutputRoot) == false)
+ throw new Exception("没有发现任何历史版本,请尝试强制重建");
+
+ // 检测构建版本是否合法
+ int maxPackageVersion = AssetBundleBuilderHelper.GetMaxPackageVersion(buildParameters.Parameters.BuildTarget, buildParameters.Parameters.OutputRoot);
+ if (buildParameters.Parameters.BuildVersion <= maxPackageVersion)
+ throw new Exception("构建版本不能小于历史版本");
+
+ // 检测补丁包是否已经存在
+ string packageDirectory = buildParameters.GetPackageDirectory();
+ if (Directory.Exists(packageDirectory))
+ throw new Exception($"补丁包已经存在:{packageDirectory}");
+
+ // 检测内置资源标记是否一致
+ PatchManifest oldPatchManifest = AssetBundleBuilderHelper.LoadPatchManifestFile(buildParameters.PipelineOutputDirectory);
+ if (buildParameters.Parameters.BuildinTags != oldPatchManifest.BuildinTags)
+ throw new Exception($"增量更新时内置资源标记必须一致:{buildParameters.Parameters.BuildinTags} != {oldPatchManifest.BuildinTags}");
+ }
+
+ // 如果是强制重建
+ if (buildParameters.Parameters.IsForceRebuild)
+ {
+ // 删除平台总目录
+ string platformDirectory = $"{buildParameters.Parameters.OutputRoot}/{buildParameters.Parameters.BuildTarget}";
+ if (EditorTools.DeleteDirectory(platformDirectory))
+ {
+ UnityEngine.Debug.Log($"删除平台总目录:{platformDirectory}");
+ }
+ }
+
+ // 如果输出目录不存在
+ if (EditorTools.CreateDirectory(buildParameters.PipelineOutputDirectory))
+ {
+ UnityEngine.Debug.Log($"创建输出目录:{buildParameters.PipelineOutputDirectory}");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskPrepare.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskPrepare.cs.meta
new file mode 100644
index 0000000..ba61476
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/BuildTasks/TaskPrepare.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0dee755cccd608d48b6581ec33c99b39
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/ECompressOption.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/ECompressOption.cs
new file mode 100644
index 0000000..759106f
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/ECompressOption.cs
@@ -0,0 +1,13 @@
+
+namespace YooAsset.Editor
+{
+ ///
+ /// AssetBundle压缩选项
+ ///
+ public enum ECompressOption
+ {
+ Uncompressed = 0,
+ LZMA,
+ LZ4,
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/ECompressOption.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/ECompressOption.cs.meta
new file mode 100644
index 0000000..e4fb021
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/ECompressOption.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1e8ac0dfc77bd1b4697db63d52ab4c6e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetEncrypter.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetEncrypter.cs
new file mode 100644
index 0000000..64f8aac
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetEncrypter.cs
@@ -0,0 +1,18 @@
+
+namespace YooAsset.Editor
+{
+ public interface IAssetEncrypter
+ {
+ ///
+ /// 检测是否需要加密
+ ///
+ bool Check(string filePath);
+
+ ///
+ /// 加密方法
+ ///
+ /// 要加密的文件数据
+ /// 返回加密后的字节数据
+ byte[] Encrypt(byte[] fileData);
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetEncrypter.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetEncrypter.cs.meta
new file mode 100644
index 0000000..3991708
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetEncrypter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 04491137351983348959c00ec4ee226a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetRedundancy.cs b/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetRedundancy.cs
new file mode 100644
index 0000000..e49e3f8
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetRedundancy.cs
@@ -0,0 +1,11 @@
+
+namespace YooAsset.Editor
+{
+ public interface IAssetRedundancy
+ {
+ ///
+ /// 检测是否冗余
+ ///
+ bool Check(string filePath);
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetRedundancy.cs.meta b/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetRedundancy.cs.meta
new file mode 100644
index 0000000..9a9b8f5
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleBuilder/IAssetRedundancy.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d211989e17a617741a1963b8523fe9c7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector.meta b/Assets/YooAsset/Editor/AssetBundleCollector.meta
new file mode 100644
index 0000000..9afb783
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 0dbd5f557007d574e80c42d4a15421a9
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSetting.cs b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSetting.cs
new file mode 100644
index 0000000..f55d2dc
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSetting.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using YooAsset.Utility;
+
+namespace YooAsset.Editor
+{
+ public class AssetBundleCollectorSetting : ScriptableObject
+ {
+ [Serializable]
+ public class Collector
+ {
+ ///
+ /// 收集的文件夹路径
+ ///
+ public string CollectDirectory = string.Empty;
+
+ ///
+ /// 打包规则类名
+ ///
+ public string PackRuleName = string.Empty;
+
+ ///
+ /// 过滤规则类名
+ ///
+ public string FilterRuleName = string.Empty;
+
+ ///
+ /// 不写入资源路径到清单文件
+ /// 注意:对于不依赖于代码加载的收集资源,可以禁止写入资源路径信息到清单文件
+ ///
+ public bool DontWriteAssetPath = false;
+
+ ///
+ /// 资源标记
+ ///
+ public string AssetTags = string.Empty;
+
+ ///
+ /// 获取资源标记列表
+ ///
+ public List GetAssetTags()
+ {
+ return StringUtility.StringToStringList(AssetTags, ';');
+ }
+
+ public override string ToString()
+ {
+ return $"Directory : {CollectDirectory} | {PackRuleName} | {FilterRuleName} | {DontWriteAssetPath} | {AssetTags}";
+ }
+ }
+
+ ///
+ /// 是否收集全路径的着色器
+ ///
+ public bool IsCollectAllShaders = false;
+
+ ///
+ /// 收集的着色器Bundle名称
+ ///
+ public string ShadersBundleName = "myshaders";
+
+ ///
+ /// 收集列表
+ ///
+ public List Collectors = new List();
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSetting.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSetting.cs.meta
new file mode 100644
index 0000000..7bdd4a4
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSetting.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4cc54f0173b27fb40aee29e7eb3364da
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSettingData.cs b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSettingData.cs
new file mode 100644
index 0000000..cc4b03a
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSettingData.cs
@@ -0,0 +1,478 @@
+using System;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using UnityEditor;
+using YooAsset.Utility;
+
+namespace YooAsset.Editor
+{
+ public static class AssetBundleCollectorSettingData
+ {
+ private static readonly Dictionary _cachePackRuleTypes = new Dictionary();
+ private static readonly Dictionary _cachePackRuleInstance = new Dictionary();
+
+ private static readonly Dictionary _cacheFilterRuleTypes = new Dictionary();
+ private static readonly Dictionary _cacheFilterRuleInstance = new Dictionary();
+
+
+ private static AssetBundleCollectorSetting _setting = null;
+ public static AssetBundleCollectorSetting Setting
+ {
+ get
+ {
+ if (_setting == null)
+ LoadSettingData();
+ return _setting;
+ }
+ }
+
+ public static List GetPackRuleNames()
+ {
+ if (_setting == null)
+ LoadSettingData();
+
+ List names = new List();
+ foreach (var pair in _cachePackRuleTypes)
+ {
+ names.Add(pair.Key);
+ }
+ return names;
+ }
+ public static List GetFilterRuleNames()
+ {
+ if (_setting == null)
+ LoadSettingData();
+
+ List names = new List();
+ foreach (var pair in _cacheFilterRuleTypes)
+ {
+ names.Add(pair.Key);
+ }
+ return names;
+ }
+ public static bool HasPackRuleName(string ruleName)
+ {
+ foreach (var pair in _cachePackRuleTypes)
+ {
+ if (pair.Key == ruleName)
+ return true;
+ }
+ return false;
+ }
+ public static bool HasFilterRuleName(string ruleName)
+ {
+ foreach (var pair in _cacheFilterRuleTypes)
+ {
+ if (pair.Key == ruleName)
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// 加载配置文件
+ ///
+ private static void LoadSettingData()
+ {
+ // 加载配置文件
+ _setting = AssetDatabase.LoadAssetAtPath(EditorDefine.AssetBundleCollectorSettingFilePath);
+ if (_setting == null)
+ {
+ Debug.LogWarning($"Create new {nameof(AssetBundleCollectorSetting)}.asset : {EditorDefine.AssetBundleCollectorSettingFilePath}");
+ _setting = ScriptableObject.CreateInstance();
+ EditorTools.CreateFileDirectory(EditorDefine.AssetBundleCollectorSettingFilePath);
+ AssetDatabase.CreateAsset(Setting, EditorDefine.AssetBundleCollectorSettingFilePath);
+ AssetDatabase.SaveAssets();
+ AssetDatabase.Refresh();
+ }
+ else
+ {
+ Debug.Log($"Load {nameof(AssetBundleCollectorSetting)}.asset ok");
+ }
+
+ // IPackRule
+ {
+ // 清空缓存集合
+ _cachePackRuleTypes.Clear();
+ _cachePackRuleInstance.Clear();
+
+ // 获取所有类型
+ List types = new List(100)
+ {
+ typeof(PackExplicit),
+ typeof(PackDirectory),
+ typeof(PackRawFile),
+ };
+ var customTypes = AssemblyUtility.GetAssignableTypes(AssemblyUtility.UnityDefaultAssemblyEditorName, typeof(IPackRule));
+ types.AddRange(customTypes);
+ for (int i = 0; i < types.Count; i++)
+ {
+ Type type = types[i];
+ if (_cachePackRuleTypes.ContainsKey(type.Name) == false)
+ _cachePackRuleTypes.Add(type.Name, type);
+ }
+ }
+
+ // IFilterRule
+ {
+ // 清空缓存集合
+ _cacheFilterRuleTypes.Clear();
+ _cacheFilterRuleInstance.Clear();
+
+ // 获取所有类型
+ List types = new List(100)
+ {
+ typeof(CollectAll),
+ typeof(CollectScene),
+ typeof(CollectPrefab),
+ typeof(CollectSprite)
+ };
+ var customTypes = AssemblyUtility.GetAssignableTypes(AssemblyUtility.UnityDefaultAssemblyEditorName, typeof(IFilterRule));
+ types.AddRange(customTypes);
+ for (int i = 0; i < types.Count; i++)
+ {
+ Type type = types[i];
+ if (_cacheFilterRuleTypes.ContainsKey(type.Name) == false)
+ _cacheFilterRuleTypes.Add(type.Name, type);
+ }
+ }
+ }
+
+ ///
+ /// 存储文件
+ ///
+ public static void SaveFile()
+ {
+ if (Setting != null)
+ {
+ EditorUtility.SetDirty(Setting);
+ AssetDatabase.SaveAssets();
+ }
+ }
+
+ // 着色器相关
+ public static void ModifyShader(bool isCollectAllShaders, string shadersBundleName)
+ {
+ if (string.IsNullOrEmpty(shadersBundleName))
+ return;
+ Setting.IsCollectAllShaders = isCollectAllShaders;
+ Setting.ShadersBundleName = shadersBundleName;
+ SaveFile();
+ }
+
+ // 收集器相关
+ public static void ClearAllCollector()
+ {
+ Setting.Collectors.Clear();
+ SaveFile();
+ }
+ public static void AddCollector(string directory, string packRuleName, string filterRuleName, bool dontWriteAssetPath, string assetTags, bool saveFile = true)
+ {
+ // 末尾添加路径分隔符号
+ if (directory.EndsWith("/") == false)
+ directory = $"{directory}/";
+
+ // 检测收集器路径冲突
+ if (CheckConflict(directory))
+ return;
+
+ // 检测资源标签
+ if (dontWriteAssetPath && string.IsNullOrEmpty(assetTags) == false)
+ Debug.LogWarning($"Collector {directory} has asset tags : {assetTags}, It is not vliad when enable dontWriteAssetPath.");
+
+ AssetBundleCollectorSetting.Collector element = new AssetBundleCollectorSetting.Collector();
+ element.CollectDirectory = directory;
+ element.PackRuleName = packRuleName;
+ element.FilterRuleName = filterRuleName;
+ element.DontWriteAssetPath = dontWriteAssetPath;
+ element.AssetTags = assetTags;
+ Setting.Collectors.Add(element);
+
+ if (saveFile)
+ SaveFile();
+ }
+ public static void RemoveCollector(string directory)
+ {
+ for (int i = 0; i < Setting.Collectors.Count; i++)
+ {
+ if (Setting.Collectors[i].CollectDirectory == directory)
+ {
+ Setting.Collectors.RemoveAt(i);
+ break;
+ }
+ }
+ SaveFile();
+ }
+ public static void ModifyCollector(string directory, string packRuleName, string filterRuleName, bool dontWriteAssetPath, string assetTags)
+ {
+ // 检测资源标签
+ if (dontWriteAssetPath && string.IsNullOrEmpty(assetTags) == false)
+ Debug.LogWarning($"Collector '{directory}' has asset tags '{assetTags}', It is invalid when enable dontWriteAssetPath.");
+
+ for (int i = 0; i < Setting.Collectors.Count; i++)
+ {
+ var collector = Setting.Collectors[i];
+ if (collector.CollectDirectory == directory)
+ {
+ collector.PackRuleName = packRuleName;
+ collector.FilterRuleName = filterRuleName;
+ collector.DontWriteAssetPath = dontWriteAssetPath;
+ collector.AssetTags = assetTags;
+ break;
+ }
+ }
+ SaveFile();
+ }
+ public static bool IsContainsCollector(string directory)
+ {
+ for (int i = 0; i < Setting.Collectors.Count; i++)
+ {
+ if (Setting.Collectors[i].CollectDirectory == directory)
+ return true;
+ }
+ return false;
+ }
+ public static bool CheckConflict(string directory)
+ {
+ if (IsContainsCollector(directory))
+ {
+ Debug.LogError($"Asset collector already existed : {directory}");
+ return true;
+ }
+
+ for (int i = 0; i < Setting.Collectors.Count; i++)
+ {
+ var wrapper = Setting.Collectors[i];
+ string compareDirectory = wrapper.CollectDirectory;
+ if (directory.StartsWith(compareDirectory))
+ {
+ Debug.LogError($"New asset collector \"{directory}\" conflict with \"{compareDirectory}\"");
+ return true;
+ }
+ if (compareDirectory.StartsWith(directory))
+ {
+ Debug.LogError($"New asset collector {directory} conflict with {compareDirectory}");
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// 获取所有的DLC标记
+ ///
+ public static List GetAllAssetTags()
+ {
+ List result = new List();
+ for (int i = 0; i < Setting.Collectors.Count; i++)
+ {
+ var tags = Setting.Collectors[i].GetAssetTags();
+ foreach (var tag in tags)
+ {
+ if (result.Contains(tag) == false)
+ result.Add(tag);
+ }
+ }
+ return result;
+ }
+
+ ///
+ /// 获取收集器总数
+ ///
+ public static int GetCollecterCount()
+ {
+ return Setting.Collectors.Count;
+ }
+
+ ///
+ /// 获取所有的收集路径
+ ///
+ public static List GetAllCollectDirectory()
+ {
+ List result = new List();
+ for (int i = 0; i < Setting.Collectors.Count; i++)
+ {
+ AssetBundleCollectorSetting.Collector collector = Setting.Collectors[i];
+ result.Add(collector.CollectDirectory);
+ }
+ return result;
+ }
+
+ ///
+ /// 获取所有收集的资源
+ /// 注意:跳过了不写入资源路径的收集器
+ ///
+ public static List GetAllCollectAssets()
+ {
+ Dictionary result = new Dictionary(10000);
+ for (int i = 0; i < Setting.Collectors.Count; i++)
+ {
+ AssetBundleCollectorSetting.Collector collector = Setting.Collectors[i];
+
+ // 注意:跳过不需要写入资源路径的收集器
+ if (collector.DontWriteAssetPath)
+ continue;
+
+ bool isRawAsset = collector.PackRuleName == nameof(PackRawFile);
+ string[] findAssets = EditorTools.FindAssets(EAssetSearchType.All, collector.CollectDirectory);
+ foreach (string assetPath in findAssets)
+ {
+ if (IsValidateAsset(assetPath) == false)
+ continue;
+ if (IsCollectAsset(assetPath, collector.FilterRuleName) == false)
+ continue;
+
+ if (result.ContainsKey(assetPath) == false)
+ {
+ var assetCollectInfo = new AssetCollectInfo(assetPath, collector.GetAssetTags(), isRawAsset);
+ result.Add(assetPath, assetCollectInfo);
+ }
+ }
+ }
+ return result.Values.ToList();
+ }
+ private static bool IsCollectAsset(string assetPath, string filterRuleName)
+ {
+ if (Setting.IsCollectAllShaders)
+ {
+ Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
+ if (assetType == typeof(UnityEngine.Shader))
+ return true;
+ }
+
+ // 根据规则设置获取标签名称
+ IFilterRule filterRuleInstance = GetFilterRuleInstance(filterRuleName);
+ return filterRuleInstance.IsCollectAsset(assetPath);
+ }
+
+ ///
+ /// 检测资源路径是否被收集器覆盖
+ ///
+ public static bool HasCollector(string assetPath)
+ {
+ // 如果收集全路径着色器
+ if (Setting.IsCollectAllShaders)
+ {
+ System.Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
+ if (assetType == typeof(UnityEngine.Shader))
+ return true;
+ }
+
+ for (int i = 0; i < Setting.Collectors.Count; i++)
+ {
+ AssetBundleCollectorSetting.Collector collector = Setting.Collectors[i];
+ if (assetPath.StartsWith(collector.CollectDirectory))
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// 检测资源是否有效
+ ///
+ public static bool IsValidateAsset(string assetPath)
+ {
+ if (assetPath.StartsWith("Assets/") == false && assetPath.StartsWith("Packages/") == false)
+ return false;
+
+ if (AssetDatabase.IsValidFolder(assetPath))
+ return false;
+
+ // 注意:忽略编辑器下的类型资源
+ Type type = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
+ if (type == typeof(LightingDataAsset))
+ return false;
+
+ string ext = System.IO.Path.GetExtension(assetPath);
+ if (ext == "" || ext == ".dll" || ext == ".cs" || ext == ".js" || ext == ".boo" || ext == ".meta")
+ return false;
+
+ return true;
+ }
+
+ ///
+ /// 获取资源包名
+ ///
+ public static string GetBundleLabel(string assetPath)
+ {
+ // 如果收集全路径着色器
+ if (Setting.IsCollectAllShaders)
+ {
+ System.Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
+ if (assetType == typeof(UnityEngine.Shader))
+ {
+ return EditorTools.GetRegularPath(Setting.ShadersBundleName);
+ }
+ }
+
+ // 获取收集器
+ AssetBundleCollectorSetting.Collector findCollector = null;
+ for (int i = 0; i < Setting.Collectors.Count; i++)
+ {
+ AssetBundleCollectorSetting.Collector collector = Setting.Collectors[i];
+ if (assetPath.StartsWith(collector.CollectDirectory))
+ {
+ findCollector = collector;
+ break;
+ }
+ }
+
+ // 如果没有找到收集器
+ string bundleLabel;
+ if (findCollector == null)
+ {
+ IPackRule defaultInstance = new PackDirectory();
+ bundleLabel = defaultInstance.GetAssetBundleLabel(assetPath);
+ }
+ else
+ {
+ // 根据规则设置获取标签名称
+ IPackRule getInstance = GetPackRuleInstance(findCollector.PackRuleName);
+ bundleLabel = getInstance.GetAssetBundleLabel(assetPath);
+ }
+
+ // 返回包名
+ return EditorTools.GetRegularPath(bundleLabel);
+ }
+
+ private static IPackRule GetPackRuleInstance(string ruleName)
+ {
+ if (_cachePackRuleInstance.TryGetValue(ruleName, out IPackRule instance))
+ return instance;
+
+ // 如果不存在创建类的实例
+ if (_cachePackRuleTypes.TryGetValue(ruleName, out Type type))
+ {
+ instance = (IPackRule)Activator.CreateInstance(type);
+ _cachePackRuleInstance.Add(ruleName, instance);
+ return instance;
+ }
+ else
+ {
+ throw new Exception($"{nameof(IPackRule)}类型无效:{ruleName}");
+ }
+ }
+ private static IFilterRule GetFilterRuleInstance(string ruleName)
+ {
+ if (_cacheFilterRuleInstance.TryGetValue(ruleName, out IFilterRule instance))
+ return instance;
+
+ // 如果不存在创建类的实例
+ if (_cacheFilterRuleTypes.TryGetValue(ruleName, out Type type))
+ {
+ instance = (IFilterRule)Activator.CreateInstance(type);
+ _cacheFilterRuleInstance.Add(ruleName, instance);
+ return instance;
+ }
+ else
+ {
+ throw new Exception($"{nameof(IFilterRule)}类型无效:{ruleName}");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSettingData.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSettingData.cs.meta
new file mode 100644
index 0000000..2bd8874
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorSettingData.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5d4d2a4e23c5e0646a07add04fd8e18a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorWindow.cs b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorWindow.cs
new file mode 100644
index 0000000..5d80dd4
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorWindow.cs
@@ -0,0 +1,243 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+
+namespace YooAsset.Editor
+{
+ public class AssetBundleCollectorWindow : EditorWindow
+ {
+ static AssetBundleCollectorWindow _thisInstance;
+
+ [MenuItem("YooAsset/AssetBundle Collector", false, 101)]
+ static void ShowWindow()
+ {
+ if (_thisInstance == null)
+ {
+ _thisInstance = EditorWindow.GetWindow(typeof(AssetBundleCollectorWindow), false, "资源包收集工具", true) as AssetBundleCollectorWindow;
+ _thisInstance.minSize = new Vector2(800, 600);
+ }
+ _thisInstance.Show();
+ }
+
+ ///
+ /// 上次打开的文件夹路径
+ ///
+ private string _lastOpenFolderPath = "Assets/";
+
+ // GUI相关
+ private const float GuiDirecotryMinSize = 300f;
+ private const float GuiDirecotryMaxSize = 800f;
+ private const float GuiPackRuleSize = 130f;
+ private const float GuiFilterRuleSize = 130f;
+ private const float GuiDontWriteAssetPathSize = 130f;
+ private const float GuiAssetTagsMinSize = 100f;
+ private const float GuiAssetTagsMaxSize = 300f;
+ private const float GuiBtnSize = 40f;
+ private Vector2 _scrollPos = Vector2.zero;
+
+ // 初始化相关
+ private string[] _packRuleArray = null;
+ private string[] _filterRuleArray = null;
+ private bool _isInit = false;
+
+
+ private void Init()
+ {
+ List packRuleNames = AssetBundleCollectorSettingData.GetPackRuleNames();
+ _packRuleArray = packRuleNames.ToArray();
+
+ List filterRuleNames = AssetBundleCollectorSettingData.GetFilterRuleNames();
+ _filterRuleArray = filterRuleNames.ToArray();
+ }
+ private int PackRuleNameToIndex(string name)
+ {
+ for (int i = 0; i < _packRuleArray.Length; i++)
+ {
+ if (_packRuleArray[i] == name)
+ return i;
+ }
+ return 0;
+ }
+ private string IndexToPackRuleName(int index)
+ {
+ for (int i = 0; i < _packRuleArray.Length; i++)
+ {
+ if (i == index)
+ return _packRuleArray[i];
+ }
+ return string.Empty;
+ }
+ private int FilterRuleNameToIndex(string name)
+ {
+ for (int i = 0; i < _filterRuleArray.Length; i++)
+ {
+ if (_filterRuleArray[i] == name)
+ return i;
+ }
+ return 0;
+ }
+ private string IndexToFilterRuleName(int index)
+ {
+ for (int i = 0; i < _filterRuleArray.Length; i++)
+ {
+ if (i == index)
+ return _filterRuleArray[i];
+ }
+ return string.Empty;
+ }
+
+ private void OnGUI()
+ {
+ if (_isInit == false)
+ {
+ _isInit = true;
+ Init();
+ }
+
+ OnDrawShader();
+ OnDrawHeadBar();
+ OnDrawCollector();
+ }
+ private void OnDrawShader()
+ {
+ bool isCollectAllShader = AssetBundleCollectorSettingData.Setting.IsCollectAllShaders;
+ string shadersBundleName = AssetBundleCollectorSettingData.Setting.ShadersBundleName;
+
+ EditorGUILayout.Space();
+
+ bool newToggleValue = EditorGUILayout.Toggle("收集所有着色器", isCollectAllShader);
+ if (newToggleValue != isCollectAllShader)
+ {
+ isCollectAllShader = newToggleValue;
+ AssetBundleCollectorSettingData.ModifyShader(isCollectAllShader, shadersBundleName);
+ }
+
+ if (isCollectAllShader)
+ {
+ string newTextValue = EditorGUILayout.TextField("AssetBundle名称", shadersBundleName, GUILayout.MaxWidth(300));
+ if (newTextValue != shadersBundleName)
+ {
+ shadersBundleName = newTextValue;
+ AssetBundleCollectorSettingData.ModifyShader(isCollectAllShader, shadersBundleName);
+ }
+ }
+ }
+ private void OnDrawHeadBar()
+ {
+ EditorGUILayout.Space();
+ EditorGUILayout.Space();
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField("Directory", GUILayout.MinWidth(GuiDirecotryMinSize), GUILayout.MaxWidth(GuiDirecotryMaxSize));
+ EditorGUILayout.LabelField("PackRule", GUILayout.MinWidth(GuiPackRuleSize), GUILayout.MaxWidth(GuiPackRuleSize));
+ EditorGUILayout.LabelField("FilterRule", GUILayout.MinWidth(GuiFilterRuleSize), GUILayout.MaxWidth(GuiFilterRuleSize));
+ EditorGUILayout.LabelField("DontWriteAssetPath", GUILayout.MinWidth(GuiDontWriteAssetPathSize), GUILayout.MaxWidth(GuiDontWriteAssetPathSize));
+ EditorGUILayout.LabelField("AssetTags", GUILayout.MinWidth(GuiAssetTagsMinSize), GUILayout.MaxWidth(GuiAssetTagsMaxSize));
+ EditorGUILayout.LabelField("", GUILayout.MinWidth(GuiBtnSize), GUILayout.MaxWidth(GuiBtnSize));
+ EditorGUILayout.EndHorizontal();
+ }
+ private void OnDrawCollector()
+ {
+ // 列表显示
+ EditorGUILayout.Space();
+ _scrollPos = EditorGUILayout.BeginScrollView(_scrollPos);
+ for (int i = 0; i < AssetBundleCollectorSettingData.Setting.Collectors.Count; i++)
+ {
+ var collector = AssetBundleCollectorSettingData.Setting.Collectors[i];
+ string directory = collector.CollectDirectory;
+ string packRuleName = collector.PackRuleName;
+ string filterRuleName = collector.FilterRuleName;
+ bool dontWriteAssetPath = collector.DontWriteAssetPath;
+ string assetTags = collector.AssetTags;
+
+ EditorGUILayout.BeginHorizontal();
+ {
+ // Directory
+ EditorGUILayout.LabelField(directory, GUILayout.MinWidth(GuiDirecotryMinSize), GUILayout.MaxWidth(GuiDirecotryMaxSize));
+
+ // IPackRule
+ {
+ int index = PackRuleNameToIndex(packRuleName);
+ int newIndex = EditorGUILayout.Popup(index, _packRuleArray, GUILayout.MinWidth(GuiPackRuleSize), GUILayout.MaxWidth(GuiPackRuleSize));
+ if (newIndex != index)
+ {
+ packRuleName = IndexToPackRuleName(newIndex);
+ AssetBundleCollectorSettingData.ModifyCollector(directory, packRuleName, filterRuleName, dontWriteAssetPath, assetTags);
+ }
+ }
+
+ // IFilterRule
+ {
+ int index = FilterRuleNameToIndex(filterRuleName);
+ int newIndex = EditorGUILayout.Popup(index, _filterRuleArray, GUILayout.MinWidth(GuiFilterRuleSize), GUILayout.MaxWidth(GuiFilterRuleSize));
+ if (newIndex != index)
+ {
+ filterRuleName = IndexToFilterRuleName(newIndex);
+ AssetBundleCollectorSettingData.ModifyCollector(directory, packRuleName, filterRuleName, dontWriteAssetPath, assetTags);
+ }
+ }
+
+ // DontWriteAssetPath
+ {
+ bool newToggleValue = EditorGUILayout.Toggle(dontWriteAssetPath, GUILayout.MinWidth(GuiDontWriteAssetPathSize), GUILayout.MaxWidth(GuiDontWriteAssetPathSize));
+ if (newToggleValue != dontWriteAssetPath)
+ {
+ dontWriteAssetPath = newToggleValue;
+ AssetBundleCollectorSettingData.ModifyCollector(directory, packRuleName, filterRuleName, dontWriteAssetPath, assetTags);
+ }
+ }
+
+ // AssetTags
+ {
+ if (collector.DontWriteAssetPath)
+ {
+ EditorGUILayout.LabelField(assetTags, GUILayout.MinWidth(GuiAssetTagsMinSize), GUILayout.MaxWidth(GuiAssetTagsMaxSize));
+ }
+ else
+ {
+ string newTextValue = EditorGUILayout.TextField(assetTags, GUILayout.MinWidth(GuiAssetTagsMinSize), GUILayout.MaxWidth(GuiAssetTagsMaxSize));
+ if (newTextValue != assetTags)
+ {
+ assetTags = newTextValue;
+ AssetBundleCollectorSettingData.ModifyCollector(directory, packRuleName, filterRuleName, dontWriteAssetPath, assetTags);
+ }
+ }
+ }
+
+ if (GUILayout.Button("-", GUILayout.MinWidth(GuiBtnSize), GUILayout.MaxWidth(GuiBtnSize)))
+ {
+ AssetBundleCollectorSettingData.RemoveCollector(directory);
+ break;
+ }
+ }
+ EditorGUILayout.EndHorizontal();
+ }
+ EditorGUILayout.EndScrollView();
+
+ // 添加按钮
+ if (GUILayout.Button("+"))
+ {
+ string resultPath = EditorTools.OpenFolderPanel("Select Folder", _lastOpenFolderPath);
+ if (resultPath != null)
+ {
+ _lastOpenFolderPath = EditorTools.AbsolutePathToAssetPath(resultPath);
+ string defaultPackRuleName = nameof(PackExplicit);
+ string defaultFilterRuleName = nameof(CollectAll);
+ bool defaultDontWriteAssetPathValue = false;
+ string defaultAssetTag = string.Empty;
+ AssetBundleCollectorSettingData.AddCollector(_lastOpenFolderPath, defaultPackRuleName, defaultFilterRuleName, defaultDontWriteAssetPathValue, defaultAssetTag);
+ }
+ }
+
+ // 导入配置按钮
+ if (GUILayout.Button("Import Config"))
+ {
+ string resultPath = EditorTools.OpenFilePath("Select File", "Assets/", "xml");
+ if (resultPath != null)
+ {
+ CollectorConfigImporter.ImportXmlConfig(resultPath);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorWindow.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorWindow.cs.meta
new file mode 100644
index 0000000..78d3ed7
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/AssetBundleCollectorWindow.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cfca8094e2a905a4da05586b2d7fa919
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/AssetCollectInfo.cs b/Assets/YooAsset/Editor/AssetBundleCollector/AssetCollectInfo.cs
new file mode 100644
index 0000000..6758b1d
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/AssetCollectInfo.cs
@@ -0,0 +1,30 @@
+using System.Collections;
+using System.Collections.Generic;
+
+namespace YooAsset.Editor
+{
+ public class AssetCollectInfo
+ {
+ ///
+ /// 资源路径
+ ///
+ public string AssetPath { private set; get; }
+
+ ///
+ /// 资源标记列表
+ ///
+ public List AssetTags { private set; get; }
+
+ ///
+ /// 是否为原生资源
+ ///
+ public bool IsRawAsset { private set; get; }
+
+ public AssetCollectInfo(string assetPath, List assetTags, bool isRawAsset)
+ {
+ AssetPath = assetPath;
+ AssetTags = assetTags;
+ IsRawAsset = isRawAsset;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/AssetCollectInfo.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/AssetCollectInfo.cs.meta
new file mode 100644
index 0000000..4e3c4c6
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/AssetCollectInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b44b2cc585bc59c4d879d1edac67f7c4
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/CollectorConfigImporter.cs b/Assets/YooAsset/Editor/AssetBundleCollector/CollectorConfigImporter.cs
new file mode 100644
index 0000000..b26cd66
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/CollectorConfigImporter.cs
@@ -0,0 +1,100 @@
+using System;
+using System.IO;
+using System.Xml;
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+using YooAsset.Utility;
+
+namespace YooAsset.Editor
+{
+ public static class CollectorConfigImporter
+ {
+ private class CollectWrapper
+ {
+ public string CollectDirectory;
+ public string PackRuleName;
+ public string FilterRuleName;
+ public bool DontWriteAssetPath;
+ public string AssetTags;
+
+ public CollectWrapper(string directory, string packRuleName, string filterRuleName, bool dontWriteAssetPath, string assetTags)
+ {
+ CollectDirectory = directory;
+ PackRuleName = packRuleName;
+ FilterRuleName = filterRuleName;
+ DontWriteAssetPath = dontWriteAssetPath;
+ AssetTags = assetTags;
+ }
+ }
+
+ public const string XmlCollector = "Collector";
+ public const string XmlDirectory = "Directory";
+ public const string XmlPackRule = "PackRule";
+ public const string XmlFilterRule = "FilterRule";
+ public const string XmlDontWriteAssetPath = "DontWriteAssetPath";
+ public const string XmlAssetTags = "AssetTags";
+
+ public static void ImportXmlConfig(string filePath)
+ {
+ if (File.Exists(filePath) == false)
+ throw new FileNotFoundException(filePath);
+
+ if (Path.GetExtension(filePath) != ".xml")
+ throw new Exception($"Only support xml : {filePath}");
+
+ List wrappers = new List();
+
+ // 加载文件
+ XmlDocument xml = new XmlDocument();
+ xml.Load(filePath);
+
+ // 解析文件
+ XmlElement root = xml.DocumentElement;
+ XmlNodeList nodeList = root.GetElementsByTagName(XmlCollector);
+ if (nodeList.Count == 0)
+ throw new Exception($"Not found any {XmlCollector}");
+ foreach (XmlNode node in nodeList)
+ {
+ XmlElement collect = node as XmlElement;
+ string directory = collect.GetAttribute(XmlDirectory);
+ string packRuleName = collect.GetAttribute(XmlPackRule);
+ string filterRuleName = collect.GetAttribute(XmlFilterRule);
+ string dontWriteAssetPath = collect.GetAttribute(XmlDontWriteAssetPath);
+ string assetTags = collect.GetAttribute(XmlAssetTags);
+
+ if (Directory.Exists(directory) == false)
+ throw new Exception($"Not found directory : {directory}");
+
+ if (collect.HasAttribute(XmlPackRule) == false)
+ throw new Exception($"Not found attribute {XmlPackRule} in collector : {directory}");
+ if (collect.HasAttribute(XmlFilterRule) == false)
+ throw new Exception($"Not found attribute {XmlFilterRule} in collector : {directory}");
+ if (collect.HasAttribute(XmlDontWriteAssetPath) == false)
+ throw new Exception($"Not found attribute {XmlDontWriteAssetPath} in collector : {directory}");
+ if (collect.HasAttribute(XmlAssetTags) == false)
+ throw new Exception($"Not found attribute {XmlAssetTags} in collector : {directory}");
+
+ if (AssetBundleCollectorSettingData.HasPackRuleName(packRuleName) == false)
+ throw new Exception($"Invalid {nameof(IPackRule)} class type : {packRuleName}");
+ if (AssetBundleCollectorSettingData.HasFilterRuleName(filterRuleName) == false)
+ throw new Exception($"Invalid {nameof(IFilterRule)} class type : {filterRuleName}");
+
+ bool dontWriteAssetPathValue = StringUtility.StringToBool(dontWriteAssetPath);
+ CollectWrapper collectWrapper = new CollectWrapper(directory, packRuleName, filterRuleName, dontWriteAssetPathValue, assetTags);
+ wrappers.Add(collectWrapper);
+ }
+
+ // 导入配置数据
+ AssetBundleCollectorSettingData.ClearAllCollector();
+ foreach (var wrapper in wrappers)
+ {
+ AssetBundleCollectorSettingData.AddCollector(wrapper.CollectDirectory, wrapper.PackRuleName, wrapper.FilterRuleName, wrapper.DontWriteAssetPath, wrapper.AssetTags, false);
+ }
+
+ // 保存配置数据
+ AssetBundleCollectorSettingData.SaveFile();
+ Debug.Log($"导入配置完毕,一共导入{wrappers.Count}个收集器。");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/CollectorConfigImporter.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/CollectorConfigImporter.cs.meta
new file mode 100644
index 0000000..241f964
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/CollectorConfigImporter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c35b6554a4bedd049a8868d9249846d7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/DefaultFilterRule.cs b/Assets/YooAsset/Editor/AssetBundleCollector/DefaultFilterRule.cs
new file mode 100644
index 0000000..95adedb
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/DefaultFilterRule.cs
@@ -0,0 +1,53 @@
+using UnityEngine;
+using UnityEditor;
+using System.IO;
+
+namespace YooAsset.Editor
+{
+ ///
+ /// 收集所有资源
+ ///
+ public class CollectAll : IFilterRule
+ {
+ public bool IsCollectAsset(string assetPath)
+ {
+ return true;
+ }
+ }
+
+ ///
+ /// 只收集场景
+ ///
+ public class CollectScene : IFilterRule
+ {
+ public bool IsCollectAsset(string assetPath)
+ {
+ return Path.GetExtension(assetPath) == ".unity";
+ }
+ }
+
+ ///
+ /// 只收集预制体
+ ///
+ public class CollectPrefab : IFilterRule
+ {
+ public bool IsCollectAsset(string assetPath)
+ {
+ return Path.GetExtension(assetPath) == ".prefab";
+ }
+ }
+
+ ///
+ /// 只收集精灵类型的资源
+ ///
+ public class CollectSprite : IFilterRule
+ {
+ public bool IsCollectAsset(string assetPath)
+ {
+ if (AssetDatabase.GetMainAssetTypeAtPath(assetPath) == typeof(Sprite))
+ return true;
+ else
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/DefaultFilterRule.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/DefaultFilterRule.cs.meta
new file mode 100644
index 0000000..df88655
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/DefaultFilterRule.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a74c81d149472fb4c960a1db1fd8accc
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/DefaultPackRule.cs b/Assets/YooAsset/Editor/AssetBundleCollector/DefaultPackRule.cs
new file mode 100644
index 0000000..9823a8a
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/DefaultPackRule.cs
@@ -0,0 +1,56 @@
+using System;
+using System.IO;
+using UnityEditor;
+using YooAsset.Utility;
+
+namespace YooAsset.Editor
+{
+ ///
+ /// 以文件路径作为AssetBundle标签名
+ ///
+ public class PackExplicit : IPackRule
+ {
+ string IPackRule.GetAssetBundleLabel(string assetPath)
+ {
+ return StringUtility.RemoveExtension(assetPath); //"Assets/Config/test.txt" --> "Assets/Config/test"
+ }
+ }
+
+ ///
+ /// 以父文件夹路径作为AssetBundle标签名
+ /// 注意:该文件夹下所有资源被打到一个AssetBundle文件里
+ ///
+ public class PackDirectory : IPackRule
+ {
+ string IPackRule.GetAssetBundleLabel(string assetPath)
+ {
+ return Path.GetDirectoryName(assetPath); //"Assets/Config/test.txt" --> "Assets/Config"
+ }
+ }
+
+ ///
+ /// 原生文件打包模式
+ /// 注意:原生文件打包支持:图片,音频,视频,文本
+ ///
+ public class PackRawFile : IPackRule
+ {
+ string IPackRule.GetAssetBundleLabel(string assetPath)
+ {
+ string extension = StringUtility.RemoveFirstChar(Path.GetExtension(assetPath));
+ if (extension == EAssetFileExtension.unity.ToString() || extension == EAssetFileExtension.prefab.ToString() ||
+ extension == EAssetFileExtension.mat.ToString() || extension == EAssetFileExtension.controller.ToString() ||
+ extension == EAssetFileExtension.fbx.ToString() || extension == EAssetFileExtension.anim.ToString() ||
+ extension == EAssetFileExtension.shader.ToString())
+ {
+ throw new Exception($"{nameof(PackRawFile)} is not support file estension : {extension}");
+ }
+
+ // 注意:原生文件只支持无依赖关系的资源
+ string[] depends = AssetDatabase.GetDependencies(assetPath, true);
+ if (depends.Length != 1)
+ throw new Exception($"{nameof(PackRawFile)} is not support estension : {extension}");
+
+ return StringUtility.RemoveExtension(assetPath);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/DefaultPackRule.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/DefaultPackRule.cs.meta
new file mode 100644
index 0000000..13b1c8c
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/DefaultPackRule.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4a924dc0a22fc104781bf9aaadd60c29
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/IFilterRule.cs b/Assets/YooAsset/Editor/AssetBundleCollector/IFilterRule.cs
new file mode 100644
index 0000000..80b5972
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/IFilterRule.cs
@@ -0,0 +1,16 @@
+
+namespace YooAsset.Editor
+{
+ ///
+ /// 资源过滤规则接口
+ ///
+ public interface IFilterRule
+ {
+ ///
+ /// 是否为收集资源
+ ///
+ /// 资源路径
+ /// 如果收集该资源返回TRUE
+ bool IsCollectAsset(string assetPath);
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/IFilterRule.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/IFilterRule.cs.meta
new file mode 100644
index 0000000..470ca48
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/IFilterRule.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ffe1385deb0bd9844a514f1c2fd65e62
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/IPackRule.cs b/Assets/YooAsset/Editor/AssetBundleCollector/IPackRule.cs
new file mode 100644
index 0000000..e48376e
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/IPackRule.cs
@@ -0,0 +1,14 @@
+
+namespace YooAsset.Editor
+{
+ ///
+ /// 资源打包规则接口
+ ///
+ public interface IPackRule
+ {
+ ///
+ /// 获取资源的打包标签
+ ///
+ string GetAssetBundleLabel(string assetPath);
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/AssetBundleCollector/IPackRule.cs.meta b/Assets/YooAsset/Editor/AssetBundleCollector/IPackRule.cs.meta
new file mode 100644
index 0000000..8094b86
--- /dev/null
+++ b/Assets/YooAsset/Editor/AssetBundleCollector/IPackRule.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 577f7c1abcdf0a140958f1d1d44d6f40
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/EditorDefine.cs b/Assets/YooAsset/Editor/EditorDefine.cs
new file mode 100644
index 0000000..69443ff
--- /dev/null
+++ b/Assets/YooAsset/Editor/EditorDefine.cs
@@ -0,0 +1,53 @@
+
+namespace YooAsset.Editor
+{
+ public class EditorDefine
+ {
+ ///
+ /// 资源包收集工具的配置文件存储路径
+ ///
+ public const string AssetBundleCollectorSettingFilePath = "Assets/YooAssetSetting/AssetBundleCollectorSetting.asset";
+ }
+
+ ///
+ /// 资源搜索类型
+ ///
+ public enum EAssetSearchType
+ {
+ All,
+ RuntimeAnimatorController,
+ AnimationClip,
+ AudioClip,
+ AudioMixer,
+ Font,
+ Material,
+ Mesh,
+ Model,
+ PhysicMaterial,
+ Prefab,
+ Scene,
+ Script,
+ Shader,
+ Sprite,
+ Texture,
+ VideoClip,
+ }
+
+ ///
+ /// 资源文件格式
+ ///
+ public enum EAssetFileExtension
+ {
+ prefab, //预制体
+ unity, //场景
+ fbx, //模型
+ anim, //动画
+ controller, //控制器
+ png, //图片
+ jpg, //图片
+ mat, //材质球
+ shader, //着色器
+ ttf, //字体
+ cs, //脚本
+ }
+}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/EditorDefine.cs.meta b/Assets/YooAsset/Editor/EditorDefine.cs.meta
new file mode 100644
index 0000000..e79619a
--- /dev/null
+++ b/Assets/YooAsset/Editor/EditorDefine.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e33ff61240a851e4fbaea71d01a460cd
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Editor/EditorTools.cs b/Assets/YooAsset/Editor/EditorTools.cs
new file mode 100644
index 0000000..1be6273
--- /dev/null
+++ b/Assets/YooAsset/Editor/EditorTools.cs
@@ -0,0 +1,878 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Linq;
+using System.IO;
+using System.Text;
+using System.Text.RegularExpressions;
+using UnityEngine;
+using UnityEditor;
+using UnityEditor.Animations;
+using YooAsset.Utility;
+
+namespace YooAsset.Editor
+{
+ ///
+ /// 编辑器工具类
+ ///
+ public static class EditorTools
+ {
+ #region NGUI
+ ///
+ /// Draw a distinctly different looking header label
+ ///
+ public static bool DrawHeader(string text)
+ {
+ return DrawHeader(text, text, false, true);
+ }
+ public static bool DrawHeader(string text, string key, bool forceOn, bool minimalistic)
+ {
+ bool state = EditorPrefs.GetBool(key, true);
+
+ if (!minimalistic) GUILayout.Space(3f);
+ if (!forceOn && !state) GUI.backgroundColor = new Color(0.8f, 0.8f, 0.8f);
+ GUILayout.BeginHorizontal();
+ GUI.changed = false;
+
+ if (minimalistic)
+ {
+ if (state) text = "\u25BC" + (char)0x200a + text;
+ else text = "\u25BA" + (char)0x200a + text;
+
+ GUILayout.BeginHorizontal();
+ GUI.contentColor = EditorGUIUtility.isProSkin ? new Color(1f, 1f, 1f, 0.7f) : new Color(0f, 0f, 0f, 0.7f);
+ if (!GUILayout.Toggle(true, text, "PreToolbar2", GUILayout.MinWidth(20f))) state = !state;
+ GUI.contentColor = Color.white;
+ GUILayout.EndHorizontal();
+ }
+ else
+ {
+ text = "" + text + "";
+ if (state) text = "\u25BC " + text;
+ else text = "\u25BA " + text;
+ if (!GUILayout.Toggle(true, text, "dragtab", GUILayout.MinWidth(20f))) state = !state;
+ }
+
+ if (GUI.changed) EditorPrefs.SetBool(key, state);
+
+ if (!minimalistic) GUILayout.Space(2f);
+ GUILayout.EndHorizontal();
+ GUI.backgroundColor = Color.white;
+ if (!forceOn && !state) GUILayout.Space(3f);
+ return state;
+ }
+ #endregion
+
+ #region Assembly
+ ///
+ /// 调用私有的静态方法
+ ///
+ /// 类的类型
+ /// 类里要调用的方法名
+ /// 调用方法传入的参数
+ public static object InvokeNonPublicStaticMethod(System.Type type, string method, params object[] parameters)
+ {
+ var methodInfo = type.GetMethod(method, BindingFlags.NonPublic | BindingFlags.Static);
+ if (methodInfo == null)
+ {
+ UnityEngine.Debug.LogError($"{type.FullName} not found method : {method}");
+ return null;
+ }
+ return methodInfo.Invoke(null, parameters);
+ }
+
+ ///
+ /// 调用公开的静态方法
+ ///
+ /// 类的类型
+ /// 类里要调用的方法名
+ /// 调用方法传入的参数
+ public static object InvokePublicStaticMethod(System.Type type, string method, params object[] parameters)
+ {
+ var methodInfo = type.GetMethod(method, BindingFlags.Public | BindingFlags.Static);
+ if (methodInfo == null)
+ {
+ UnityEngine.Debug.LogError($"{type.FullName} not found method : {method}");
+ return null;
+ }
+ return methodInfo.Invoke(null, parameters);
+ }
+ #endregion
+
+ #region EditorUtility
+ ///
+ /// 搜集资源
+ ///
+ /// 搜集的资源类型
+ /// 指定搜索的文件夹列表
+ /// 返回搜集到的资源路径列表
+ public static string[] FindAssets(EAssetSearchType searchType, string[] searchInFolders)
+ {
+ // 注意:AssetDatabase.FindAssets()不支持末尾带分隔符的文件夹路径
+ for (int i = 0; i < searchInFolders.Length; i++)
+ {
+ string folderPath = searchInFolders[i];
+ searchInFolders[i] = folderPath.TrimEnd('/');
+ }
+
+ // 注意:获取指定目录下的所有资源对象(包括子文件夹)
+ string[] guids;
+ if (searchType == EAssetSearchType.All)
+ guids = AssetDatabase.FindAssets(string.Empty, searchInFolders);
+ else
+ guids = AssetDatabase.FindAssets($"t:{searchType}", searchInFolders);
+
+ // 注意:AssetDatabase.FindAssets()可能会获取到重复的资源
+ List result = new List();
+ for (int i = 0; i < guids.Length; i++)
+ {
+ string guid = guids[i];
+ string assetPath = AssetDatabase.GUIDToAssetPath(guid);
+ if (result.Contains(assetPath) == false)
+ {
+ result.Add(assetPath);
+ }
+ }
+
+ // 返回结果
+ return result.ToArray();
+ }
+
+ ///
+ /// 搜集资源
+ ///
+ /// 搜集的资源类型
+ /// 指定搜索的文件夹
+ /// 返回搜集到的资源路径列表
+ public static string[] FindAssets(EAssetSearchType searchType, string searchInFolder)
+ {
+ return FindAssets(searchType, new string[] { searchInFolder });
+ }
+
+ ///
+ /// 打开搜索面板
+ ///
+ /// 标题名称
+ /// 默认搜索路径
+ /// 返回选择的文件夹绝对路径,如果无效返回NULL
+ public static string OpenFolderPanel(string title, string defaultPath)
+ {
+ string openPath = EditorUtility.OpenFolderPanel(title, defaultPath, string.Empty);
+ if (string.IsNullOrEmpty(openPath))
+ return null;
+
+ if (openPath.Contains("/Assets") == false)
+ {
+ Debug.LogWarning("Please select unity assets folder.");
+ return null;
+ }
+ return openPath;
+ }
+
+ ///
+ /// 打开搜索面板
+ ///
+ /// 标题名称
+ /// 默认搜索路径
+ /// 返回选择的文件绝对路径,如果无效返回NULL
+ public static string OpenFilePath(string title, string defaultPath, string extension = "")
+ {
+ string openPath = EditorUtility.OpenFilePanel(title, defaultPath, extension);
+ if (string.IsNullOrEmpty(openPath))
+ return null;
+
+ if (openPath.Contains("/Assets") == false)
+ {
+ Debug.LogWarning("Please select unity assets file.");
+ return null;
+ }
+ return openPath;
+ }
+
+ ///
+ /// 显示进度框
+ ///
+ public static void DisplayProgressBar(string tips, int progressValue, int totalValue)
+ {
+ EditorUtility.DisplayProgressBar("进度", $"{tips} : {progressValue}/{totalValue}", (float)progressValue / totalValue);
+ }
+
+ ///
+ /// 隐藏进度框
+ ///
+ public static void ClearProgressBar()
+ {
+ EditorUtility.ClearProgressBar();
+ }
+ #endregion
+
+ #region 编辑器窗口
+ public static void FocusUnitySceneWindow()
+ {
+ EditorWindow.FocusWindowIfItsOpen();
+ }
+ public static void FocusUnityGameWindow()
+ {
+ System.Type T = Assembly.Load("UnityEditor").GetType("UnityEditor.GameView");
+ EditorWindow.GetWindow(T, false, "GameView", true);
+ }
+ public static void FocueUnityProjectWindow()
+ {
+ System.Type T = Assembly.Load("UnityEditor").GetType("UnityEditor.ProjectBrowser");
+ EditorWindow.GetWindow(T, false, "Project", true);
+ }
+ public static void FocusUnityHierarchyWindow()
+ {
+ System.Type T = Assembly.Load("UnityEditor").GetType("UnityEditor.SceneHierarchyWindow");
+ EditorWindow.GetWindow(T, false, "Hierarchy", true);
+ }
+ public static void FocusUnityInspectorWindow()
+ {
+ System.Type T = Assembly.Load("UnityEditor").GetType("UnityEditor.InspectorWindow");
+ EditorWindow.GetWindow(T, false, "Inspector", true);
+ }
+ public static void FocusUnityConsoleWindow()
+ {
+ System.Type T = Assembly.Load("UnityEditor").GetType("UnityEditor.ConsoleWindow");
+ EditorWindow.GetWindow(T, false, "Console", true);
+ }
+ #endregion
+
+ #region 引用关系
+ ///
+ /// 获取场景里的克隆预制体
+ ///
+ public static GameObject GetClonePrefabInScene(GameObject sourcePrefab)
+ {
+ GameObject[] findObjects = GameObject.FindObjectsOfType();
+ if (findObjects.Length == 0)
+ return null;
+
+ for (int i = 0; i < findObjects.Length; i++)
+ {
+ GameObject findObject = findObjects[i];
+
+#if UNITY_2017_4
+ // 判断对象是否为一个预制体的引用
+ if (PrefabUtility.GetPrefabType(findObject) == PrefabType.PrefabInstance)
+ {
+ // 判断是否为同一个预制体
+ Object source = PrefabUtility.GetPrefabParent(findObject);
+ if (source.GetInstanceID() == sourcePrefab.GetInstanceID())
+ return findObject;
+ }
+#else
+ // 判断对象是否为一个预制体的引用
+ if (PrefabUtility.GetPrefabInstanceStatus(findObject) == PrefabInstanceStatus.Connected)
+ {
+ // 判断是否为同一个预制体
+ Object source = PrefabUtility.GetCorrespondingObjectFromSource(findObject);
+ if (source.GetInstanceID() == sourcePrefab.GetInstanceID())
+ return findObject;
+ }
+#endif
+ }
+
+ return null; //没有找到合适的对象
+ }
+
+ ///
+ /// 查找场景里的引用对象
+ ///
+ public static void FindReferencesInScene(UnityEngine.Object to)
+ {
+ var referencedBy = new List