Initial commit
parent
1d54164bfb
commit
e66853fd46
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3ac1aba04265f9143957a5b474de973d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ac093515ae764b94aa07be91d4ba978b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 197364393c72852419af32ef010600bf
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class AssetBundleBrowserWindow
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d6938e4c868d3f34f97d3bb0dbd9d509
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1fdecc5500229d44887425ce619352fc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 构建参数
|
||||
/// </summary>
|
||||
public class BuildParameters
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否验证构建结果
|
||||
/// </summary>
|
||||
public bool IsVerifyBuildingResult = false;
|
||||
|
||||
/// <summary>
|
||||
/// 输出的根目录
|
||||
/// </summary>
|
||||
public string OutputRoot;
|
||||
|
||||
/// <summary>
|
||||
/// 构建的平台
|
||||
/// </summary>
|
||||
public BuildTarget BuildTarget;
|
||||
|
||||
/// <summary>
|
||||
/// 构建的版本(资源版本号)
|
||||
/// </summary>
|
||||
public int BuildVersion;
|
||||
|
||||
/// <summary>
|
||||
/// 是否允许冗余机制
|
||||
/// 说明:冗余机制可以帮助我们减少包体数量
|
||||
/// </summary>
|
||||
public bool ApplyRedundancy = false;
|
||||
|
||||
/// <summary>
|
||||
/// 是否附加上文件扩展名
|
||||
/// </summary>
|
||||
public bool AppendFileExtension = false;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 压缩选项
|
||||
/// </summary>
|
||||
public ECompressOption CompressOption;
|
||||
|
||||
/// <summary>
|
||||
/// 是否强制重新构建整个项目,如果为FALSE则是增量打包
|
||||
/// </summary>
|
||||
public bool IsForceRebuild;
|
||||
|
||||
/// <summary>
|
||||
/// 内置资源的标记列表
|
||||
/// 注意:分号为分隔符
|
||||
/// </summary>
|
||||
public string BuildinTags;
|
||||
|
||||
#region 高级选项
|
||||
/// <summary>
|
||||
/// 文件名附加上哈希值
|
||||
/// </summary>
|
||||
public bool IsAppendHash = false;
|
||||
|
||||
/// <summary>
|
||||
/// 禁止写入类型树结构(可以降低包体和内存并提高加载效率)
|
||||
/// </summary>
|
||||
public bool IsDisableWriteTypeTree = false;
|
||||
|
||||
/// <summary>
|
||||
/// 忽略类型树变化
|
||||
/// </summary>
|
||||
public bool IsIgnoreTypeTreeChanges = true;
|
||||
|
||||
/// <summary>
|
||||
/// 禁用名称查找资源(可以降内存并提高加载效率)
|
||||
/// </summary>
|
||||
public bool IsDisableLoadAssetByFileName = false;
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取内置标记列表
|
||||
/// </summary>
|
||||
public List<string> GetBuildinTags()
|
||||
{
|
||||
return StringUtility.StringToStringList(BuildinTags, ';');
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建参数环境
|
||||
/// </summary>
|
||||
public class BuildParametersContext : IContextObject
|
||||
{
|
||||
/// <summary>
|
||||
/// 构建参数
|
||||
/// </summary>
|
||||
public BuildParameters Parameters { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 构建管线的输出目录
|
||||
/// </summary>
|
||||
public string PipelineOutputDirectory { private set; get; }
|
||||
|
||||
|
||||
public BuildParametersContext(BuildParameters parameters)
|
||||
{
|
||||
Parameters = parameters;
|
||||
PipelineOutputDirectory = AssetBundleBuilderHelper.MakePipelineOutputDirectory(parameters.OutputRoot, parameters.BuildTarget);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取本次构建的补丁目录
|
||||
/// </summary>
|
||||
public string GetPackageDirectory()
|
||||
{
|
||||
return $"{Parameters.OutputRoot}/{Parameters.BuildTarget}/{Parameters.BuildVersion}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取构建选项
|
||||
/// </summary>
|
||||
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();
|
||||
|
||||
/// <summary>
|
||||
/// 开始构建
|
||||
/// </summary>
|
||||
public bool Run(BuildParameters buildParameters)
|
||||
{
|
||||
// 清空旧数据
|
||||
_buildContext.ClearAllContext();
|
||||
|
||||
// 构建参数
|
||||
var buildParametersContext = new BuildParametersContext(buildParameters);
|
||||
_buildContext.SetContextObject(buildParametersContext);
|
||||
|
||||
// 执行构建流程
|
||||
List<IBuildTask> pipeline = new List<IBuildTask>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: de7563040250b4e4a835d1fc90238e38
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取默认的输出根路录
|
||||
/// </summary>
|
||||
public static string GetDefaultOutputRoot()
|
||||
{
|
||||
string projectPath = EditorTools.GetProjectPath();
|
||||
return $"{projectPath}/Bundles";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取构建管线的输出目录
|
||||
/// </summary>
|
||||
public static string MakePipelineOutputDirectory(string outputRoot, BuildTarget buildTarget)
|
||||
{
|
||||
return $"{outputRoot}/{buildTarget}/{ResourceSettingData.Setting.UnityManifestFileName}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 制作AssetBundle的完整名称
|
||||
/// 注意:名称为全部小写并且包含后缀名
|
||||
/// </summary>
|
||||
public static string MakeBundleName(string bundleLabel, string bundleVariant)
|
||||
{
|
||||
if (string.IsNullOrEmpty(bundleVariant))
|
||||
return bundleLabel.ToLower();
|
||||
else
|
||||
return $"{bundleLabel}.{bundleVariant}".ToLower();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 清空流文件夹
|
||||
/// </summary>
|
||||
public static void ClearStreamingAssetsFolder()
|
||||
{
|
||||
string streamingPath = Application.dataPath + "/StreamingAssets";
|
||||
EditorTools.ClearFolder(streamingPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除流文件夹内无关的文件
|
||||
/// 删除.manifest文件和.meta文件
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有补丁包版本列表
|
||||
/// 注意:列表会按照版本号从小到大排序
|
||||
/// </summary>
|
||||
private static List<int> GetPackageVersionList(BuildTarget buildTarget, string outputRoot)
|
||||
{
|
||||
List<int> versionList = new List<int>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前最大的补丁包版本号
|
||||
/// </summary>
|
||||
/// <returns>如果没有任何补丁版本,那么返回-1</returns>
|
||||
public static int GetMaxPackageVersion(BuildTarget buildTarget, string outputRoot)
|
||||
{
|
||||
List<int> versionList = GetPackageVersionList(buildTarget, outputRoot);
|
||||
if (versionList.Count == 0)
|
||||
return -1;
|
||||
return versionList[versionList.Count - 1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否存在任何补丁包版本
|
||||
/// </summary>
|
||||
public static bool HasAnyPackageVersion(BuildTarget buildTarget, string outputRoot)
|
||||
{
|
||||
List<int> versionList = GetPackageVersionList(buildTarget, outputRoot);
|
||||
return versionList.Count > 0;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 从输出目录加载补丁清单文件
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f48abdec05f0dbe438a83e181fe6bc93
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 检测所有损坏的预制体文件
|
||||
/// </summary>
|
||||
public static void CheckCorruptionPrefab(List<string> 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($"没有发现损坏预制件");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测所有动画控制器的冗余状态
|
||||
/// </summary>
|
||||
public static void FindRedundantAnimationState(List<string> 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<AnimatorController>(assetPath);
|
||||
if (EditorTools.FindRedundantAnimationState(animator))
|
||||
{
|
||||
findCount++;
|
||||
Debug.LogWarning($"发现冗余的动画控制器:{assetPath}");
|
||||
}
|
||||
EditorTools.DisplayProgressBar("检测冗余的动画控制器", ++checkCount, findAssets.Length);
|
||||
}
|
||||
EditorTools.ClearProgressBar();
|
||||
|
||||
if (findCount == 0)
|
||||
Debug.Log($"没有发现冗余的动画控制器");
|
||||
else
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理所有材质球的冗余属性
|
||||
/// </summary>
|
||||
public static void ClearMaterialUnusedProperty(List<string> 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<Material>(assetPath);
|
||||
if (EditorTools.ClearMaterialUnusedProperty(mat))
|
||||
{
|
||||
removedCount++;
|
||||
Debug.LogWarning($"材质球已被处理:{assetPath}");
|
||||
}
|
||||
EditorTools.DisplayProgressBar("清理冗余的材质球", ++checkCount, findAssets.Length);
|
||||
}
|
||||
EditorTools.ClearProgressBar();
|
||||
|
||||
if (removedCount == 0)
|
||||
Debug.Log($"没有发现冗余的材质球");
|
||||
else
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fe50795c51a46884088139b840c1557f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行构建
|
||||
/// </summary>
|
||||
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";
|
||||
|
||||
/// <summary>
|
||||
/// 存储配置
|
||||
/// </summary>
|
||||
private void SaveSettingsToPlayerPrefs()
|
||||
{
|
||||
EditorTools.PlayerSetEnum<ECompressOption>(StrEditorCompressOption, _compressOption);
|
||||
EditorPrefs.SetBool(StrEditorIsAppendExtension, _isAppendExtension);
|
||||
EditorPrefs.SetBool(StrEditorIsForceRebuild, _isForceRebuild);
|
||||
EditorPrefs.SetString(StrEditorBuildinTags, _buildinTags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取配置
|
||||
/// </summary>
|
||||
private void LoadSettingsFromPlayerPrefs()
|
||||
{
|
||||
_compressOption = EditorTools.PlayerGetEnum<ECompressOption>(StrEditorCompressOption, ECompressOption.Uncompressed);
|
||||
_isAppendExtension = EditorPrefs.GetBool(StrEditorIsAppendExtension, false);
|
||||
_isForceRebuild = EditorPrefs.GetBool(StrEditorIsForceRebuild, false);
|
||||
_buildinTags = EditorPrefs.GetString(StrEditorBuildinTags, string.Empty);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 66e43420e95cd0b4bae8803a31e9817b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,117 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 构建的资源信息类
|
||||
/// </summary>
|
||||
public class BuildAssetInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源路径
|
||||
/// </summary>
|
||||
public string AssetPath { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源包标签
|
||||
/// </summary>
|
||||
public string BundleLabel { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源包文件格式
|
||||
/// </summary>
|
||||
public string BundleVariant { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为原生资源
|
||||
/// </summary>
|
||||
public bool IsRawAsset = false;
|
||||
|
||||
/// <summary>
|
||||
/// 是否为主动收集资源
|
||||
/// </summary>
|
||||
public bool IsCollectAsset = false;
|
||||
|
||||
/// <summary>
|
||||
/// 资源标记列表
|
||||
/// </summary>
|
||||
public List<string> AssetTags = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 被依赖次数
|
||||
/// </summary>
|
||||
public int DependCount = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 依赖的所有资源信息
|
||||
/// 注意:包括零依赖资源(零依赖资源的资源包名无效)
|
||||
/// </summary>
|
||||
public List<BuildAssetInfo> AllDependAssetInfos { private set; get; } = null;
|
||||
|
||||
|
||||
public BuildAssetInfo(string assetPath)
|
||||
{
|
||||
AssetPath = assetPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置所有依赖的资源
|
||||
/// </summary>
|
||||
public void SetAllDependAssetInfos(List<BuildAssetInfo> dependAssetInfos)
|
||||
{
|
||||
if (AllDependAssetInfos != null)
|
||||
throw new System.Exception("Should never get here !");
|
||||
|
||||
AllDependAssetInfos = dependAssetInfos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置资源包的标签和文件格式
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加资源标记
|
||||
/// </summary>
|
||||
public void AddAssetTags(List<string> tags)
|
||||
{
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
if (AssetTags.Contains(tag) == false)
|
||||
{
|
||||
AssetTags.Add(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取资源包的完整名称
|
||||
/// </summary>
|
||||
public string GetBundleName()
|
||||
{
|
||||
if (string.IsNullOrEmpty(BundleLabel) || string.IsNullOrEmpty(BundleVariant))
|
||||
throw new System.ArgumentNullException();
|
||||
|
||||
return AssetBundleBuilderHelper.MakeBundleName(BundleLabel, BundleVariant);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测资源包名是否有效
|
||||
/// </summary>
|
||||
public bool CheckBundleNameValid()
|
||||
{
|
||||
if (string.IsNullOrEmpty(BundleLabel) == false && string.IsNullOrEmpty(BundleVariant) == false)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 447008dd110b8d746aafbe88c78bee5d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,148 @@
|
|||
using System.Linq;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 构建的资源包信息类
|
||||
/// </summary>
|
||||
public class BuildBundleInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源包完整名称
|
||||
/// </summary>
|
||||
public string BundleName { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源包标签名
|
||||
/// </summary>
|
||||
public string BundleLabel { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源包文件格式
|
||||
/// </summary>
|
||||
public string BundleVariant { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 包含的资源列表
|
||||
/// </summary>
|
||||
public readonly List<BuildAssetInfo> Assets = new List<BuildAssetInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// 是否为原生文件
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含指定资源
|
||||
/// </summary>
|
||||
public bool IsContainsAsset(string assetPath)
|
||||
{
|
||||
foreach (var assetInfo in Assets)
|
||||
{
|
||||
if (assetInfo.AssetPath == assetPath)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个打包资源
|
||||
/// </summary>
|
||||
public void PackAsset(BuildAssetInfo assetInfo)
|
||||
{
|
||||
if (IsContainsAsset(assetInfo.AssetPath))
|
||||
throw new System.Exception($"Asset is existed : {assetInfo.AssetPath}");
|
||||
|
||||
Assets.Add(assetInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文件的扩展名
|
||||
/// </summary>
|
||||
public string GetAppendExtension()
|
||||
{
|
||||
if (IsRawFile)
|
||||
return $".{ResourceSettingData.Setting.RawFileVariant}";
|
||||
else
|
||||
return $".{ResourceSettingData.Setting.AssetBundleFileVariant}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取资源标记列表
|
||||
/// </summary>
|
||||
public string[] GetAssetTags()
|
||||
{
|
||||
List<string> result = new List<string>(Assets.Count);
|
||||
foreach (var assetInfo in Assets)
|
||||
{
|
||||
foreach (var assetTag in assetInfo.AssetTags)
|
||||
{
|
||||
if (result.Contains(assetTag) == false)
|
||||
result.Add(assetTag);
|
||||
}
|
||||
}
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取主动收集的资源路径列表
|
||||
/// </summary>
|
||||
public string[] GetCollectAssetPaths()
|
||||
{
|
||||
return Assets.Where(t => t.IsCollectAsset).Select(t => t.AssetPath).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取构建的资源路径列表
|
||||
/// </summary>
|
||||
public string[] GetBuildinAssetPaths()
|
||||
{
|
||||
return Assets.Select(t => t.AssetPath).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取主动收集的资源信息列表
|
||||
/// </summary>
|
||||
public BuildAssetInfo[] GetCollectAssetInfos()
|
||||
{
|
||||
return Assets.Where(t => t.IsCollectAsset).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建AssetBundleBuild类
|
||||
/// </summary>
|
||||
public UnityEditor.AssetBundleBuild CreatePipelineBuild()
|
||||
{
|
||||
// 注意:我们不在支持AssetBundle的变种机制
|
||||
AssetBundleBuild build = new AssetBundleBuild();
|
||||
build.assetBundleName = BundleName;
|
||||
build.assetBundleVariant = string.Empty;
|
||||
build.assetNames = GetBuildinAssetPaths();
|
||||
return build;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 826a9d7b4de0eba40b5c39b33747c011
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2875232ad7f87c148b752d432f3a54f2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
public class BuildContext
|
||||
{
|
||||
private readonly Dictionary<System.Type, IContextObject> _contextObjects = new Dictionary<System.Type, IContextObject>();
|
||||
|
||||
/// <summary>
|
||||
/// 清空所有情景对象
|
||||
/// </summary>
|
||||
public void ClearAllContext()
|
||||
{
|
||||
_contextObjects.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置情景对象
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取情景对象
|
||||
/// </summary>
|
||||
public T GetContextObject<T>() 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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6a232601f99c4634ea17fca4979f80ab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
public class BuildRunner
|
||||
{
|
||||
/// <summary>
|
||||
/// 执行构建流程
|
||||
/// </summary>
|
||||
/// <returns>如果成功返回TRUE,否则返回FALSE</returns>
|
||||
public static bool Run(List<IBuildTask> 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 79014124da678684388454d6ea892722
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
public interface IBuildTask
|
||||
{
|
||||
void Run(BuildContext context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8888657c45e12c646a8349bac6bf0326
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
public interface IContextObject
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 151a4acd5ad1c2046be20e080f9bdad4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 254f430e0264bd84387878f8d7280e44
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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<AssetBundleBuilder.BuildParametersContext>();
|
||||
var buildMapContext = context.GetContextObject<TaskGetBuildMap.BuildMapContext>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证构建结果
|
||||
/// </summary>
|
||||
private void VerifyingBuildingResult(BuildContext context, AssetBundleManifest unityManifest)
|
||||
{
|
||||
var buildParameters = context.GetContextObject<AssetBundleBuilder.BuildParametersContext>();
|
||||
var buildMapContext = context.GetContextObject<TaskGetBuildMap.BuildMapContext>();
|
||||
string[] buildedBundles = unityManifest.GetAllAssetBundles();
|
||||
|
||||
// 1. 过滤掉原生Bundle
|
||||
List<BuildBundleInfo> expectBundles = new List<BuildBundleInfo>(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("构建结果验证成功!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析.manifest文件并获取资源列表
|
||||
/// </summary>
|
||||
private string[] GetAssetBundleAllAssets(string filePath)
|
||||
{
|
||||
string manifestFilePath = $"{filePath}.manifest";
|
||||
List<string> assetLines = new List<string>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9466e826c135e994c84961e4b921ca5f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 拷贝内置文件到StreamingAssets
|
||||
/// </summary>
|
||||
public class TaskCopyBuildinFiles : IBuildTask
|
||||
{
|
||||
void IBuildTask.Run(BuildContext context)
|
||||
{
|
||||
// 注意:我们只有在强制重建的时候才会拷贝
|
||||
var buildParameters = context.GetContextObject<AssetBundleBuilder.BuildParametersContext>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5c77e17c3a3a57548a218f1cd26f5a55
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,155 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using YooAsset.Utility;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建补丁清单文件
|
||||
/// </summary>
|
||||
public class TaskCreatePatchManifest : IBuildTask
|
||||
{
|
||||
void IBuildTask.Run(BuildContext context)
|
||||
{
|
||||
var buildParameters = context.GetContextObject<AssetBundleBuilder.BuildParametersContext>();
|
||||
var encryptionContext = context.GetContextObject<TaskEncryption.EncryptionContext>();
|
||||
var buildMapContext = context.GetContextObject<TaskGetBuildMap.BuildMapContext>();
|
||||
CreatePatchManifestFile(buildParameters, buildMapContext, encryptionContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建补丁清单文件到输出目录
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取资源包列表
|
||||
/// </summary>
|
||||
private List<PatchBundle> GetAllPatchBundle(AssetBundleBuilder.BuildParametersContext buildParameters,
|
||||
TaskGetBuildMap.BuildMapContext buildMapContext, TaskEncryption.EncryptionContext encryptionContext)
|
||||
{
|
||||
List<PatchBundle> result = new List<PatchBundle>(1000);
|
||||
|
||||
// 内置标记列表
|
||||
List<string> 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<string> buildinTags)
|
||||
{
|
||||
// 注意:没有任何标记的Bundle文件默认为内置文件
|
||||
if (bundleTags.Length == 0)
|
||||
return true;
|
||||
|
||||
foreach (var tag in bundleTags)
|
||||
{
|
||||
if (buildinTags.Contains(tag))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取资源列表
|
||||
/// </summary>
|
||||
private List<PatchAsset> GetAllPatchAsset(TaskGetBuildMap.BuildMapContext buildMapContext, List<PatchBundle> bundleList)
|
||||
{
|
||||
List<PatchAsset> result = new List<PatchAsset>(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<PatchBundle> bundleList)
|
||||
{
|
||||
List<int> result = new List<int>();
|
||||
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<PatchBundle> bundleList)
|
||||
{
|
||||
for (int index = 0; index < bundleList.Count; index++)
|
||||
{
|
||||
if (bundleList[index].BundleName == bundleName)
|
||||
return index;
|
||||
}
|
||||
throw new Exception($"Not found bundle name : {bundleName}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 566c37f5a59f2e84397a9527981a7310
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,83 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 制作补丁包
|
||||
/// </summary>
|
||||
public class TaskCreatePatchPackage : IBuildTask
|
||||
{
|
||||
void IBuildTask.Run(BuildContext context)
|
||||
{
|
||||
var buildParameters = context.GetContextObject<AssetBundleBuilder.BuildParametersContext>();
|
||||
CopyPatchFiles(buildParameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 拷贝补丁文件到补丁包目录
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d110092f543e3fe40a4d3882e1a718e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,205 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建说明文件
|
||||
/// </summary>
|
||||
public class TaskCreateReadme : IBuildTask
|
||||
{
|
||||
void IBuildTask.Run(BuildContext context)
|
||||
{
|
||||
var buildParameters = context.GetContextObject<AssetBundleBuilder.BuildParametersContext>();
|
||||
var buildMapContext = context.GetContextObject<TaskGetBuildMap.BuildMapContext>();
|
||||
CreateReadmeFile(buildParameters, buildMapContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建Readme文件到输出目录
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1660a082a6449f4429fcca15e4383f0b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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<string> EncryptList;
|
||||
|
||||
/// <summary>
|
||||
/// 检测是否为加密文件
|
||||
/// </summary>
|
||||
public bool IsEncryptFile(string bundleName)
|
||||
{
|
||||
return EncryptList.Contains(bundleName);
|
||||
}
|
||||
}
|
||||
|
||||
void IBuildTask.Run(BuildContext context)
|
||||
{
|
||||
var buildParameters = context.GetContextObject<AssetBundleBuilder.BuildParametersContext>();
|
||||
var buildMapContext = context.GetContextObject<TaskGetBuildMap.BuildMapContext>();
|
||||
|
||||
var encrypter = CreateAssetEncrypter();
|
||||
List<string> encryptList = EncryptFiles(encrypter, buildParameters, buildMapContext);
|
||||
|
||||
EncryptionContext encryptionContext = new EncryptionContext();
|
||||
encryptionContext.EncryptList = encryptList;
|
||||
context.SetContextObject(encryptionContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建加密类
|
||||
/// </summary>
|
||||
/// <returns>如果没有定义类型,则返回NULL</returns>
|
||||
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]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加密文件
|
||||
/// </summary>
|
||||
private List<string> EncryptFiles(IAssetEncrypter encrypter, AssetBundleBuilder.BuildParametersContext buildParameters, TaskGetBuildMap.BuildMapContext buildMapContext)
|
||||
{
|
||||
// 加密资源列表
|
||||
List<string> encryptList = new List<string>();
|
||||
|
||||
// 如果没有设置加密类
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c78d471226b9c8d429a2d962370f480b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源包列表
|
||||
/// </summary>
|
||||
public readonly List<BuildBundleInfo> BundleInfos = new List<BuildBundleInfo>(1000);
|
||||
|
||||
/// <summary>
|
||||
/// 冗余的资源列表
|
||||
/// </summary>
|
||||
public readonly List<string> RedundancyList = new List<string>(1000);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个打包资源
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有的打包资源
|
||||
/// </summary>
|
||||
public List<BuildAssetInfo> GetAllAssets()
|
||||
{
|
||||
List<BuildAssetInfo> result = new List<BuildAssetInfo>(BundleInfos.Count);
|
||||
foreach (var bundleInfo in BundleInfos)
|
||||
{
|
||||
result.AddRange(bundleInfo.Assets);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取AssetBundle内包含的标记列表
|
||||
/// </summary>
|
||||
public string[] GetAssetTags(string bundleFullName)
|
||||
{
|
||||
if (TryGetBundleInfo(bundleFullName, out BuildBundleInfo bundleInfo))
|
||||
{
|
||||
return bundleInfo.GetAssetTags();
|
||||
}
|
||||
throw new Exception($"Not found {nameof(BuildBundleInfo)} : {bundleFullName}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取AssetBundle内收集的资源路径列表
|
||||
/// </summary>
|
||||
public string[] GetCollectAssetPaths(string bundleFullName)
|
||||
{
|
||||
if (TryGetBundleInfo(bundleFullName, out BuildBundleInfo bundleInfo))
|
||||
{
|
||||
return bundleInfo.GetCollectAssetPaths();
|
||||
}
|
||||
throw new Exception($"Not found {nameof(BuildBundleInfo)} : {bundleFullName}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取AssetBundle内构建的资源路径列表
|
||||
/// </summary>
|
||||
public string[] GetBuildinAssetPaths(string bundleFullName)
|
||||
{
|
||||
if (TryGetBundleInfo(bundleFullName, out BuildBundleInfo bundleInfo))
|
||||
{
|
||||
return bundleInfo.GetBuildinAssetPaths();
|
||||
}
|
||||
throw new Exception($"Not found {nameof(BuildBundleInfo)} : {bundleFullName}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取构建管线里需要的数据
|
||||
/// </summary>
|
||||
public UnityEditor.AssetBundleBuild[] GetPipelineBuilds()
|
||||
{
|
||||
List<AssetBundleBuild> builds = new List<AssetBundleBuild>(BundleInfos.Count);
|
||||
foreach (var bundleInfo in BundleInfos)
|
||||
{
|
||||
if (bundleInfo.IsRawFile == false)
|
||||
builds.Add(bundleInfo.CreatePipelineBuild());
|
||||
}
|
||||
return builds.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测是否包含BundleName
|
||||
/// </summary>
|
||||
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<AssetBundleBuilder.BuildParametersContext>();
|
||||
BuildMapContext buildMapContext = new BuildMapContext();
|
||||
context.SetContextObject(buildMapContext);
|
||||
SetupBuildMap(buildMapContext, buildParametersContext);
|
||||
|
||||
// 检测构建结果
|
||||
CheckBuildMapContent(buildMapContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 组织构建的资源包
|
||||
/// </summary>
|
||||
private void SetupBuildMap(BuildMapContext buildMapContext, AssetBundleBuilder.BuildParametersContext buildParameters)
|
||||
{
|
||||
Dictionary<string, BuildAssetInfo> buildAssets = new Dictionary<string, BuildAssetInfo>();
|
||||
|
||||
// 1. 获取主动收集的资源
|
||||
List<AssetCollectInfo> allCollectInfos = AssetBundleCollectorSettingData.GetAllCollectAssets();
|
||||
|
||||
// 2. 对收集的资源进行依赖分析
|
||||
int progressValue = 0;
|
||||
foreach (AssetCollectInfo collectInfo in allCollectInfos)
|
||||
{
|
||||
string mainAssetPath = collectInfo.AssetPath;
|
||||
|
||||
// 获取所有依赖资源
|
||||
List<BuildAssetInfo> 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<BuildAssetInfo>(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<BuildAssetInfo> undependentAssets = new List<BuildAssetInfo>();
|
||||
foreach (KeyValuePair<string, BuildAssetInfo> 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<string, BuildAssetInfo> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定资源依赖的所有资源列表
|
||||
/// 注意:返回列表里已经包括主资源自己
|
||||
/// </summary>
|
||||
private List<BuildAssetInfo> GetAllDependencies(string mainAssetPath)
|
||||
{
|
||||
List<BuildAssetInfo> result = new List<BuildAssetInfo>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测构建结果
|
||||
/// </summary>
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建冗余类
|
||||
/// </summary>
|
||||
/// <returns>如果没有定义类型,则返回NULL</returns>
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: baee18c5c7b76584b90413bf20fdae9a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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<AssetBundleBuilder.BuildParametersContext>();
|
||||
|
||||
// 检测构建平台是否合法
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0dee755cccd608d48b6581ec33c99b39
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// AssetBundle压缩选项
|
||||
/// </summary>
|
||||
public enum ECompressOption
|
||||
{
|
||||
Uncompressed = 0,
|
||||
LZMA,
|
||||
LZ4,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1e8ac0dfc77bd1b4697db63d52ab4c6e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
public interface IAssetEncrypter
|
||||
{
|
||||
/// <summary>
|
||||
/// 检测是否需要加密
|
||||
/// </summary>
|
||||
bool Check(string filePath);
|
||||
|
||||
/// <summary>
|
||||
/// 加密方法
|
||||
/// </summary>
|
||||
/// <param name="fileData">要加密的文件数据</param>
|
||||
/// <returns>返回加密后的字节数据</returns>
|
||||
byte[] Encrypt(byte[] fileData);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 04491137351983348959c00ec4ee226a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
public interface IAssetRedundancy
|
||||
{
|
||||
/// <summary>
|
||||
/// 检测是否冗余
|
||||
/// </summary>
|
||||
bool Check(string filePath);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d211989e17a617741a1963b8523fe9c7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0dbd5f557007d574e80c42d4a15421a9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 收集的文件夹路径
|
||||
/// </summary>
|
||||
public string CollectDirectory = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 打包规则类名
|
||||
/// </summary>
|
||||
public string PackRuleName = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 过滤规则类名
|
||||
/// </summary>
|
||||
public string FilterRuleName = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 不写入资源路径到清单文件
|
||||
/// 注意:对于不依赖于代码加载的收集资源,可以禁止写入资源路径信息到清单文件
|
||||
/// </summary>
|
||||
public bool DontWriteAssetPath = false;
|
||||
|
||||
/// <summary>
|
||||
/// 资源标记
|
||||
/// </summary>
|
||||
public string AssetTags = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 获取资源标记列表
|
||||
/// </summary>
|
||||
public List<string> GetAssetTags()
|
||||
{
|
||||
return StringUtility.StringToStringList(AssetTags, ';');
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Directory : {CollectDirectory} | {PackRuleName} | {FilterRuleName} | {DontWriteAssetPath} | {AssetTags}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否收集全路径的着色器
|
||||
/// </summary>
|
||||
public bool IsCollectAllShaders = false;
|
||||
|
||||
/// <summary>
|
||||
/// 收集的着色器Bundle名称
|
||||
/// </summary>
|
||||
public string ShadersBundleName = "myshaders";
|
||||
|
||||
/// <summary>
|
||||
/// 收集列表
|
||||
/// </summary>
|
||||
public List<Collector> Collectors = new List<Collector>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4cc54f0173b27fb40aee29e7eb3364da
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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<string, System.Type> _cachePackRuleTypes = new Dictionary<string, System.Type>();
|
||||
private static readonly Dictionary<string, IPackRule> _cachePackRuleInstance = new Dictionary<string, IPackRule>();
|
||||
|
||||
private static readonly Dictionary<string, System.Type> _cacheFilterRuleTypes = new Dictionary<string, System.Type>();
|
||||
private static readonly Dictionary<string, IFilterRule> _cacheFilterRuleInstance = new Dictionary<string, IFilterRule>();
|
||||
|
||||
|
||||
private static AssetBundleCollectorSetting _setting = null;
|
||||
public static AssetBundleCollectorSetting Setting
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_setting == null)
|
||||
LoadSettingData();
|
||||
return _setting;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<string> GetPackRuleNames()
|
||||
{
|
||||
if (_setting == null)
|
||||
LoadSettingData();
|
||||
|
||||
List<string> names = new List<string>();
|
||||
foreach (var pair in _cachePackRuleTypes)
|
||||
{
|
||||
names.Add(pair.Key);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
public static List<string> GetFilterRuleNames()
|
||||
{
|
||||
if (_setting == null)
|
||||
LoadSettingData();
|
||||
|
||||
List<string> names = new List<string>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载配置文件
|
||||
/// </summary>
|
||||
private static void LoadSettingData()
|
||||
{
|
||||
// 加载配置文件
|
||||
_setting = AssetDatabase.LoadAssetAtPath<AssetBundleCollectorSetting>(EditorDefine.AssetBundleCollectorSettingFilePath);
|
||||
if (_setting == null)
|
||||
{
|
||||
Debug.LogWarning($"Create new {nameof(AssetBundleCollectorSetting)}.asset : {EditorDefine.AssetBundleCollectorSettingFilePath}");
|
||||
_setting = ScriptableObject.CreateInstance<AssetBundleCollectorSetting>();
|
||||
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<Type> types = new List<Type>(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<Type> types = new List<Type>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 存储文件
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有的DLC标记
|
||||
/// </summary>
|
||||
public static List<string> GetAllAssetTags()
|
||||
{
|
||||
List<string> result = new List<string>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取收集器总数
|
||||
/// </summary>
|
||||
public static int GetCollecterCount()
|
||||
{
|
||||
return Setting.Collectors.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有的收集路径
|
||||
/// </summary>
|
||||
public static List<string> GetAllCollectDirectory()
|
||||
{
|
||||
List<string> result = new List<string>();
|
||||
for (int i = 0; i < Setting.Collectors.Count; i++)
|
||||
{
|
||||
AssetBundleCollectorSetting.Collector collector = Setting.Collectors[i];
|
||||
result.Add(collector.CollectDirectory);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有收集的资源
|
||||
/// 注意:跳过了不写入资源路径的收集器
|
||||
/// </summary>
|
||||
public static List<AssetCollectInfo> GetAllCollectAssets()
|
||||
{
|
||||
Dictionary<string, AssetCollectInfo> result = new Dictionary<string, AssetCollectInfo>(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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测资源路径是否被收集器覆盖
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测资源是否有效
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取资源包名
|
||||
/// </summary>
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5d4d2a4e23c5e0646a07add04fd8e18a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上次打开的文件夹路径
|
||||
/// </summary>
|
||||
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<string> packRuleNames = AssetBundleCollectorSettingData.GetPackRuleNames();
|
||||
_packRuleArray = packRuleNames.ToArray();
|
||||
|
||||
List<string> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cfca8094e2a905a4da05586b2d7fa919
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,30 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
public class AssetCollectInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源路径
|
||||
/// </summary>
|
||||
public string AssetPath { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源标记列表
|
||||
/// </summary>
|
||||
public List<string> AssetTags { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为原生资源
|
||||
/// </summary>
|
||||
public bool IsRawAsset { private set; get; }
|
||||
|
||||
public AssetCollectInfo(string assetPath, List<string> assetTags, bool isRawAsset)
|
||||
{
|
||||
AssetPath = assetPath;
|
||||
AssetTags = assetTags;
|
||||
IsRawAsset = isRawAsset;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b44b2cc585bc59c4d879d1edac67f7c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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<CollectWrapper> wrappers = new List<CollectWrapper>();
|
||||
|
||||
// 加载文件
|
||||
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}个收集器。");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c35b6554a4bedd049a8868d9249846d7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,53 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.IO;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 收集所有资源
|
||||
/// </summary>
|
||||
public class CollectAll : IFilterRule
|
||||
{
|
||||
public bool IsCollectAsset(string assetPath)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 只收集场景
|
||||
/// </summary>
|
||||
public class CollectScene : IFilterRule
|
||||
{
|
||||
public bool IsCollectAsset(string assetPath)
|
||||
{
|
||||
return Path.GetExtension(assetPath) == ".unity";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 只收集预制体
|
||||
/// </summary>
|
||||
public class CollectPrefab : IFilterRule
|
||||
{
|
||||
public bool IsCollectAsset(string assetPath)
|
||||
{
|
||||
return Path.GetExtension(assetPath) == ".prefab";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 只收集精灵类型的资源
|
||||
/// </summary>
|
||||
public class CollectSprite : IFilterRule
|
||||
{
|
||||
public bool IsCollectAsset(string assetPath)
|
||||
{
|
||||
if (AssetDatabase.GetMainAssetTypeAtPath(assetPath) == typeof(Sprite))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a74c81d149472fb4c960a1db1fd8accc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using YooAsset.Utility;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 以文件路径作为AssetBundle标签名
|
||||
/// </summary>
|
||||
public class PackExplicit : IPackRule
|
||||
{
|
||||
string IPackRule.GetAssetBundleLabel(string assetPath)
|
||||
{
|
||||
return StringUtility.RemoveExtension(assetPath); //"Assets/Config/test.txt" --> "Assets/Config/test"
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 以父文件夹路径作为AssetBundle标签名
|
||||
/// 注意:该文件夹下所有资源被打到一个AssetBundle文件里
|
||||
/// </summary>
|
||||
public class PackDirectory : IPackRule
|
||||
{
|
||||
string IPackRule.GetAssetBundleLabel(string assetPath)
|
||||
{
|
||||
return Path.GetDirectoryName(assetPath); //"Assets/Config/test.txt" --> "Assets/Config"
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 原生文件打包模式
|
||||
/// 注意:原生文件打包支持:图片,音频,视频,文本
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4a924dc0a22fc104781bf9aaadd60c29
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源过滤规则接口
|
||||
/// </summary>
|
||||
public interface IFilterRule
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否为收集资源
|
||||
/// </summary>
|
||||
/// <param name="assetPath">资源路径</param>
|
||||
/// <returns>如果收集该资源返回TRUE</returns>
|
||||
bool IsCollectAsset(string assetPath);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ffe1385deb0bd9844a514f1c2fd65e62
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源打包规则接口
|
||||
/// </summary>
|
||||
public interface IPackRule
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取资源的打包标签
|
||||
/// </summary>
|
||||
string GetAssetBundleLabel(string assetPath);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 577f7c1abcdf0a140958f1d1d44d6f40
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
public class EditorDefine
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源包收集工具的配置文件存储路径
|
||||
/// </summary>
|
||||
public const string AssetBundleCollectorSettingFilePath = "Assets/YooAssetSetting/AssetBundleCollectorSetting.asset";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源搜索类型
|
||||
/// </summary>
|
||||
public enum EAssetSearchType
|
||||
{
|
||||
All,
|
||||
RuntimeAnimatorController,
|
||||
AnimationClip,
|
||||
AudioClip,
|
||||
AudioMixer,
|
||||
Font,
|
||||
Material,
|
||||
Mesh,
|
||||
Model,
|
||||
PhysicMaterial,
|
||||
Prefab,
|
||||
Scene,
|
||||
Script,
|
||||
Shader,
|
||||
Sprite,
|
||||
Texture,
|
||||
VideoClip,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源文件格式
|
||||
/// </summary>
|
||||
public enum EAssetFileExtension
|
||||
{
|
||||
prefab, //预制体
|
||||
unity, //场景
|
||||
fbx, //模型
|
||||
anim, //动画
|
||||
controller, //控制器
|
||||
png, //图片
|
||||
jpg, //图片
|
||||
mat, //材质球
|
||||
shader, //着色器
|
||||
ttf, //字体
|
||||
cs, //脚本
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e33ff61240a851e4fbaea71d01a460cd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 编辑器工具类
|
||||
/// </summary>
|
||||
public static class EditorTools
|
||||
{
|
||||
#region NGUI
|
||||
/// <summary>
|
||||
/// Draw a distinctly different looking header label
|
||||
/// </summary>
|
||||
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 = "<b><size=11>" + text + "</size></b>";
|
||||
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
|
||||
/// <summary>
|
||||
/// 调用私有的静态方法
|
||||
/// </summary>
|
||||
/// <param name="type">类的类型</param>
|
||||
/// <param name="method">类里要调用的方法名</param>
|
||||
/// <param name="parameters">调用方法传入的参数</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调用公开的静态方法
|
||||
/// </summary>
|
||||
/// <param name="type">类的类型</param>
|
||||
/// <param name="method">类里要调用的方法名</param>
|
||||
/// <param name="parameters">调用方法传入的参数</param>
|
||||
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
|
||||
/// <summary>
|
||||
/// 搜集资源
|
||||
/// </summary>
|
||||
/// <param name="searchType">搜集的资源类型</param>
|
||||
/// <param name="searchInFolders">指定搜索的文件夹列表</param>
|
||||
/// <returns>返回搜集到的资源路径列表</returns>
|
||||
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<string> result = new List<string>();
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 搜集资源
|
||||
/// </summary>
|
||||
/// <param name="searchType">搜集的资源类型</param>
|
||||
/// <param name="searchInFolder">指定搜索的文件夹</param>
|
||||
/// <returns>返回搜集到的资源路径列表</returns>
|
||||
public static string[] FindAssets(EAssetSearchType searchType, string searchInFolder)
|
||||
{
|
||||
return FindAssets(searchType, new string[] { searchInFolder });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开搜索面板
|
||||
/// </summary>
|
||||
/// <param name="title">标题名称</param>
|
||||
/// <param name="defaultPath">默认搜索路径</param>
|
||||
/// <returns>返回选择的文件夹绝对路径,如果无效返回NULL</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开搜索面板
|
||||
/// </summary>
|
||||
/// <param name="title">标题名称</param>
|
||||
/// <param name="defaultPath">默认搜索路径</param>
|
||||
/// <returns>返回选择的文件绝对路径,如果无效返回NULL</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示进度框
|
||||
/// </summary>
|
||||
public static void DisplayProgressBar(string tips, int progressValue, int totalValue)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("进度", $"{tips} : {progressValue}/{totalValue}", (float)progressValue / totalValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 隐藏进度框
|
||||
/// </summary>
|
||||
public static void ClearProgressBar()
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 编辑器窗口
|
||||
public static void FocusUnitySceneWindow()
|
||||
{
|
||||
EditorWindow.FocusWindowIfItsOpen<SceneView>();
|
||||
}
|
||||
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 引用关系
|
||||
/// <summary>
|
||||
/// 获取场景里的克隆预制体
|
||||
/// </summary>
|
||||
public static GameObject GetClonePrefabInScene(GameObject sourcePrefab)
|
||||
{
|
||||
GameObject[] findObjects = GameObject.FindObjectsOfType<GameObject>();
|
||||
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; //没有找到合适的对象
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找场景里的引用对象
|
||||
/// </summary>
|
||||
public static void FindReferencesInScene(UnityEngine.Object to)
|
||||
{
|
||||
var referencedBy = new List<Object>();
|
||||
|
||||
GameObject[] findObjects = GameObject.FindObjectsOfType<GameObject>(); //注意:只能获取激活的GameObject
|
||||
for (int j = 0; j < findObjects.Length; j++)
|
||||
{
|
||||
GameObject findObject = findObjects[j];
|
||||
|
||||
#if UNITY_2017_4
|
||||
// 如果Prefab匹配
|
||||
if (PrefabUtility.GetPrefabType(findObject) == PrefabType.PrefabInstance)
|
||||
{
|
||||
if (PrefabUtility.GetPrefabParent(findObject) == to)
|
||||
referencedBy.Add(findObject);
|
||||
}
|
||||
#else
|
||||
// 如果Prefab匹配
|
||||
if (PrefabUtility.GetPrefabInstanceStatus(findObject) == PrefabInstanceStatus.Connected)
|
||||
{
|
||||
if (PrefabUtility.GetCorrespondingObjectFromSource(findObject) == to)
|
||||
referencedBy.Add(findObject);
|
||||
}
|
||||
#endif
|
||||
|
||||
// 如果组件匹配
|
||||
Component[] components = findObject.GetComponents<Component>();
|
||||
for (int i = 0; i < components.Length; i++)
|
||||
{
|
||||
Component c = components[i];
|
||||
if (!c) continue;
|
||||
|
||||
SerializedObject so = new SerializedObject(c);
|
||||
SerializedProperty sp = so.GetIterator();
|
||||
while (sp.NextVisible(true))
|
||||
{
|
||||
if (sp.propertyType == SerializedPropertyType.ObjectReference)
|
||||
{
|
||||
if (sp.objectReferenceValue != null && sp.objectReferenceValue == to)
|
||||
referencedBy.Add(c.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (referencedBy.Any())
|
||||
Selection.objects = referencedBy.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找场景里的引用对象
|
||||
/// </summary>
|
||||
public static void FindReferencesInPrefabs(UnityEngine.Object to, GameObject[] sourcePrefabs)
|
||||
{
|
||||
var referencedBy = new List<Object>();
|
||||
|
||||
for (int j = 0; j < sourcePrefabs.Length; j++)
|
||||
{
|
||||
GameObject clonePrefab = GetClonePrefabInScene(sourcePrefabs[j]);
|
||||
if (clonePrefab == null)
|
||||
continue;
|
||||
|
||||
#if UNITY_2017_4
|
||||
// 如果Prefab匹配
|
||||
if (PrefabUtility.GetPrefabParent(clonePrefab) == to)
|
||||
referencedBy.Add(clonePrefab);
|
||||
#else
|
||||
// 如果Prefab匹配
|
||||
if (PrefabUtility.GetCorrespondingObjectFromSource(clonePrefab) == to)
|
||||
referencedBy.Add(clonePrefab);
|
||||
#endif
|
||||
|
||||
// 如果组件匹配
|
||||
Component[] components = clonePrefab.GetComponentsInChildren<Component>(true); //GetComponents<Component>();
|
||||
for (int i = 0; i < components.Length; i++)
|
||||
{
|
||||
Component c = components[i];
|
||||
if (!c) continue;
|
||||
|
||||
SerializedObject so = new SerializedObject(c);
|
||||
SerializedProperty sp = so.GetIterator();
|
||||
while (sp.NextVisible(true))
|
||||
{
|
||||
if (sp.propertyType == SerializedPropertyType.ObjectReference)
|
||||
{
|
||||
if (sp.objectReferenceValue != null && sp.objectReferenceValue == to)
|
||||
referencedBy.Add(c.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (referencedBy.Any())
|
||||
Selection.objects = referencedBy.ToArray();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 材质球
|
||||
/// <summary>
|
||||
/// 清理无用的材质球属性
|
||||
/// </summary>
|
||||
public static bool ClearMaterialUnusedProperty(Material mat)
|
||||
{
|
||||
bool removeUnused = false;
|
||||
SerializedObject so = new SerializedObject(mat);
|
||||
SerializedProperty sp = so.FindProperty("m_SavedProperties");
|
||||
|
||||
sp.Next(true);
|
||||
do
|
||||
{
|
||||
if (sp.isArray == false)
|
||||
continue;
|
||||
|
||||
for (int i = sp.arraySize - 1; i >= 0; --i)
|
||||
{
|
||||
var p1 = sp.GetArrayElementAtIndex(i);
|
||||
if (p1.isArray)
|
||||
{
|
||||
for (int ii = p1.arraySize - 1; ii >= 0; --ii)
|
||||
{
|
||||
var p2 = p1.GetArrayElementAtIndex(ii);
|
||||
var val = p2.FindPropertyRelative("first");
|
||||
if (mat.HasProperty(val.stringValue) == false)
|
||||
{
|
||||
Debug.Log($"Material {mat.name} remove unused property : {val.stringValue}");
|
||||
p1.DeleteArrayElementAtIndex(ii);
|
||||
removeUnused = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var val = p1.FindPropertyRelative("first");
|
||||
if (mat.HasProperty(val.stringValue) == false)
|
||||
{
|
||||
Debug.Log($"Material {mat.name} remove unused property : {val.stringValue}");
|
||||
sp.DeleteArrayElementAtIndex(i);
|
||||
removeUnused = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (sp.Next(false));
|
||||
so.ApplyModifiedProperties();
|
||||
return removeUnused;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 动画控制器
|
||||
/// <summary>
|
||||
/// 查找动画控制器里冗余的动画状态机
|
||||
/// </summary>
|
||||
public static bool FindRedundantAnimationState(AnimatorController animatorController)
|
||||
{
|
||||
if (animatorController == null)
|
||||
return false;
|
||||
|
||||
string assetPath = AssetDatabase.GetAssetPath(animatorController);
|
||||
|
||||
// 查找使用的状态机名称
|
||||
List<string> usedStateNames = new List<string>();
|
||||
foreach (var layer in animatorController.layers)
|
||||
{
|
||||
foreach (var state in layer.stateMachine.states)
|
||||
{
|
||||
usedStateNames.Add(state.state.name);
|
||||
}
|
||||
}
|
||||
|
||||
List<string> allLines = new List<string>();
|
||||
List<int> stateIndexList = new List<int>();
|
||||
using (StreamReader reader = File.OpenText(assetPath))
|
||||
{
|
||||
string content;
|
||||
while (null != (content = reader.ReadLine()))
|
||||
{
|
||||
allLines.Add(content);
|
||||
if (content.StartsWith("AnimatorState:"))
|
||||
{
|
||||
stateIndexList.Add(allLines.Count - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<string> allStateNames = new List<string>();
|
||||
foreach (var index in stateIndexList)
|
||||
{
|
||||
for (int i = index; i < allLines.Count; i++)
|
||||
{
|
||||
string content = allLines[i];
|
||||
content = content.Trim();
|
||||
if (content.StartsWith("m_Name"))
|
||||
{
|
||||
string[] splits = content.Split(':');
|
||||
string name = splits[1].TrimStart(' '); //移除前面的空格
|
||||
allStateNames.Add(name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool foundRedundantState = false;
|
||||
foreach (var stateName in allStateNames)
|
||||
{
|
||||
if (usedStateNames.Contains(stateName) == false)
|
||||
{
|
||||
Debug.LogWarning($"发现冗余的动画文件:{assetPath}={stateName}");
|
||||
foundRedundantState = true;
|
||||
}
|
||||
}
|
||||
return foundRedundantState;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 控制台
|
||||
private static MethodInfo _clearConsoleMethod;
|
||||
private static MethodInfo ClearConsoleMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_clearConsoleMethod == null)
|
||||
{
|
||||
Assembly assembly = Assembly.GetAssembly(typeof(SceneView));
|
||||
System.Type logEntries = assembly.GetType("UnityEditor.LogEntries");
|
||||
_clearConsoleMethod = logEntries.GetMethod("Clear");
|
||||
}
|
||||
return _clearConsoleMethod;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空控制台
|
||||
/// </summary>
|
||||
public static void ClearUnityConsole()
|
||||
{
|
||||
ClearConsoleMethod.Invoke(new object(), null);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 文件
|
||||
/// <summary>
|
||||
/// 测试写入权限
|
||||
/// </summary>
|
||||
public static bool HasWriteAccess(string directoryPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
string tmpFilePath = Path.Combine(directoryPath, Path.GetRandomFileName());
|
||||
using (FileStream fs = new FileStream(tmpFilePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.ReadWrite))
|
||||
{
|
||||
StreamWriter writer = new StreamWriter(fs);
|
||||
writer.Write(0);
|
||||
}
|
||||
File.Delete(tmpFilePath);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建文件所在的目录
|
||||
/// </summary>
|
||||
/// <param name="filePath">文件路径</param>
|
||||
public static void CreateFileDirectory(string filePath)
|
||||
{
|
||||
string destDirectory = Path.GetDirectoryName(filePath);
|
||||
CreateDirectory(destDirectory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建文件夹
|
||||
/// </summary>
|
||||
public static bool CreateDirectory(string directory)
|
||||
{
|
||||
if (Directory.Exists(directory) == false)
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除文件夹及子目录
|
||||
/// </summary>
|
||||
public static bool DeleteDirectory(string directory)
|
||||
{
|
||||
if (Directory.Exists(directory))
|
||||
{
|
||||
Directory.Delete(directory, true);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 文件重命名
|
||||
/// </summary>
|
||||
public static void FileRename(string filePath, string newName)
|
||||
{
|
||||
string dirPath = Path.GetDirectoryName(filePath);
|
||||
string destPath;
|
||||
if (Path.HasExtension(filePath))
|
||||
{
|
||||
string extentsion = Path.GetExtension(filePath);
|
||||
destPath = $"{dirPath}/{newName}{extentsion}";
|
||||
}
|
||||
else
|
||||
{
|
||||
destPath = $"{dirPath}/{newName}";
|
||||
}
|
||||
FileInfo fileInfo = new FileInfo(filePath);
|
||||
fileInfo.MoveTo(destPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 文件移动
|
||||
/// </summary>
|
||||
public static void FileMoveTo(string filePath, string destPath)
|
||||
{
|
||||
FileInfo fileInfo = new FileInfo(filePath);
|
||||
fileInfo.MoveTo(destPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 拷贝文件夹
|
||||
/// 注意:包括所有子目录的文件
|
||||
/// </summary>
|
||||
public static void CopyDirectory(string sourcePath, string destPath)
|
||||
{
|
||||
sourcePath = EditorTools.GetRegularPath(sourcePath);
|
||||
|
||||
// If the destination directory doesn't exist, create it.
|
||||
if (Directory.Exists(destPath) == false)
|
||||
Directory.CreateDirectory(destPath);
|
||||
|
||||
string[] fileList = Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories);
|
||||
foreach (string file in fileList)
|
||||
{
|
||||
string temp = EditorTools.GetRegularPath(file);
|
||||
string savePath = temp.Replace(sourcePath, destPath);
|
||||
CopyFile(file, savePath, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 拷贝文件
|
||||
/// </summary>
|
||||
public static void CopyFile(string sourcePath, string destPath, bool overwrite)
|
||||
{
|
||||
if (File.Exists(sourcePath) == false)
|
||||
throw new FileNotFoundException(sourcePath);
|
||||
|
||||
// 创建目录
|
||||
CreateFileDirectory(destPath);
|
||||
|
||||
// 复制文件
|
||||
File.Copy(sourcePath, destPath, overwrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空文件夹
|
||||
/// </summary>
|
||||
/// <param name="folderPath">要清理的文件夹路径</param>
|
||||
public static void ClearFolder(string directoryPath)
|
||||
{
|
||||
if (Directory.Exists(directoryPath) == false)
|
||||
return;
|
||||
|
||||
// 删除文件
|
||||
string[] allFiles = Directory.GetFiles(directoryPath);
|
||||
for (int i = 0; i < allFiles.Length; i++)
|
||||
{
|
||||
File.Delete(allFiles[i]);
|
||||
}
|
||||
|
||||
// 删除文件夹
|
||||
string[] allFolders = Directory.GetDirectories(directoryPath);
|
||||
for (int i = 0; i < allFolders.Length; i++)
|
||||
{
|
||||
Directory.Delete(allFolders[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文件字节大小
|
||||
/// </summary>
|
||||
public static long GetFileSize(string filePath)
|
||||
{
|
||||
FileInfo fileInfo = new FileInfo(filePath);
|
||||
return fileInfo.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取文件的所有文本内容
|
||||
/// </summary>
|
||||
public static string ReadFileAllText(string filePath)
|
||||
{
|
||||
if (File.Exists(filePath) == false)
|
||||
return string.Empty;
|
||||
|
||||
return File.ReadAllText(filePath, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取文本的所有文本内容
|
||||
/// </summary>
|
||||
public static string[] ReadFileAllLine(string filePath)
|
||||
{
|
||||
if (File.Exists(filePath) == false)
|
||||
return null;
|
||||
|
||||
return File.ReadAllLines(filePath, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测AssetBundle文件是否合法
|
||||
/// </summary>
|
||||
public static bool CheckBundleFileValid(byte[] fileData)
|
||||
{
|
||||
string signature = ReadStringToNull(fileData, 20);
|
||||
if (signature == "UnityFS" || signature == "UnityRaw" || signature == "UnityWeb" || signature == "\xFA\xFA\xFA\xFA\xFA\xFA\xFA\xFA")
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
private static string ReadStringToNull(byte[] data, int maxLength)
|
||||
{
|
||||
List<byte> bytes = new List<byte>();
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
if (i >= maxLength)
|
||||
break;
|
||||
|
||||
byte bt = data[i];
|
||||
if (bt == 0)
|
||||
break;
|
||||
|
||||
bytes.Add(bt);
|
||||
}
|
||||
|
||||
if (bytes.Count == 0)
|
||||
return string.Empty;
|
||||
else
|
||||
return Encoding.UTF8.GetString(bytes.ToArray());
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 路径
|
||||
/// <summary>
|
||||
/// 获取规范的路径
|
||||
/// </summary>
|
||||
public static string GetRegularPath(string path)
|
||||
{
|
||||
return path.Replace('\\', '/').Replace("\\", "/"); //替换为Linux路径格式
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取项目工程路径
|
||||
/// </summary>
|
||||
public static string GetProjectPath()
|
||||
{
|
||||
string projectPath = Path.GetDirectoryName(Application.dataPath);
|
||||
return GetRegularPath(projectPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换文件的绝对路径为Unity资源路径
|
||||
/// 例如 D:\\YourPorject\\Assets\\Works\\file.txt 替换为 Assets/Works/file.txt
|
||||
/// </summary>
|
||||
public static string AbsolutePathToAssetPath(string absolutePath)
|
||||
{
|
||||
string content = GetRegularPath(absolutePath);
|
||||
return Substring(content, "Assets", true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换Unity资源路径为文件的绝对路径
|
||||
/// 例如:Assets/Works/file.txt 替换为 D:\\YourPorject/Assets/Works/file.txt
|
||||
/// </summary>
|
||||
public static string AssetPathToAbsolutePath(string assetPath)
|
||||
{
|
||||
string projectPath = GetProjectPath();
|
||||
return $"{projectPath}/{assetPath}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 递归查找目标文件夹路径
|
||||
/// </summary>
|
||||
/// <param name="root">搜索的根目录</param>
|
||||
/// <param name="folderName">目标文件夹名称</param>
|
||||
/// <returns>返回找到的文件夹路径,如果没有找到返回空字符串</returns>
|
||||
public static string FindFolder(string root, string folderName)
|
||||
{
|
||||
DirectoryInfo rootInfo = new DirectoryInfo(root);
|
||||
DirectoryInfo[] infoList = rootInfo.GetDirectories();
|
||||
for (int i = 0; i < infoList.Length; i++)
|
||||
{
|
||||
string fullPath = infoList[i].FullName;
|
||||
if (infoList[i].Name == folderName)
|
||||
return fullPath;
|
||||
|
||||
string result = FindFolder(fullPath, folderName);
|
||||
if (string.IsNullOrEmpty(result) == false)
|
||||
return result;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 字符串
|
||||
/// <summary>
|
||||
/// 是否含有中文
|
||||
/// </summary>
|
||||
public static bool IncludeChinese(string content)
|
||||
{
|
||||
foreach (var c in content)
|
||||
{
|
||||
if (c >= 0x4e00 && c <= 0x9fbb)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否是数字
|
||||
/// </summary>
|
||||
public static bool IsNumber(string content)
|
||||
{
|
||||
if (string.IsNullOrEmpty(content))
|
||||
return false;
|
||||
string pattern = @"^\d*$";
|
||||
return Regex.IsMatch(content, pattern);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 首字母大写
|
||||
/// </summary>
|
||||
public static string Capitalize(string content)
|
||||
{
|
||||
return content.Substring(0, 1).ToUpper() + (content.Length > 1 ? content.Substring(1).ToLower() : "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 截取字符串
|
||||
/// 获取匹配到的后面内容
|
||||
/// </summary>
|
||||
/// <param name="content">内容</param>
|
||||
/// <param name="key">关键字</param>
|
||||
/// <param name="includeKey">分割的结果里是否包含关键字</param>
|
||||
/// <param name="searchBegin">是否使用初始匹配的位置,否则使用末尾匹配的位置</param>
|
||||
public static string Substring(string content, string key, bool includeKey, bool firstMatch = true)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key))
|
||||
return content;
|
||||
|
||||
int startIndex = -1;
|
||||
if (firstMatch)
|
||||
startIndex = content.IndexOf(key); //返回子字符串第一次出现位置
|
||||
else
|
||||
startIndex = content.LastIndexOf(key); //返回子字符串最后出现的位置
|
||||
|
||||
// 如果没有找到匹配的关键字
|
||||
if (startIndex == -1)
|
||||
return content;
|
||||
|
||||
if (includeKey)
|
||||
return content.Substring(startIndex);
|
||||
else
|
||||
return content.Substring(startIndex + key.Length);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 玩家偏好
|
||||
// 枚举
|
||||
public static void PlayerSetEnum<T>(string key, T value)
|
||||
{
|
||||
string enumName = value.ToString();
|
||||
EditorPrefs.SetString(key, enumName);
|
||||
}
|
||||
public static T PlayerGetEnum<T>(string key, T defaultValue)
|
||||
{
|
||||
string enumName = EditorPrefs.GetString(key, defaultValue.ToString());
|
||||
return StringUtility.NameToEnum<T>(enumName);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d8cbaf07016a5d045a8f463042a64c25
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dcb9955c15609744a9666bd76f6af3d9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,112 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEditor;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
public static class ShaderVariantCollectionHelper
|
||||
{
|
||||
[Serializable]
|
||||
public class ShaderVariantWrapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Shader asset path in editor.
|
||||
/// </summary>
|
||||
public string AssetPath;
|
||||
|
||||
/// <summary>
|
||||
/// Shader name.
|
||||
/// </summary>
|
||||
public string ShaderName;
|
||||
|
||||
/// <summary>
|
||||
/// Pass type to use in this variant.
|
||||
/// </summary>
|
||||
public PassType PassType;
|
||||
|
||||
/// <summary>
|
||||
/// Array of shader keywords to use in this variant.
|
||||
/// </summary>
|
||||
public string[] Keywords;
|
||||
|
||||
public ShaderVariantWrapper(string assetPath, string shaderName, PassType passType, params string[] keywords)
|
||||
{
|
||||
AssetPath = assetPath;
|
||||
ShaderName = shaderName;
|
||||
PassType = passType;
|
||||
Keywords = keywords;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ShaderVariantCollectionWrapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Number of shaders in this collection
|
||||
/// </summary>
|
||||
public int ShaderCount;
|
||||
|
||||
/// <summary>
|
||||
/// Number of total varians in this collection
|
||||
/// </summary>
|
||||
public int VariantCount;
|
||||
|
||||
/// <summary>
|
||||
/// Shader variants list.
|
||||
/// </summary>
|
||||
public List<ShaderVariantWrapper> ShaderVariants = new List<ShaderVariantWrapper>(1000);
|
||||
|
||||
public void Add(ShaderVariantWrapper variant)
|
||||
{
|
||||
ShaderVariants.Add(variant);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static ShaderVariantCollectionWrapper Extract(ShaderVariantCollection svc)
|
||||
{
|
||||
var result = new ShaderVariantCollectionWrapper();
|
||||
using (var so = new SerializedObject(svc))
|
||||
{
|
||||
var shaderArray = so.FindProperty("m_Shaders.Array");
|
||||
if (shaderArray != null && shaderArray.isArray)
|
||||
{
|
||||
for (int i = 0; i < shaderArray.arraySize; ++i)
|
||||
{
|
||||
var shaderRef = shaderArray.FindPropertyRelative($"data[{i}].first");
|
||||
var shaderVariantsArray = shaderArray.FindPropertyRelative($"data[{i}].second.variants");
|
||||
if (shaderRef != null && shaderRef.propertyType == SerializedPropertyType.ObjectReference && shaderVariantsArray != null && shaderVariantsArray.isArray)
|
||||
{
|
||||
var shader = shaderRef.objectReferenceValue as Shader;
|
||||
if (shader == null)
|
||||
{
|
||||
throw new Exception("Invalid shader in ShaderVariantCollection file.");
|
||||
}
|
||||
|
||||
string shaderAssetPath = AssetDatabase.GetAssetPath(shader);
|
||||
string shaderName = shader.name;
|
||||
|
||||
// 添加变种信息
|
||||
for (int j = 0; j < shaderVariantsArray.arraySize; ++j)
|
||||
{
|
||||
var propKeywords = shaderVariantsArray.FindPropertyRelative($"Array.data[{j}].keywords");
|
||||
var propPassType = shaderVariantsArray.FindPropertyRelative($"Array.data[{j}].passType");
|
||||
if (propKeywords != null && propPassType != null && propKeywords.propertyType == SerializedPropertyType.String)
|
||||
{
|
||||
string[] keywords = propKeywords.stringValue.Split(' ');
|
||||
PassType pathType = (PassType)propPassType.intValue;
|
||||
result.Add(new ShaderVariantWrapper(shaderAssetPath, shaderName, pathType, keywords));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ab74d4ff4a2805147883de70a1559a0a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,213 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
public static class ShaderVariantCollector
|
||||
{
|
||||
private const float WaitMilliseconds = 1000f;
|
||||
private static string _saveFilePath;
|
||||
private static bool _isStarted = false;
|
||||
private static readonly Stopwatch _elapsedTime = new Stopwatch();
|
||||
|
||||
static ShaderVariantCollector()
|
||||
{
|
||||
EditorApplication.update += EditorUpdate;
|
||||
}
|
||||
private static void EditorUpdate()
|
||||
{
|
||||
// 注意:一定要延迟保存才会起效
|
||||
if (_isStarted && _elapsedTime.ElapsedMilliseconds > WaitMilliseconds)
|
||||
{
|
||||
_isStarted = false;
|
||||
_elapsedTime.Stop();
|
||||
|
||||
// 保存结果
|
||||
SaveCurrentShaderVariantCollection();
|
||||
|
||||
// 创建说明文件
|
||||
CreateReadme();
|
||||
}
|
||||
}
|
||||
private static void CreateReadme()
|
||||
{
|
||||
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
|
||||
|
||||
ShaderVariantCollection svc = AssetDatabase.LoadAssetAtPath<ShaderVariantCollection>(_saveFilePath);
|
||||
if(svc != null)
|
||||
{
|
||||
var wrapper = ShaderVariantCollectionHelper.Extract(svc);
|
||||
string jsonContents = JsonUtility.ToJson(wrapper, true);
|
||||
string savePath = _saveFilePath.Replace(".shadervariants", ".json");
|
||||
File.WriteAllText(savePath, jsonContents);
|
||||
}
|
||||
|
||||
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始收集
|
||||
/// </summary>
|
||||
public static void Run(string saveFilePath)
|
||||
{
|
||||
if (_isStarted)
|
||||
return;
|
||||
|
||||
if (Path.HasExtension(saveFilePath) == false)
|
||||
saveFilePath = $"{saveFilePath}.shadervariants";
|
||||
if (Path.GetExtension(saveFilePath) != ".shadervariants")
|
||||
throw new System.Exception("Shader variant file extension is invalid.");
|
||||
EditorTools.CreateFileDirectory(saveFilePath);
|
||||
_saveFilePath = saveFilePath;
|
||||
|
||||
// 聚焦到游戏窗口
|
||||
EditorTools.FocusUnityGameWindow();
|
||||
|
||||
// 清空旧数据
|
||||
ClearCurrentShaderVariantCollection();
|
||||
|
||||
// 创建临时测试场景
|
||||
CreateTemperScene();
|
||||
|
||||
// 收集着色器变种
|
||||
var materials = GetAllMaterials();
|
||||
CollectVariants(materials);
|
||||
|
||||
_isStarted = true;
|
||||
_elapsedTime.Reset();
|
||||
_elapsedTime.Start();
|
||||
|
||||
UnityEngine.Debug.LogWarning("已经启动着色器变种收集工作,该工具只支持在编辑器下人工操作!");
|
||||
}
|
||||
|
||||
private static void CreateTemperScene()
|
||||
{
|
||||
// 创建临时场景
|
||||
EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects);
|
||||
}
|
||||
private static List<Material> GetAllMaterials()
|
||||
{
|
||||
int progressValue = 0;
|
||||
List<string> allAssets = new List<string>(1000);
|
||||
|
||||
// 获取所有打包的资源
|
||||
List<AssetCollectInfo> allCollectInfos = AssetBundleCollectorSettingData.GetAllCollectAssets();
|
||||
List<string> collectAssets = allCollectInfos.Select(t => t.AssetPath).ToList();
|
||||
foreach (var assetPath in collectAssets)
|
||||
{
|
||||
string[] depends = AssetDatabase.GetDependencies(assetPath, true);
|
||||
foreach (var depend in depends)
|
||||
{
|
||||
if (allAssets.Contains(depend) == false)
|
||||
allAssets.Add(depend);
|
||||
}
|
||||
EditorTools.DisplayProgressBar("获取所有打包资源", ++progressValue, collectAssets.Count);
|
||||
}
|
||||
EditorTools.ClearProgressBar();
|
||||
|
||||
// 搜集所有材质球
|
||||
progressValue = 0;
|
||||
var shaderDic = new Dictionary<Shader, List<Material>>(100);
|
||||
foreach (var assetPath in allAssets)
|
||||
{
|
||||
System.Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
|
||||
if (assetType == typeof(UnityEngine.Material))
|
||||
{
|
||||
var material = AssetDatabase.LoadAssetAtPath<Material>(assetPath);
|
||||
var shader = material.shader;
|
||||
if (shader == null)
|
||||
continue;
|
||||
|
||||
if (shaderDic.ContainsKey(shader) == false)
|
||||
{
|
||||
shaderDic.Add(shader, new List<Material>());
|
||||
}
|
||||
if (shaderDic[shader].Contains(material) == false)
|
||||
{
|
||||
shaderDic[shader].Add(material);
|
||||
}
|
||||
}
|
||||
EditorTools.DisplayProgressBar("搜集所有材质球", ++progressValue, allAssets.Count);
|
||||
}
|
||||
EditorTools.ClearProgressBar();
|
||||
|
||||
// 返回结果
|
||||
var materials = new List<Material>(1000);
|
||||
foreach (var valuePair in shaderDic)
|
||||
{
|
||||
materials.AddRange(valuePair.Value);
|
||||
}
|
||||
return materials;
|
||||
}
|
||||
private static void CollectVariants(List<Material> materials)
|
||||
{
|
||||
Camera camera = Camera.main;
|
||||
if(camera == null)
|
||||
throw new System.Exception("Not found main camera.");
|
||||
|
||||
// 设置主相机
|
||||
float aspect = camera.aspect;
|
||||
int totalMaterials = materials.Count;
|
||||
float height = Mathf.Sqrt(totalMaterials / aspect) + 1;
|
||||
float width = Mathf.Sqrt(totalMaterials / aspect) * aspect + 1;
|
||||
float halfHeight = Mathf.CeilToInt(height / 2f);
|
||||
float halfWidth = Mathf.CeilToInt(width / 2f);
|
||||
camera.orthographic = true;
|
||||
camera.orthographicSize = halfHeight;
|
||||
camera.transform.position = new Vector3(0f, 0f, -10f);
|
||||
|
||||
// 创建测试球体
|
||||
int xMax = (int)(width - 1);
|
||||
int x = 0, y = 0;
|
||||
int progressValue = 0;
|
||||
for (int i = 0; i < materials.Count; i++)
|
||||
{
|
||||
var material = materials[i];
|
||||
var position = new Vector3(x - halfWidth + 1f, y - halfHeight + 1f, 0f);
|
||||
CreateSphere(material, position, i);
|
||||
if (x == xMax)
|
||||
{
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
else
|
||||
{
|
||||
x++;
|
||||
}
|
||||
EditorTools.DisplayProgressBar("测试所有材质球", ++progressValue, materials.Count);
|
||||
}
|
||||
EditorTools.ClearProgressBar();
|
||||
}
|
||||
private static void CreateSphere(Material material, Vector3 position, int index)
|
||||
{
|
||||
var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
||||
go.GetComponent<Renderer>().material = material;
|
||||
go.transform.position = position;
|
||||
go.name = $"Sphere_{index}|{material.name}";
|
||||
}
|
||||
|
||||
private static void ClearCurrentShaderVariantCollection()
|
||||
{
|
||||
EditorTools.InvokeNonPublicStaticMethod(typeof(ShaderUtil), "ClearCurrentShaderVariantCollection");
|
||||
}
|
||||
private static void SaveCurrentShaderVariantCollection()
|
||||
{
|
||||
EditorTools.InvokeNonPublicStaticMethod(typeof(ShaderUtil), "SaveCurrentShaderVariantCollection", _saveFilePath);
|
||||
}
|
||||
public static int GetCurrentShaderVariantCollectionShaderCount()
|
||||
{
|
||||
return (int)EditorTools.InvokeNonPublicStaticMethod(typeof(ShaderUtil), "GetCurrentShaderVariantCollectionShaderCount");
|
||||
}
|
||||
public static int GetCurrentShaderVariantCollectionVariantCount()
|
||||
{
|
||||
return (int)EditorTools.InvokeNonPublicStaticMethod(typeof(ShaderUtil), "GetCurrentShaderVariantCollectionVariantCount");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 21b4cc6bf4c0c064d8e2687024e24c86
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,58 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
public class ShaderVariantCollectionWindow : EditorWindow
|
||||
{
|
||||
static ShaderVariantCollectionWindow _thisInstance;
|
||||
|
||||
[MenuItem("YooAsset/ShaderVariant Collector", false, 203)]
|
||||
static void ShowWindow()
|
||||
{
|
||||
if (_thisInstance == null)
|
||||
{
|
||||
_thisInstance = EditorWindow.GetWindow(typeof(ShaderVariantCollectionWindow), false, "着色器变种收集工具", true) as ShaderVariantCollectionWindow;
|
||||
_thisInstance.minSize = new Vector2(800, 600);
|
||||
}
|
||||
_thisInstance.Show();
|
||||
}
|
||||
|
||||
private string _saveFilePath = "Assets/MyShaderVariants.shadervariants";
|
||||
private ShaderVariantCollection _selectSVC;
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
_saveFilePath = EditorGUILayout.TextField("收集文件保存路径", _saveFilePath);
|
||||
|
||||
int currentShaderCount = ShaderVariantCollector.GetCurrentShaderVariantCollectionShaderCount();
|
||||
int currentVariantCount = ShaderVariantCollector.GetCurrentShaderVariantCollectionVariantCount();
|
||||
EditorGUILayout.LabelField($"CurrentShaderCount : {currentShaderCount}");
|
||||
EditorGUILayout.LabelField($"CurrentVariantCount : {currentVariantCount}");
|
||||
|
||||
// 搜集变种
|
||||
EditorGUILayout.Space();
|
||||
if (GUILayout.Button("搜集变种", GUILayout.MaxWidth(80)))
|
||||
{
|
||||
ShaderVariantCollector.Run(_saveFilePath);
|
||||
}
|
||||
|
||||
// 查询
|
||||
EditorGUILayout.Space();
|
||||
if (GUILayout.Button("查询", GUILayout.MaxWidth(80)))
|
||||
{
|
||||
string resultPath = EditorTools.OpenFilePath("Select File", "Assets/", "shadervariants");
|
||||
if (string.IsNullOrEmpty(resultPath))
|
||||
return;
|
||||
string assetPath = EditorTools.AbsolutePathToAssetPath(resultPath);
|
||||
_selectSVC = AssetDatabase.LoadAssetAtPath<ShaderVariantCollection>(assetPath);
|
||||
}
|
||||
if (_selectSVC != null)
|
||||
{
|
||||
EditorGUILayout.LabelField($"ShaderCount : {_selectSVC.shaderCount}");
|
||||
EditorGUILayout.LabelField($"VariantCount : {_selectSVC.variantCount}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 70401cc80b9807e46bd8283e01b4302f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "YooAsset.Editor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"YooAsset"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4d1926c9df5b052469a1c63448b7609a
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,6 +1,7 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 tuyoogame
|
||||
Copyright (c) 2018-2021 何冠峰
|
||||
Copyright (c) 2021-2022 TuYoo Games
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 685cb35952719f9499a0cdb255604729
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,2 @@
|
|||
# YooAsset
|
||||
Unity assets system
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cf18b110959e74849b675ca29f77c7dd
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 40472229e23f3314f9a7123ad2d6e1e4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8e6b180f448258247a506a5d0ea4922e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,149 @@
|
|||
|
||||
namespace YooAsset
|
||||
{
|
||||
public class AssetBundleInfo
|
||||
{
|
||||
private readonly PatchBundle _patchBundle;
|
||||
|
||||
/// <summary>
|
||||
/// 资源包名称
|
||||
/// </summary>
|
||||
public string BundleName { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 本地存储的路径
|
||||
/// </summary>
|
||||
public string LocalPath { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 远端下载地址
|
||||
/// </summary>
|
||||
public string RemoteMainURL { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 远端下载备用地址
|
||||
/// </summary>
|
||||
public string RemoteFallbackURL { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件哈希值
|
||||
/// </summary>
|
||||
public string Hash
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_patchBundle == null)
|
||||
return string.Empty;
|
||||
else
|
||||
return _patchBundle.Hash;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 校验的CRC
|
||||
/// </summary>
|
||||
public string CRC
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_patchBundle == null)
|
||||
return string.Empty;
|
||||
else
|
||||
return _patchBundle.CRC;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 文件大小
|
||||
/// </summary>
|
||||
public long SizeBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_patchBundle == null)
|
||||
return 0;
|
||||
else
|
||||
return _patchBundle.SizeBytes;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源版本
|
||||
/// </summary>
|
||||
public int Version
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_patchBundle == null)
|
||||
return 0;
|
||||
else
|
||||
return _patchBundle.Version;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否为加密文件
|
||||
/// </summary>
|
||||
public bool IsEncrypted
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_patchBundle == null)
|
||||
return false;
|
||||
else
|
||||
return _patchBundle.IsEncrypted;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否为原生文件
|
||||
/// </summary>
|
||||
public bool IsRawFile
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_patchBundle == null)
|
||||
return false;
|
||||
else
|
||||
return _patchBundle.IsRawFile;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private AssetBundleInfo()
|
||||
{
|
||||
}
|
||||
internal AssetBundleInfo(PatchBundle patchBundle, string localPath, string mainURL, string fallbackURL)
|
||||
{
|
||||
_patchBundle = patchBundle;
|
||||
BundleName = patchBundle.BundleName;
|
||||
LocalPath = localPath;
|
||||
RemoteMainURL = mainURL;
|
||||
RemoteFallbackURL = fallbackURL;
|
||||
}
|
||||
internal AssetBundleInfo(PatchBundle patchBundle, string localPath)
|
||||
{
|
||||
_patchBundle = patchBundle;
|
||||
BundleName = patchBundle.BundleName;
|
||||
LocalPath = localPath;
|
||||
RemoteMainURL = string.Empty;
|
||||
RemoteFallbackURL = string.Empty;
|
||||
}
|
||||
internal AssetBundleInfo(string bundleName, string localPath)
|
||||
{
|
||||
_patchBundle = null;
|
||||
BundleName = bundleName;
|
||||
LocalPath = localPath;
|
||||
RemoteMainURL = string.Empty;
|
||||
RemoteFallbackURL = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否为JAR包内文件
|
||||
/// </summary>
|
||||
public bool IsBuildinJarFile()
|
||||
{
|
||||
return LocalPath.StartsWith("jar:");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2939a2b9862f05545969cc5fcb378bd8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,185 @@
|
|||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
public struct AssetOperationHandle : IEnumerator
|
||||
{
|
||||
private IAssetProvider _provider;
|
||||
|
||||
internal AssetOperationHandle(IAssetProvider provider)
|
||||
{
|
||||
_provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 句柄是否有效(AssetFileLoader销毁会导致所有句柄失效)
|
||||
/// </summary>
|
||||
public bool IsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
return _provider != null && _provider.IsValid;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前的加载状态
|
||||
/// </summary>
|
||||
public EAssetStates States
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsValid == false)
|
||||
return EAssetStates.None;
|
||||
return _provider.States;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载进度
|
||||
/// </summary>
|
||||
public float Progress
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsValid == false)
|
||||
return 0;
|
||||
return _provider.Progress;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否加载完毕
|
||||
/// </summary>
|
||||
public bool IsDone
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsValid == false)
|
||||
return false;
|
||||
return _provider.IsDone;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 完成委托
|
||||
/// </summary>
|
||||
public event System.Action<AssetOperationHandle> Completed
|
||||
{
|
||||
add
|
||||
{
|
||||
if (IsValid == false)
|
||||
throw new System.Exception($"{nameof(AssetOperationHandle)} is invalid");
|
||||
if (_provider.IsDone)
|
||||
value.Invoke(this);
|
||||
else
|
||||
_provider.Callback += value;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (IsValid == false)
|
||||
throw new System.Exception($"{nameof(AssetOperationHandle)} is invalid");
|
||||
_provider.Callback -= value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源对象
|
||||
/// </summary>
|
||||
public UnityEngine.Object AssetObject
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsValid == false)
|
||||
return null;
|
||||
return _provider.AssetObject;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源对象集合
|
||||
/// </summary>
|
||||
public UnityEngine.Object[] AllAssets
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsValid == false)
|
||||
return null;
|
||||
return _provider.AllAssets;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 扩展的实例对象
|
||||
/// </summary>
|
||||
public IAssetInstance AssetInstance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsValid == false)
|
||||
return null;
|
||||
return _provider.AssetInstance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化的游戏对象(只限于请求的资源对象类型为GameObject)
|
||||
/// </summary>
|
||||
public GameObject InstantiateObject
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsValid == false)
|
||||
return null;
|
||||
if (_provider.AssetObject == null)
|
||||
return null;
|
||||
return UnityEngine.Object.Instantiate(_provider.AssetObject as GameObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放资源句柄
|
||||
/// </summary>
|
||||
public void Release()
|
||||
{
|
||||
if (IsValid == false)
|
||||
return;
|
||||
_provider.Release();
|
||||
_provider = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 等待异步执行完毕
|
||||
/// </summary>
|
||||
public void WaitForAsyncComplete()
|
||||
{
|
||||
if (IsValid == false)
|
||||
return;
|
||||
_provider.WaitForAsyncComplete();
|
||||
}
|
||||
|
||||
#region 异步操作相关
|
||||
/// <summary>
|
||||
/// 异步操作任务
|
||||
/// </summary>
|
||||
public System.Threading.Tasks.Task<object> Task
|
||||
{
|
||||
get { return _provider.Task; }
|
||||
}
|
||||
|
||||
// 协程相关
|
||||
bool IEnumerator.MoveNext()
|
||||
{
|
||||
return !IsDone;
|
||||
}
|
||||
void IEnumerator.Reset()
|
||||
{
|
||||
}
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get { return AssetObject; }
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6b766125ab790154ca132adb5c77333d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,123 @@
|
|||
using System.IO;
|
||||
using YooAsset.Utility;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
internal static class AssetPathHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取规范化的路径
|
||||
/// </summary>
|
||||
public static string GetRegularPath(string path)
|
||||
{
|
||||
return path.Replace('\\', '/').Replace("\\", "/"); //替换为Linux路径格式
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文件所在的目录路径(Linux格式)
|
||||
/// </summary>
|
||||
public static string GetDirectory(string filePath)
|
||||
{
|
||||
string directory = Path.GetDirectoryName(filePath);
|
||||
return GetRegularPath(directory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取基于流文件夹的加载路径
|
||||
/// </summary>
|
||||
public static string MakeStreamingLoadPath(string path)
|
||||
{
|
||||
return StringUtility.Format("{0}/{1}", UnityEngine.Application.streamingAssetsPath, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取基于沙盒文件夹的加载路径
|
||||
/// </summary>
|
||||
public static string MakePersistentLoadPath(string path)
|
||||
{
|
||||
string root = MakePersistentRootPath();
|
||||
return StringUtility.Format("{0}/{1}", root, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取沙盒文件夹路径
|
||||
/// </summary>
|
||||
public static string MakePersistentRootPath()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// 注意:为了方便调试查看,编辑器下把存储目录放到项目里
|
||||
string projectPath = GetDirectory(UnityEngine.Application.dataPath);
|
||||
return StringUtility.Format("{0}/Sandbox", projectPath);
|
||||
#else
|
||||
return StringUtility.Format("{0}/Sandbox", UnityEngine.Application.persistentDataPath);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取网络资源加载路径
|
||||
/// </summary>
|
||||
public static string ConvertToWWWPath(string path)
|
||||
{
|
||||
// 注意:WWW加载方式,必须要在路径前面加file://
|
||||
#if UNITY_EDITOR
|
||||
return StringUtility.Format("file:///{0}", path);
|
||||
#elif UNITY_IPHONE
|
||||
return StringUtility.Format("file://{0}", path);
|
||||
#elif UNITY_ANDROID
|
||||
return path;
|
||||
#elif UNITY_STANDALONE
|
||||
return StringUtility.Format("file:///{0}", path);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 合并资源路径
|
||||
/// </summary>
|
||||
internal static string CombineAssetPath(string root, string location)
|
||||
{
|
||||
if (string.IsNullOrEmpty(root))
|
||||
return location;
|
||||
else
|
||||
return $"{root}/{location}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取AssetDatabase的加载路径
|
||||
/// </summary>
|
||||
internal static string FindDatabaseAssetPath(string filePath)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (File.Exists(filePath))
|
||||
return filePath;
|
||||
|
||||
// AssetDatabase加载资源需要提供文件后缀格式,然而资源定位地址并没有文件格式信息。
|
||||
// 所以我们通过查找该文件所在文件夹内同名的首个文件来确定AssetDatabase的加载路径。
|
||||
// 注意:AssetDatabase.FindAssets() 返回文件内包括递归文件夹内所有资源的GUID
|
||||
string fileName = Path.GetFileName(filePath);
|
||||
string directory = GetDirectory(filePath);
|
||||
string[] guids = UnityEditor.AssetDatabase.FindAssets(string.Empty, new[] { directory });
|
||||
for (int i = 0; i < guids.Length; i++)
|
||||
{
|
||||
string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(guids[i]);
|
||||
|
||||
if (UnityEditor.AssetDatabase.IsValidFolder(assetPath))
|
||||
continue;
|
||||
|
||||
string assetDirectory = GetDirectory(assetPath);
|
||||
if (assetDirectory != directory)
|
||||
continue;
|
||||
|
||||
string assetName = Path.GetFileNameWithoutExtension(assetPath);
|
||||
if (assetName == fileName)
|
||||
return assetPath;
|
||||
}
|
||||
|
||||
// 没有找到同名的资源文件
|
||||
Logger.Warning($"Not found asset : {filePath}");
|
||||
return filePath;
|
||||
#else
|
||||
throw new System.NotImplementedException();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9581e7c53fe081749ab849ec6e2be3d6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,326 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
internal static class AssetSystem
|
||||
{
|
||||
private static readonly List<BundleFileLoader> _loaders = new List<BundleFileLoader>(1000);
|
||||
private static readonly List<AssetProviderBase> _providers = new List<AssetProviderBase>(1000);
|
||||
|
||||
/// <summary>
|
||||
/// 在编辑器下模拟运行
|
||||
/// </summary>
|
||||
public static bool SimulationOnEditor { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 运行时的最大加载个数
|
||||
/// </summary>
|
||||
public static int AssetLoadingMaxNumber { private set; get; }
|
||||
|
||||
public static IDecryptServices DecryptServices { private set; get; }
|
||||
public static IBundleServices BundleServices { private set; get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 初始化资源系统
|
||||
/// 注意:在使用AssetSystem之前需要初始化
|
||||
/// </summary>
|
||||
public static void Initialize(bool simulationOnEditor, int assetLoadingMaxNumber, IDecryptServices decryptServices, IBundleServices bundleServices)
|
||||
{
|
||||
SimulationOnEditor = simulationOnEditor;
|
||||
AssetLoadingMaxNumber = assetLoadingMaxNumber;
|
||||
DecryptServices = decryptServices;
|
||||
BundleServices = bundleServices;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轮询更新
|
||||
/// </summary>
|
||||
public static void UpdatePoll()
|
||||
{
|
||||
// 更新加载器
|
||||
foreach (var loader in _loaders)
|
||||
{
|
||||
loader.Update();
|
||||
}
|
||||
|
||||
// 更新资源提供者
|
||||
// 注意:循环更新的时候,可能会扩展列表
|
||||
// 注意:不能限制场景对象的加载
|
||||
int loadingCount = 0;
|
||||
for (int i = 0; i < _providers.Count; i++)
|
||||
{
|
||||
var provider = _providers[i];
|
||||
if (provider.IsSceneProvider())
|
||||
{
|
||||
provider.Update();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (loadingCount < AssetLoadingMaxNumber)
|
||||
provider.Update();
|
||||
|
||||
if (provider.IsDone == false)
|
||||
loadingCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// 注意:需要立刻卸载场景
|
||||
if (SimulationOnEditor)
|
||||
{
|
||||
for (int i = _providers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
AssetProviderBase provider = _providers[i];
|
||||
if (provider.IsSceneProvider() && provider.CanDestroy())
|
||||
{
|
||||
provider.Destory();
|
||||
_providers.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = _loaders.Count - 1; i >= 0; i--)
|
||||
{
|
||||
BundleFileLoader loader = _loaders[i];
|
||||
if (loader.IsSceneLoader())
|
||||
{
|
||||
loader.TryDestroyAllProviders();
|
||||
}
|
||||
}
|
||||
for (int i = _loaders.Count - 1; i >= 0; i--)
|
||||
{
|
||||
BundleFileLoader loader = _loaders[i];
|
||||
if (loader.IsSceneLoader() && loader.CanDestroy())
|
||||
{
|
||||
loader.Destroy(false);
|
||||
_loaders.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源回收(卸载引用计数为零的资源)
|
||||
/// </summary>
|
||||
public static void UnloadUnusedAssets()
|
||||
{
|
||||
if (SimulationOnEditor)
|
||||
{
|
||||
for (int i = _providers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_providers[i].CanDestroy())
|
||||
{
|
||||
_providers[i].Destory();
|
||||
_providers.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = _loaders.Count - 1; i >= 0; i--)
|
||||
{
|
||||
BundleFileLoader loader = _loaders[i];
|
||||
loader.TryDestroyAllProviders();
|
||||
}
|
||||
for (int i = _loaders.Count - 1; i >= 0; i--)
|
||||
{
|
||||
BundleFileLoader loader = _loaders[i];
|
||||
if (loader.CanDestroy())
|
||||
{
|
||||
loader.Destroy(false);
|
||||
_loaders.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 强制回收所有资源
|
||||
/// </summary>
|
||||
public static void ForceUnloadAllAssets()
|
||||
{
|
||||
foreach (var provider in _providers)
|
||||
{
|
||||
provider.Destory();
|
||||
}
|
||||
_providers.Clear();
|
||||
|
||||
foreach (var loader in _loaders)
|
||||
{
|
||||
loader.Destroy(true);
|
||||
}
|
||||
_loaders.Clear();
|
||||
|
||||
// 注意:调用底层接口释放所有资源
|
||||
Resources.UnloadUnusedAssets();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步加载场景
|
||||
/// </summary>
|
||||
/// <param name="scenePath">场景名称</param>
|
||||
public static AssetOperationHandle LoadSceneAsync(string scenePath, SceneInstanceParam instanceParam)
|
||||
{
|
||||
AssetProviderBase provider = TryGetAssetProvider(scenePath);
|
||||
if (provider == null)
|
||||
{
|
||||
if (SimulationOnEditor)
|
||||
provider = new DatabaseSceneProvider(scenePath, instanceParam);
|
||||
else
|
||||
provider = new BundledSceneProvider(scenePath, instanceParam);
|
||||
_providers.Add(provider);
|
||||
}
|
||||
|
||||
// 引用计数增加
|
||||
provider.Reference();
|
||||
return provider.Handle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步加载资源对象
|
||||
/// </summary>
|
||||
/// <param name="assetPath">资源路径</param>
|
||||
/// <param name="assetType">资源类型</param>
|
||||
public static AssetOperationHandle LoadAssetAsync(string assetPath, System.Type assetType)
|
||||
{
|
||||
AssetProviderBase provider = TryGetAssetProvider(assetPath);
|
||||
if (provider == null)
|
||||
{
|
||||
if (SimulationOnEditor)
|
||||
provider = new DatabaseAssetProvider(assetPath, assetType);
|
||||
else
|
||||
provider = new BundledAssetProvider(assetPath, assetType);
|
||||
_providers.Add(provider);
|
||||
}
|
||||
|
||||
// 引用计数增加
|
||||
provider.Reference();
|
||||
return provider.Handle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步加载所有子资源对象
|
||||
/// </summary>
|
||||
/// <param name="assetPath">资源路径</param>
|
||||
/// <param name="assetType">资源类型</param>、
|
||||
public static AssetOperationHandle LoadSubAssetsAsync(string assetPath, System.Type assetType)
|
||||
{
|
||||
AssetProviderBase provider = TryGetAssetProvider(assetPath);
|
||||
if (provider == null)
|
||||
{
|
||||
if (SimulationOnEditor)
|
||||
provider = new DatabaseSubAssetsProvider(assetPath, assetType);
|
||||
else
|
||||
provider = new BundledSubAssetsProvider(assetPath, assetType);
|
||||
_providers.Add(provider);
|
||||
}
|
||||
|
||||
// 引用计数增加
|
||||
provider.Reference();
|
||||
return provider.Handle;
|
||||
}
|
||||
|
||||
|
||||
internal static BundleFileLoader CreateOwnerBundleLoader(string assetPath)
|
||||
{
|
||||
string bundleName = BundleServices.GetAssetBundleName(assetPath);
|
||||
AssetBundleInfo bundleInfo = BundleServices.GetAssetBundleInfo(bundleName);
|
||||
return CreateBundleFileLoaderInternal(bundleInfo);
|
||||
}
|
||||
internal static List<BundleFileLoader> CreateDependBundleLoaders(string assetPath)
|
||||
{
|
||||
List<BundleFileLoader> result = new List<BundleFileLoader>();
|
||||
string[] depends = BundleServices.GetAllDependencies(assetPath);
|
||||
if (depends != null)
|
||||
{
|
||||
foreach (var dependBundleName in depends)
|
||||
{
|
||||
AssetBundleInfo dependBundleInfo = BundleServices.GetAssetBundleInfo(dependBundleName);
|
||||
BundleFileLoader dependLoader = CreateBundleFileLoaderInternal(dependBundleInfo);
|
||||
result.Add(dependLoader);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
internal static void RemoveBundleProviders(List<AssetProviderBase> providers)
|
||||
{
|
||||
foreach (var provider in providers)
|
||||
{
|
||||
_providers.Remove(provider);
|
||||
}
|
||||
}
|
||||
|
||||
private static BundleFileLoader CreateBundleFileLoaderInternal(AssetBundleInfo bundleInfo)
|
||||
{
|
||||
// 如果加载器已经存在
|
||||
BundleFileLoader loader = TryGetBundleFileLoader(bundleInfo.BundleName);
|
||||
if (loader != null)
|
||||
return loader;
|
||||
|
||||
// 新增下载需求
|
||||
loader = new BundleFileLoader(bundleInfo);
|
||||
_loaders.Add(loader);
|
||||
return loader;
|
||||
}
|
||||
private static BundleFileLoader TryGetBundleFileLoader(string bundleName)
|
||||
{
|
||||
BundleFileLoader loader = null;
|
||||
for (int i = 0; i < _loaders.Count; i++)
|
||||
{
|
||||
BundleFileLoader temp = _loaders[i];
|
||||
if (temp.BundleInfo.BundleName.Equals(bundleName))
|
||||
{
|
||||
loader = temp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return loader;
|
||||
}
|
||||
private static AssetProviderBase TryGetAssetProvider(string assetPath)
|
||||
{
|
||||
AssetProviderBase provider = null;
|
||||
for (int i = 0; i < _providers.Count; i++)
|
||||
{
|
||||
AssetProviderBase temp = _providers[i];
|
||||
if (temp.AssetPath.Equals(assetPath))
|
||||
{
|
||||
provider = temp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
#region 调试专属方法
|
||||
internal static void GetDebugSummy(DebugSummy summy)
|
||||
{
|
||||
summy.ClearAll();
|
||||
summy.BundleCount = _loaders.Count;
|
||||
summy.AssetCount = _providers.Count;
|
||||
|
||||
foreach (var provider in _providers)
|
||||
{
|
||||
DebugSummy.ProviderInfo providerInfo = new DebugSummy.ProviderInfo();
|
||||
providerInfo.AssetPath = provider.AssetPath;
|
||||
providerInfo.RefCount = provider.RefCount;
|
||||
providerInfo.States = provider.States;
|
||||
providerInfo.BundleInfos.Clear();
|
||||
summy.ProviderInfos.Add(providerInfo);
|
||||
|
||||
if (provider is BundledProvider)
|
||||
{
|
||||
BundledProvider temp = provider as BundledProvider;
|
||||
temp.GetBundleDebugInfos(providerInfo.BundleInfos);
|
||||
}
|
||||
}
|
||||
|
||||
// 重新排序
|
||||
summy.ProviderInfos.Sort();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3ecc5913758e8234fbba5ef2fd192f86
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 解密方法
|
||||
/// </summary>
|
||||
public enum EDecryptMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取解密的数据偏移
|
||||
/// </summary>
|
||||
GetDecryptOffset,
|
||||
|
||||
/// <summary>
|
||||
/// 获取解密的字节数据
|
||||
/// </summary>
|
||||
GetDecryptBinary,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6a446add12cd4554a9bd178a7ef2db55
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
namespace YooAsset
|
||||
{
|
||||
public interface IBundleServices
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取AssetBundle的信息
|
||||
/// </summary>
|
||||
AssetBundleInfo GetAssetBundleInfo(string bundleName);
|
||||
|
||||
/// <summary>
|
||||
/// 获取资源所属的资源包名称
|
||||
/// </summary>
|
||||
string GetAssetBundleName(string assetPath);
|
||||
|
||||
/// <summary>
|
||||
/// 获取资源依赖的所有AssetBundle列表
|
||||
/// </summary>
|
||||
string[] GetAllDependencies(string assetPath);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 70420213c551a2b4b8cf014067699b07
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue