Files
TEngine/Assets/TEngine/Scripts/Editor/AssetBundleBuild/AssetbundleBuilder.cs
ALEXTANG 1edbfcc086 升级HybridCLR 1.1.20
升级HybridCLR 1.1.20
2023-02-10 12:06:15 +08:00

1941 lines
68 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using TEngine.Runtime;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;
namespace TEngineCore.Editor
{
public class AssetbundleBuilder
{
internal struct AssetBundleMeta
{
public string BundleName;
public Hash128 Hash;
public string[] Assets;
public string[] Dependencies;
}
/// <summary>
/// ab环的名称过滤器
/// </summary>
private HashSet<string> _filterAbCircle;
/// <summary>
/// AB环信息
/// </summary>
private List<List<string>> _abCircles;
/// <summary>
/// 环检测最大数量上限
/// </summary>
private const int MAXCircleNum = 800;
/// <summary>
/// ab到子资源的映射
/// </summary>
Dictionary<string, List<AbBuildInfo>> _abName2AssetsInfos;
/// <summary>
/// 路径到其对应的Build信息的映射
/// </summary>
private Dictionary<string, AbBuildInfo> _assetBuildInfos;
/// <summary>
/// AB包对应的AB依赖关系
/// </summary>
private Dictionary<string, List<string>> _abDeps;
/// <summary>
/// 资源环Index
/// </summary>
private int _assetCircleIndex = 0;
/// <summary>
/// 资源环的容器(判断用)
/// </summary>
private List<string> _assetCircleFilter;
/// <summary>
/// 环名到info的映射
/// </summary>
private Dictionary<string, List<AbBuildInfo>> _assetCircle2Infos;
/// <summary>
/// AB包连接的分隔符
/// </summary>
char abSep = 'S';
/// <summary>
/// 文件过滤
/// </summary>
private string[] fileExcludeFilter = new[] { ".cs", ".meta", ".dll", ".DS_Store"/*, ".unity" */};
/// <summary>
/// 资源环信息初始化
/// </summary>
void InitCircleInfo()
{
_assetCircle2Infos = new Dictionary<string, List<AbBuildInfo>>();
_assetCircleFilter = new List<string>();
_assetCircleIndex = 0;
}
/// <summary>
/// 设置资源所对应的BuildInfo信息
/// </summary>
/// <param name="abBuildInfoDic"></param>
/// <param name="path"></param>
/// <param name="abName"></param>
/// <param name="parent"></param>
/// <param name="parents"></param>
void SetAssetAbBuildInfo(string path, string abName, AbBuildInfo parent, List<AbBuildInfo> parents = null, bool isTop = false)
{
var info = GetOrCreateAbBuildInfo(path);
if (isTop)
info.SetAsTopRes();
info.SetABName(abName, path);
info.AddMyParent(parent);
info.AddMyParent(parents);
}
AbBuildInfo GetOrCreateAbBuildInfo(string path)
{
if (!_assetBuildInfos.TryGetValue(path, out AbBuildInfo info))
{
bool isTopRes = path.LastIndexOf(FileSystem.GameResourcePath, StringComparison.Ordinal) == 0;
info = new AbBuildInfo(isTopRes, path);
if (path.EndsWith(".unity"))
{
info.SetAsScene();
}
_assetBuildInfos.Add(path, info);
}
return info;
}
/// <summary>
/// 设置子项资源的依赖情况剔除了scriptobject
/// </summary>
/// <param name="abBuildInfoDic"></param>
/// <param name="path"></param>
void SetDependenciesExcludeSo(string path)
{
if (string.IsNullOrEmpty(path))
return;
if (IsSOCutOff(path))
return;
var info = GetOrCreateAbBuildInfo(path);
var parent = info.GetChildParent();
var childs = info.GetChilds();
if (childs != null)
{
foreach (var depend in childs)
{
Check(depend.Path);
}
}
else
{
//一层依赖
string[] depends = null;
//mat特殊处理获取全依赖
if (path.EndsWith(".mat"))
{
var mat = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path);
var deps = EditorUtility.CollectDependencies(new[] { mat });
depends = new string[deps.Length - 1];
int count = 0;
foreach (var dep in deps)
{
string matDPath = AssetDatabase.GetAssetPath(dep);
;
if (matDPath.Equals(path))
continue;
depends[count] = matDPath;
count++;
}
}
else
depends = AssetDatabase.GetDependencies(path, false);
foreach (string depend in depends)
{
Check(depend);
}
}
void Check(string targetPath)
{
if (targetPath.EndsWith(".cs"))
return;
CheckIfHandle(targetPath);
info.AddChild(GetOrCreateAbBuildInfo(targetPath));
//判断是否在环中
int start = _assetCircleFilter.IndexOf(targetPath);
if (start > -1)
{
//设置当前环信息
SetAssetCircleAbName(_assetCircleFilter.GetRange(start, _assetCircleFilter.Count - start));
//跳过
return;
}
SetAssetAbBuildInfo(targetPath, null, parent.Item1, parent.Item2);
if (IsSOCutOff(targetPath))
return;
_assetCircleFilter.Add(targetPath);
SetDependenciesExcludeSo(targetPath);
_assetCircleFilter.Remove(targetPath);
}
}
internal Dictionary<string, List<AbBuildInfo>> GetAbName2AssetsInfos()
{
return _abName2AssetsInfos;
}
internal Dictionary<string, List<string>> GetAbDepence()
{
return _abDeps;
}
/// <summary>
/// 设置额外的ab信息
/// </summary>
/// <param name="path2AbNames"></param>
internal void InsertAdditionalTopRes(Dictionary<string, string> path2AbNames, bool forceAdd = true)
{
if (path2AbNames != null && path2AbNames.Count > 0)
{
foreach (var p2a in path2AbNames)
{
string curPath = p2a.Key.Replace("\\", "/");
_assetBuildInfos.TryGetValue(curPath, out var info);
//如果当前资源不在收集表中,则剔除
if (!forceAdd)
{
if (info == null)
continue;
}
if (info != null)
info.SetOnlyRecord(false);
SetDependenciesAdditional(curPath, p2a
.Value);
}
}
}
/// <summary>
/// 设置规则外的AB信息
/// </summary>
/// <param name="path">对应资源路径</param>
public void SetDependenciesAdditional(string path, string abName)
{
if (_assetBuildInfos == null)
{
Debug.LogError("在错误的时机调用了SetDependenciesAdditional函数");
return;
}
if (string.IsNullOrEmpty(path) || string.IsNullOrEmpty(abName))
return;
abName = abName.ToLower();
SetAssetAbBuildInfo(path, abName, null, null, true);
_assetCircleFilter.Add(path);
SetDependenciesExcludeSo(path);
_assetCircleFilter.Remove(path);
}
/// <summary>
/// 是否属于被切断的SO
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
bool IsSOCutOff(string path)
{
return false;
}
/// <summary>
/// 设置资源环的AB名
/// </summary>
/// <param name="paths"></param>
/// <param name="dic"></param>
void SetAssetCircleAbName(List<string> paths)
{
//环名容器,要求唯一
List<string> circleNames = new List<string>();
foreach (var path in paths)
{
if (_assetBuildInfos.TryGetValue(path, out var info))
{
if (!string.IsNullOrEmpty(info.AbName) && info.IsInCircle)
{
if (!circleNames.Contains(info.AbName))
circleNames.Add(info.AbName);
}
}
else
{
Debug.LogError("不存在的环节点path" + path);
}
}
string abName = string.Empty;
if (circleNames.Count == 0)
{
//如果没有数据,说明是新的环,获取名字
abName = GetCurCircleName();
}
else
{
//否则取第一个环的名字
abName = circleNames[0];
}
//更新环字典
//1.将多余的部分添加进第一个名字的部分,去除多余的名字
//2.将新路径传入其中
if (_assetCircle2Infos.ContainsKey(abName))
{
//将circleNames中index从1开始的全部设为0的环名
for (int i = 1; i < circleNames.Count; ++i)
{
if (_assetCircle2Infos.TryGetValue(circleNames[i], out var abs))
{
for (int j = 0; j < abs.Count; ++j)
{
//将多余的部分添加进第一个名字的部分
AddInCircleDic(abName, abs[j]);
}
}
//去除多余的环名字
_assetCircle2Infos.Remove(circleNames[i]);
}
}
//设置传入数据的ab情况
foreach (var path in paths)
{
if (_assetBuildInfos.TryGetValue(path, out var info))
{
AddInCircleDic(abName, info);
}
}
}
/// <summary>
/// 将成环的资源信息设入资源环容器
/// </summary>
/// <param name="key"></param>
/// <param name="info"></param>
void AddInCircleDic(string key, AbBuildInfo info)
{
if (!_assetCircle2Infos.ContainsKey(key))
{
_assetCircle2Infos.Add(key, new List<AbBuildInfo>());
}
if (!_assetCircle2Infos[key].Contains(info))
{
info.SetCircleInfo(key);
_assetCircle2Infos[key].Add(info);
}
}
/// <summary>
/// 收集所有的Atlas将其下资源命名统一AB包名
/// </summary>
protected void CollectAllAtlasSprite()
{
if (_assetBuildInfos == null)
{
Debug.LogError("在错误的时机调用了收集Atlas");
return;
}
string[] atlas = AssetDatabase.FindAssets("t:SpriteAtlas");
for (int i = 0; i < atlas.Length; i++)
{
string atlasPath = AssetDatabase.GUIDToAssetPath(atlas[i]);
EditorUtility.DisplayProgressBar("InitSpriteAtlasRef...", atlasPath, (float)(i + 1) / atlas.Length);
string[] str = AssetDatabase.GetDependencies(atlasPath, false);
//如果其中资源存在被引用情况那么整个Atlas资源都需要设置统一包名
bool setSameAB = false;
for (int j = 0; j < str.Length; ++j)
{
//查看有无记录
if (_assetBuildInfos.TryGetValue(str[j], out var info))
{
if (info.OnlyRecord)
continue;
setSameAB = true;
break;
}
}
//需要统一设置
if (setSameAB)
{
string abName = $"atlas_{BuilderUtility.GetStringHash(atlasPath)}";
for (int j = 0; j < str.Length; ++j)
{
AbBuildInfo info = GetOrCreateAbBuildInfo(str[j]);
info.SetOnlyRecord(false);
info.SetSpriteAtlasInfo(abName);
}
//设置图集本身AB名
GetOrCreateAbBuildInfo(atlasPath).SetOnlyRecord(false);
GetOrCreateAbBuildInfo(atlasPath).SetSpriteAtlasInfo(abName);
}
}
EditorUtility.ClearProgressBar();
}
/// <summary>
/// 合并资源环
/// </summary>
void CombineCircle()
{
StringBuilder stringbuilder = new StringBuilder();
//初始环信息记录
stringbuilder.Append("路径名").Append(",").Append("所在环包").Append(",").Append("环包总数:" + _assetCircle2Infos.Count)
.Append("\n");
foreach (var name2Path in _assetCircle2Infos)
{
foreach (var info in name2Path.Value)
{
stringbuilder.Append(info.Path).Append(",").Append(name2Path.Key).Append("\n");
}
}
//合并环
/*
* 1.父ab一致设为父
* 2.无父或父不一致,不处理,合并
*/
List<string> _todel = new List<string>();
foreach (var info in _assetCircle2Infos)
{
List<string> parentsAB = new List<string>();
//如果当前资源是场景,那么这个环外,
// 其他数据,如果包含顶层资源,除去场景,合并
// 如果全非顶层资源删除记录auto
bool hasScene = false;
bool hasTopRes = false;
List<AbBuildInfo> scenes = new List<AbBuildInfo>();
foreach (var c in info.Value)
{
if (c.IsScene || c.Path.EndsWith(".unity"))
{
hasScene = true;
scenes.Add(c);
continue;
}
if (c.IsInTopRes)
{
hasTopRes = true;
}
}
//从环中删除场景
foreach (var scene in scenes)
{
scene.SetAsScene();
scene.SetCircleInfo(GetSceneName(scene.Path));
info.Value.Remove(scene);
}
if (hasScene)
{
if (!hasTopRes)
{
_todel.Add(info.Key);
foreach (var c in info.Value)
{
_assetBuildInfos.Remove(c.Path);
}
continue;
}
}
//遍历当前的资源环数据
foreach (var c in info.Value)
{
foreach (var parent in c.Parents)
{
//父物体为环,无视,环最后会统一融入,此时融入无意义
if (parent.AbName.IndexOf("circleab_", StringComparison.Ordinal) == 0)
continue;
//父物体无AB名
if (string.IsNullOrEmpty(parent.AbName))
{
Debug.LogError("parent.AbName = null" + parent.Path);
}
//正常的父物体
if (!string.IsNullOrEmpty(parent.AbName) && !parentsAB.Contains(parent.AbName))
parentsAB.Add(parent.AbName);
}
}
//这些环的父AB一致且只有一个设为父AB
if (parentsAB.Count == 1)
{
foreach (var c in info.Value)
{
c.SetCircleInfo(parentsAB[0]);
}
if (!_todel.Contains(info.Key))
_todel.Add(info.Key);
}
}
//删除被合并的的环
foreach (var del in _todel)
{
Debug.Log("删除环:" + del);
_assetCircle2Infos.Remove(del);
}
//合并环,使用第一个环的环名
string abName = string.Empty;
foreach (var info in _assetCircle2Infos)
{
abName = string.IsNullOrEmpty(abName) ? info.Key : abName;
if (info.Key.Equals(abName))
continue;
foreach (var abinfo in info.Value)
{
abinfo.SetCircleInfo(abName);
}
_assetCircle2Infos[abName].AddRange(info.Value);
}
// 将环信息结构存盘
try
{
if (_assetCircle2Infos.Count > 0)
{
stringbuilder.Append("\n").Append("\n").Append("\n").Append("\n").Append("\n");
//环信息输出
stringbuilder.Append("路径名").Append(",").Append("所在环包").Append(",").Append("环包总数:" + _assetCircle2Infos.Count)
.Append("\n");
foreach (var name2Path in _assetCircle2Infos)
{
if (!name2Path.Key.Equals(abName))
continue;
foreach (var info in name2Path.Value)
{
stringbuilder.Append(info.Path).Append(",").Append(name2Path.Key).Append("\n");
}
}
stringbuilder.Append("\n").Append("\n").Append("\n").Append("\n").Append("\n");
stringbuilder.Append("环包名").Append(",").Append("环信息").Append(",").Append("环包总数:" + _assetCircle2Infos.Count)
.Append("\n");
foreach (var name2Path in _assetCircle2Infos)
{
if (!name2Path.Key.Equals(abName))
continue;
foreach (var info in name2Path.Value)
{
string parentsAB = string.Empty;
foreach (var parent in info.Parents)
{
parentsAB += parent.AbName + "&&&&&&";
}
stringbuilder.Append(name2Path.Key).Append(",").Append(parentsAB).Append("\n");
}
}
BuilderUtility.ExportCsv("AssetCircleInfo.csv", stringbuilder);
Debug.Log("<color=green>AssetCircleInfo 为资源环信息</color>");
}
}
catch (System.Exception e)
{
Debug.Log("<color=red>数据导出 失败</color>");
Debug.Log(e);
}
}
/// <summary>
/// 获取AB名到资源的映射
/// </summary>
/// <param name="name2Paths"></param>
/// <returns></returns>
AssetBundleBuild[] GetAbName2PathsDic(out Dictionary<string, List<string>> name2Paths)
{
name2Paths = new Dictionary<string, List<string>>();
foreach (var info in _abName2AssetsInfos)
{
if (info.Value == null)
{
Debug.LogError(info.Key + " has not any abInfos");
continue;
}
string abName = info.Key;
if (name2Paths.ContainsKey(abName))
{
Debug.LogError("重复的AB名出现了" + abName);
}
else
{
name2Paths.Add(abName, new List<string>());
foreach (AbBuildInfo abBuildInfo in info.Value)
{
name2Paths[abName].Add(abBuildInfo.Path);
}
}
name2Paths[abName].Sort();
}
AssetBundleBuild[] buildMap = new AssetBundleBuild[name2Paths.Count];
int curIndex = 0;
foreach (var info in name2Paths)
{
buildMap[curIndex].assetNames = info.Value.ToArray();
buildMap[curIndex].assetBundleName = info.Key;
curIndex++;
}
return buildMap;
}
/// <summary>
/// 获取单包混合的新AB包名
/// </summary>
/// <param name="names"></param>
/// <param name="nameDic"></param>
/// <returns></returns>
private string GetMixAbNameInfo(List<string> names)
{
names.Sort();
StringBuilder sb = new StringBuilder();
foreach (var name in names)
{
sb.Append(name).Append(abSep);
}
return sb.ToString();
}
/// <summary>
/// 获取资源环的ab名
/// </summary>
/// <returns></returns>
string GetCurCircleName()
{
return ("circleab_strange_circleab_" + (_assetCircleIndex++)).ToLower();
}
/// <summary>
/// 是否属于操作范围内的资源gameresources与artraw下
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
bool CheckIfHandle(string path)
{
if (path.LastIndexOf(FileSystem.GameResourcePath, StringComparison.Ordinal) == 0)
return true;
if (Builder.Instance.CurBuilderData.bundleConfig == null || Builder.Instance.CurBuilderData.bundleConfig.filters == null)
{
if (path.LastIndexOf(FileSystem.ArtResourcePath, StringComparison.Ordinal) == 0)
return true;
GetOrCreateAbBuildInfo(path).SetOnlyRecord();
return false;
}
foreach (var filter in Builder.Instance.CurBuilderData.bundleConfig.filters)
{
if (path.LastIndexOf(filter.filterPath, StringComparison.Ordinal) == 0)
return true;
}
GetOrCreateAbBuildInfo(path).SetOnlyRecord();
return false;
}
/// <summary>
/// 按规则生成AssetBundle Name
/// </summary>
internal bool CollectAssetBundles(BuilderUtility.BuilderBundlePolicy bundlePolicy,
BundlePolicyConfig config = null)
{
InitCircleInfo();
_assetBuildInfos = new Dictionary<string, AbBuildInfo>();
bool result = true;
switch (bundlePolicy)
{
case BuilderUtility.BuilderBundlePolicy.SingleFile:
result = SimplePolicy();
break;
case BuilderUtility.BuilderBundlePolicy.Directory:
result = DirectoryBasedPolicy();
break;
case BuilderUtility.BuilderBundlePolicy.Configuration:
result = ConfigurationBasedPolicy(config);
break;
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorUtility.ClearProgressBar();
return result;
}
/// <summary>
/// 按GameResources下一个资源一个AB的策略
/// </summary>
protected bool SimplePolicy()
{
if (!CollectAssetBundle(FileSystem.GameResourcePath, BuilderUtility.BundlePolicy.SingleFile))
return false;
if (!CollectScenes())
return false;
// SetAndCheckAssetDependencies(_assetBuildInfos);
return true;
}
/// <summary>
/// 按GameResources下子目录一个AB的策略
/// </summary>
protected bool DirectoryBasedPolicy()
{
string[] subDirs = Directory.GetDirectories(FileSystem.GameResourcePath);
for (int i = 0; i < subDirs.Length; ++i)
{
if (EditorUtility.DisplayCancelableProgressBar("Collecting AssetBundle in directories", subDirs[i],
(float)(i + 1) / subDirs.Length))
{
EditorUtility.ClearProgressBar();
return false;
}
if (!CollectAssetBundle(subDirs[i], BuilderUtility.BundlePolicy.Directory))
return false;
}
if (!CollectScenes())
return false;
// SetAndCheckAssetDependencies(_assetBuildInfos);
return true;
}
/// <summary>
/// 按配置策略打AB
/// </summary>
protected bool ConfigurationBasedPolicy(BundlePolicyConfig config)
{
if (config)
{
Builder.Instance.CurBuilderData.bundleConfig = config;
foreach (var dir in Builder.Instance.CurBuilderData.bundleConfig.directoryBuild)
{
string temp = AssetDatabase.GetAssetPath(dir.mObject);
BuilderUtility.BundlePolicy policy = (BuilderUtility.BundlePolicy)(dir.buildType);
if (policy == BuilderUtility.BundlePolicy.ChildDirectory)
{
ChildDirectoryPolicy(temp);
continue;
}
if (!CollectAssetBundle(temp, policy))
return false;
}
if (!CollectScenes())
return false;
// SetAndCheckAssetDependencies(_assetBuildInfos);
}
else
UnityEngine.Debug.LogError($"Missing AssetBundle Collection Configuration");
return true;
}
/// <summary>
/// 按GameResources下子目录一个AB的策略
/// </summary>
bool ChildDirectoryPolicy(string path)
{
string[] subDirs = Directory.GetDirectories(path);
string[] subFiles = Directory.GetFiles(path);
for (int i = 0; i < subDirs.Length; ++i)
{
if (EditorUtility.DisplayCancelableProgressBar("Collecting AssetBundle in directories", subDirs[i],
(float)(i + 1) / subDirs.Length))
{
return false;
}
bool result = CollectAssetBundle(subDirs[i], BuilderUtility.BundlePolicy.ChildDirectory);
if (!result)
return false;
}
for (int i = 0; i < subFiles.Length; ++i)
{
if (EditorUtility.DisplayCancelableProgressBar("Collecting AssetBundle in directories", subFiles[i],
(float)(i + 1) / subFiles.Length))
{
return false;
}
bool isBreak = false;
for (int j = 0; j < fileExcludeFilter.Length; ++j)
{
if (subFiles[i].EndsWith(fileExcludeFilter[j]))
{
isBreak = true;
break;
}
}
if (isBreak)
continue;
bool result = CollectAssetBundle(subFiles[i], BuilderUtility.BundlePolicy.ChildDirectory, true);
if (!result)
return false;
}
EditorUtility.ClearProgressBar();
return true;
}
bool CollectAssetBundle(string path, BuilderUtility.BundlePolicy policy, bool isSingleFile = false)
{
if (isSingleFile)
{
string suffix = GetPathSuffix(path);
SetAssetBuildInfo(policy, path.Replace("\\", "/"), "", suffix);
return true;
}
using (FileTree fileTree = FileTree.CreateWithExcludeFilter(path, fileExcludeFilter))
{
//前缀
string suffix = GetPathSuffix(path);
List<FileInfo> files = fileTree.GetAllFiles();
for (int i = 0; i < files.Count; ++i)
{
string assetPath = files[i].GetAssetPath();
if (EditorUtility.DisplayCancelableProgressBar("Collecting AssetBundle", assetPath,
(float)(i + 1) / files.Count))
{
EditorUtility.ClearProgressBar();
return false;
}
SetAssetBuildInfo(policy, assetPath, fileTree.Name, suffix);
}
}
return true;
}
string GetPathSuffix(string path)
{
if (path[path.Length - 1] == '/')
{
path = path.Substring(0, path.Length - 1);
}
path = Path.GetDirectoryName(path)?.Replace("\\", "/");
string[] name = path?.Split('/');
string suffix;
if (name?.Length > 1)
suffix = name[name.Length - 1] + "_";
else
{
Debug.LogError("路径无法正确读取文件夹名称" + path);
suffix = "ErrorDirection" + "_";
}
return suffix;
}
void SetAssetBuildInfo(BuilderUtility.BundlePolicy policy, string assetPath, string directoryName, string suffix)
{
string abName = string.Empty;
switch (policy)
{
case BuilderUtility.BundlePolicy.SingleFile:
abName = GetAssetBundleName(assetPath, BuilderUtility.AssetSourceType.GameSource);
break;
case BuilderUtility.BundlePolicy.Directory:
abName = directoryName;
break;
case BuilderUtility.BundlePolicy.ChildDirectory:
//如果是文件夹,父文件夹+子文件夹名||||如果是文件,父文件夹+single
if (!string.IsNullOrEmpty(directoryName) && string.IsNullOrEmpty(Path.GetExtension(directoryName)))
abName = suffix + directoryName;
else
abName = suffix + "single";
break;
case BuilderUtility.BundlePolicy.CustomAbName:
abName = AssetImporter.GetAtPath(assetPath).assetBundleName;
if (string.IsNullOrEmpty(abName))
{
Debug.LogError("发现Custom策略下未命名AB资源单包处理:" + assetPath);
abName = GetAssetBundleName(assetPath, BuilderUtility.AssetSourceType.GameSource);
}
break;
}
//设置自身信息
SetAssetAbBuildInfo(assetPath, abName, null);
_assetCircleFilter.Clear();
_assetCircleFilter.Add(assetPath);
SetDependenciesExcludeSo(assetPath);
_assetCircleFilter.Remove(assetPath);
}
bool CollectScenes()
{
EditorBuildSettingsScene[] buildSettingsScenes = EditorBuildSettings.scenes;
// 默认第一个为内嵌场景
for (int i = 1; i < buildSettingsScenes.Length; ++i)
{
if (buildSettingsScenes[i] == null || string.IsNullOrEmpty(buildSettingsScenes[i].path) ||
!buildSettingsScenes[i].enabled)
continue;
if (EditorUtility.DisplayCancelableProgressBar("Collecting Scenes", buildSettingsScenes[i].path,
(float)(i + 1) / buildSettingsScenes.Length))
{
EditorUtility.ClearProgressBar();
return false;
}
SetAssetAbBuildInfo(buildSettingsScenes[i].path,
$"unityscene_{GetSceneName(buildSettingsScenes[i].path)}", null);
GetOrCreateAbBuildInfo(buildSettingsScenes[i].path).SetAsScene();
_assetCircleFilter.Clear();
_assetCircleFilter.Add(buildSettingsScenes[i].path);
SetDependenciesExcludeSo(buildSettingsScenes[i].path);
_assetCircleFilter.Remove(buildSettingsScenes[i].path);
}
return true;
}
string GetSceneName(string scenePath)
{
scenePath = scenePath.Substring(0, scenePath.LastIndexOf('.'));
return scenePath.Substring(scenePath.LastIndexOf('/') + 1);
}
/// <summary>
/// 最后设置ab包名应放在所有基础ab信息设置完成之后
/// </summary>
/// <param name="doSingleCombine">是否进行单包合并</param>
internal void SetAndCheckAssetDependencies(bool doSingleCombine = true)
{
if (_assetBuildInfos == null)
{
Debug.LogError("在错误的时机调用了SetDependenciesAdditional函数");
return;
}
//合并资源环
CombineCircle();
//合并所有SpriteAtlas图集信息
CollectAllAtlasSprite();
//设置AB名
List<string> excludeKeys = new List<string>();
foreach (var info in _assetBuildInfos)
{
//剔除文件夹
if (Directory.Exists(info.Key))
{
excludeKeys.Add(info.Key);
continue;
}
//剔除RawBytes
if (info.Key.Contains(AssetConfig.AssetRootPath + "/" + "RawBytes"))
{
if (Builder.Instance.CurBuilderData.builderBundlePolicy == BuilderUtility.BuilderBundlePolicy.Configuration)
{
Debug.LogError($"存在RawBytes下的资源被依赖{info.Key}");
}
excludeKeys.Add(info.Key);
continue;
}
//剔除单纯做记录的数据
if (info.Value.OnlyRecord)
{
excludeKeys.Add(info.Key);
continue;
}
//如果一个AB会成为单包先不设置AB名标志标记为为单包
info.Value.FixAbName(info.Key);
}
//剔除无意义的ab信息
foreach (var key in excludeKeys)
{
_assetBuildInfos.Remove(key);
}
#region
//记录"单包合并名"所对应单包信息
Dictionary<string, List<AbBuildInfo>> abName2AssetDic = new Dictionary<string, List<AbBuildInfo>>();
int cc = 0;
foreach (var abInfo in _assetBuildInfos)
{
if (!abInfo.Value.IsSingle)
continue;
string abName;
if (abInfo.Value.Path.EndsWith(".unity"))
{
abInfo.Value.SetAsScene();
abName = abInfo.Value.Path;
}
else
{
AbBuildInfo info = abInfo.Value;
List<string> parents = new List<string>();
foreach (var abBuildInfo in info.Parents)
{
if (!parents.Contains(abBuildInfo.AbName))
parents.Add(abBuildInfo.AbName);
}
abName = GetMixAbNameInfo(parents);
}
cc++;
if (abName2AssetDic.ContainsKey(abName))
{
abName2AssetDic[abName].Add(abInfo.Value);
}
else
{
abName2AssetDic.Add(abName, new List<AbBuildInfo>() { abInfo.Value });
}
}
Debug.Log("非顶层资源单包数量:" + cc);
//默认进行单包合并操作
//对于只有一个资源的单包合并名,恢复成单包名
StringBuilder linker = new StringBuilder();
foreach (var list in abName2AssetDic)
{
if (list.Value.Count == 1)
{
list.Value[0].SetABName(GetAssetBundleName(list.Value[0].Path,
list.Value[0].IsInTopRes
? BuilderUtility.AssetSourceType.GameSource
: BuilderUtility.AssetSourceType.ArtSource));
}
else
{
if (doSingleCombine)
{
List<string> abs = new List<string>(list.Key.Split(abSep));
abs.Sort();
linker.Clear();
foreach (string ab in abs)
{
linker.Append(ab);
}
string abName = "artcombine_" + BuilderUtility.GetStringHash(linker.ToString());
foreach (var info in list.Value)
{
info.SetABName(abName);
}
}
else
{
foreach (var info in list.Value)
{
info.SetABName(GetAssetBundleName(
info.Path, info.IsInTopRes ? BuilderUtility.AssetSourceType.GameSource : BuilderUtility.AssetSourceType.ArtSource));
}
}
}
}
#endregion
//ab到子资源的映射
_abName2AssetsInfos = new Dictionary<string, List<AbBuildInfo>>();
List<string> combinelist = new List<string>();
foreach (var info in _assetBuildInfos)
{
if (string.IsNullOrEmpty(info.Value.AbName))
{
Debug.LogError("cannot use auto with emptyname:::path:" + info.Value.Path);
continue;
}
if (info.Value.IsSingle && !combinelist.Contains(info.Value.AbName))
combinelist.Add(info.Value.AbName);
string abName = info.Value.AbName.ToLower();
if (_abName2AssetsInfos.ContainsKey(abName))
{
_abName2AssetsInfos[abName].Add(info.Value);
}
else
{
_abName2AssetsInfos.Add(abName, new List<AbBuildInfo>() { info.Value });
}
}
Debug.Log("合并后数量:" + combinelist.Count);
combinelist.Clear();
/*
* 主动记录AB包依赖
*/
_abDeps = new Dictionary<string, List<string>>();
foreach (var info in _abName2AssetsInfos)
{
if (!_abDeps.ContainsKey(info.Key))
_abDeps.Add(info.Key, new List<string>());
foreach (var value in info.Value)
{
foreach (var child in value.DirectChilds)
{
if (child.OnlyRecord)
continue;
if (!_abDeps[info.Key].Contains(child.AbName) && !info.Key.Equals(child.AbName))
_abDeps[info.Key].Add(child.AbName);
}
}
_abDeps[info.Key].Sort();
}
}
internal void BuildAssetBundlesAfterCollect(bool bDevelopment, bool bCleanCache = false)
{
HandleOriginAssetBundleOutput(bDevelopment, bCleanCache);
string outputPath = GetAssetBundleOutputPath();
//构造buildMap
var buildMap = GetAbName2PathsDic(out Dictionary<string, List<string>> name2Paths);
// 将AB信息结构存盘
try
{
StringBuilder stringbuilder = new StringBuilder();
stringbuilder.Append("资源路径").Append(",").Append("所在AB包").Append(",").Append("AB包总数" + name2Paths.Count)
.Append("\n");
foreach (var name2Path in name2Paths)
{
foreach (var path in name2Path.Value)
{
stringbuilder.Append(path).Append(",").Append(name2Path.Key).Append("\n");
}
}
BuilderUtility.ExportCsv("AssetBundleInfo.csv", stringbuilder);
Debug.Log("<color=green>AssetBundleInfo 可以查看资源所对应的AB包</color>");
}
catch (System.Exception e)
{
Debug.Log("<color=red>数据导出 失败</color>");
Debug.Log(e);
}
AssetBundleManifest manifest = BuildPipeline.BuildAssetBundles(outputPath, buildMap,
BuildAssetBundleOptions.DeterministicAssetBundle | BuildAssetBundleOptions.ChunkBasedCompression,
EditorUserBuildSettings.activeBuildTarget);
if (manifest != null)
{
uint offset = 0;
if (Builder.Instance.CurBuilderData.bEnableEncrypt)
{
try
{
string offVal = Builder.Instance.CurBuilderData.abEncryptOffset.Trim();
offset = Convert.ToUInt32(offVal);
}
catch (Exception e)
{
offset = 0;
Debug.LogWarning("AB加密不执行" + e.ToString());
}
}
WriteAssetBundleManifest(outputPath, manifest, name2Paths, offset);
if (Builder.Instance.CurBuilderData.bEnableEncrypt && offset > 0)
{
var start = DateTime.Now;
string buildTarget = EditorUserBuildSettings.activeBuildTarget.ToString();
AssetbundleEncryption.EncryptAssetBundlesByDirectory($"{FileSystem.AssetBundleBuildPath}/{buildTarget}", offset);
Debug.Log("AB加密总耗时" + (DateTime.Now - start).TotalSeconds.ToString("F2") + " 秒");
}
CopyAssetBundles();
AssetDatabase.Refresh();
}
}
void WriteAssetBundleManifest(string path, AssetBundleManifest manifest,
Dictionary<string, List<string>> buildMap, uint abOffset)
{
List<AssetBundleMeta> assetBundleMetas = new List<AssetBundleMeta>();
// 名字到bundleMeta的表做环检查的时候会用到
Dictionary<string, AssetBundleMeta> nameToBundleMetas =
new Dictionary<string, AssetBundleMeta>();
foreach (var bundleName in manifest.GetAllAssetBundles())
{
AssetBundleMeta assetBundleMeta = new AssetBundleMeta
{
BundleName = bundleName,
Hash = manifest.GetAssetBundleHash(bundleName),
Dependencies = _abDeps[bundleName].ToArray()//manifest.GetDirectDependencies(bundleName)
};
#if NEONABYSS_SUPPORT
if (assetBundleMeta.BundleName == "scriptobject")
{
assetBundleMeta.Dependencies = new string[0];
}
#endif
if (!buildMap.ContainsKey(bundleName))
{
Debug.LogError("不存在的bundleName" + bundleName);
continue;
}
string[] assetPaths = buildMap[bundleName].ToArray();
List<string> toRecordAssets = new List<string>();
for (int j = 0; j < assetPaths.Length; ++j)
{
if (_assetBuildInfos.TryGetValue(assetPaths[j], out AbBuildInfo info))
{
if (info.IsScene)
{
toRecordAssets.Add(assetPaths[j]);
}
else if (info.IsInTopRes)
{
toRecordAssets.Add(assetPaths[j]);
}
}
}
assetBundleMeta.Assets = toRecordAssets.ToArray();
assetBundleMetas.Add(assetBundleMeta);
// 保存名字到bundleMeta的表 环检查要用到
nameToBundleMetas[bundleName] = assetBundleMeta;
}
FileStream stream = new FileStream(path + "/" + AssetConfig.AssetBundleMeta, FileMode.OpenOrCreate);
BinaryWriter writer = new BinaryWriter(stream);
writer.Write((uint)1);
writer.Write(assetBundleMetas.Count);
writer.Write(abOffset);
foreach (var meta in assetBundleMetas)
{
writer.Write(meta.BundleName);
writer.Write(meta.Assets.Length);
foreach (var assetPath in meta.Assets)
{
if (_assetBuildInfos.TryGetValue(assetPath, out AbBuildInfo info))
{
if (info.IsScene)
{
// 场景只用名字索引
writer.Write(GetSceneName(assetPath));
}
else if (info.IsInTopRes)
{
int index = assetPath.IndexOf(FileSystem.GameResourcePath, StringComparison.Ordinal);
if (index >= 0)
writer.Write(assetPath.Remove(index, FileSystem.GameResourcePath.Length + 1));
else
writer.Write(assetPath);
}
else
Debug.LogError($"非顶层的AB信息无法写入assetPaths[j]:" + assetPath);
}
else
Debug.LogError($"不存在的AB信息无法写入assetPaths[j]:" + assetPath);
}
writer.Write(meta.Dependencies.Length);
foreach (var dep in meta.Dependencies)
writer.Write(dep);
}
writer.Close();
// 环检查
_filterAbCircle = new HashSet<string>();
List<string> checkedBundleMetas = new List<string>();
_abCircles = new List<List<string>>();
for (int i = 0; i < assetBundleMetas.Count; i++)
{
if (_filterAbCircle.Contains(assetBundleMetas[i].BundleName))
continue;
List<string> filter = new List<string>();
if (CheckDependentRing(assetBundleMetas[i], nameToBundleMetas, checkedBundleMetas, filter))
{
Debug.LogError($"环数量已超过{MAXCircleNum},退出循环");
break;
}
foreach (var ab in filter)
{
if (!_filterAbCircle.Contains(ab))
_filterAbCircle.Add(ab);
}
_filterAbCircle.Add(assetBundleMetas[i].BundleName);
}
//输出AB环信息
ExportAbBlackList();
}
/// <summary>
/// 深度优先检查依赖环
/// </summary>
/// <param name="assetBundleMeta">待检查bundle</param>
/// <param name="allBundleMetas">所有bundle</param>
/// <param name="checkedBundleMetas">检查过的bundle</param>
/// <returns></returns>
bool CheckDependentRing(AssetBundleMeta assetBundleMeta,
Dictionary<string, AssetBundleMeta> allBundleMetas,
List<string> checkedBundleMetas, List<string> filter, List<string> nameFilter = null)
{
nameFilter = nameFilter ?? new List<string>();
int start = checkedBundleMetas.IndexOf(assetBundleMeta.BundleName);
if (start > -1)
{
//发现环
List<string> range = checkedBundleMetas.GetRange(start, checkedBundleMetas.Count - start);
if (AddAbCircleRnage(range, nameFilter))
return true;
//_abCircles.Add(range);
return false;
}
//如果一个节点的子节点已经参与过环检查,那么这个子节点一定不会诞生新的环,跳过
if (_filterAbCircle.Contains(assetBundleMeta.BundleName))
return false;
checkedBundleMetas.Add(assetBundleMeta.BundleName);
if (!filter.Contains(assetBundleMeta.BundleName))
filter.Add(assetBundleMeta.BundleName);
for (int i = 0; i < assetBundleMeta.Dependencies.Length; i++)
{
if (CheckDependentRing(allBundleMetas[assetBundleMeta.Dependencies[i]], allBundleMetas,
checkedBundleMetas,
filter, nameFilter))
return true;
}
checkedBundleMetas.Remove(assetBundleMeta.BundleName);
return false;
}
/// <summary>
/// 添加AB环信息
/// </summary>
/// <param name="circle"></param>
/// <param name="namefilter"></param>
/// <returns>是否退出</returns>
bool AddAbCircleRnage(List<string> circle, List<string> namefilter)
{
string sep = "&";
StringBuilder sb = new StringBuilder();
foreach (var str in circle)
{
sb.Append(str).Append(sep);
}
if (namefilter.Contains(sb.ToString()))
{
return false;
}
else
{
for (int off = 0; off < circle.Count; off++)
{
sb.Clear();
for (int i = off; i < circle.Count; ++i)
{
sb.Append(circle[i]).Append(sep);
}
for (int i = 0; i < off; ++i)
{
sb.Append(circle[i]).Append(sep);
}
if (namefilter.Contains(sb.ToString()))
{
Debug.LogError("Namefilter 已存在" + sb.ToString());
}
else
{
namefilter.Add(sb.ToString());
}
}
}
_abCircles.Add(circle);
return _abCircles.Count > MAXCircleNum;
}
/// <summary>
/// 拷贝AB数据
/// </summary>
internal void CopyAssetBundles()
{
if (Directory.Exists(FileSystem.StreamAssetBundlePath))
BuilderUtility.ClearAllByPath(FileSystem.StreamAssetBundlePath);
AssetDatabase.Refresh();
Directory.CreateDirectory(FileSystem.StreamAssetBundlePath);
string buildTarget = EditorUserBuildSettings.activeBuildTarget.ToString();
string srcPath = $"{FileSystem.AssetBundleBuildPath}/{buildTarget}";
if (!Directory.Exists(srcPath))
{
Debug.LogWarning("不存在AB包无法复制");
return;
}
using (FileTree fileTree =
FileTree.CreateWithExcludeFilter(srcPath,
new[] { ".manifest" }))
{
foreach (FileInfo file in fileTree.GetAllFiles())
{
if (file.Name != buildTarget)
{
file.CopyTo($"{FileSystem.StreamAssetBundlePath}/{file.Name}");
}
}
}
AssetDatabase.Refresh();
if (Builder.Instance.CurBuilderData != null && Builder.Instance.CurBuilderData.buildType == BuilderUtility.BuildType.Development)
{
string target = $"{FileSystem.ResourceRoot}/AssetBundles";
if (Directory.Exists(target))
Directory.Delete(target, true);
LoaderUtilities.CopyDirectory(FileSystem.StreamAssetBundlePath, target);
}
}
void ExportAbBlackList()
{
string separation = "->";
if (_abCircles == null || _abCircles.Count == 0)
{
return;
}
StringBuilder abCircleList = new StringBuilder();
abCircleList.Append("AB环信息,具体数据,依赖关系\n");
Dictionary<string, List<string>> infoItem = new Dictionary<string, List<string>>();
for (int i = 0; i < _abCircles.Count; ++i)
{
//当前的AB环
var curList = _abCircles[i];
string name = "";
List<string> nameOrder = new List<string>();
for (int j = 0, l = curList.Count; j < l; ++j)
{
//当前处理的节点
string cur = curList[j];
string next;
next = (j == curList.Count - 1) ? curList[0] : curList[j + 1];
//当前next中资源在cur的路径关系
//cur name:A->B
string curName = cur + separation + next;
nameOrder.Add(curName);
name += cur + separation;
if (infoItem.ContainsKey(curName))
{
continue;
}
infoItem.Add(curName, new List<string>());
foreach (var asset in _abName2AssetsInfos[cur])
{
var depends = asset.GetChilds();
if (depends != null && depends.Count > 0)
{
foreach (var dep in depends)
{
if (dep.OnlyRecord)
continue;
if (dep.AbName.Equals(next))
{
//出现了当前的依赖项依赖到了目标AB
infoItem[curName].Add(asset.Path + "==依赖==>" + dep.Path);
}
}
}
}
if (infoItem[curName].Count == 0)
{
Debug.LogWarning("无法找到对应AB依赖关系可能为mat资源残留的错误引用" + curName);
}
}
name += curList[0];
abCircleList.Append(name + "\n");
foreach (var p2p in nameOrder)
{
foreach (var path in infoItem[p2p])
{
abCircleList.Append(" ,").Append(path).Append(",").Append(p2p).Append("\n");
}
}
}
BuilderUtility.ExportCsv("AssetbundleCircleList.csv", abCircleList);
abCircleList.Clear();
_abCircles.Clear();
}
protected virtual string GetAssetBundleName(string assetPath, BuilderUtility.AssetSourceType sourceType)
{
string assetBundleName = null;
if (sourceType == BuilderUtility.AssetSourceType.GameSource)
{
assetBundleName = assetPath.Remove(0, AssetConfig.AssetRootPath.Length + 1);
int dotIndex = assetBundleName.LastIndexOf('.');
if (dotIndex > 0)
return assetBundleName.Substring(0, dotIndex).Replace('/', '_').Replace('.', '_');
else
return assetBundleName.Replace('/', '_').Replace('.', '_');
}
else if (sourceType == BuilderUtility.AssetSourceType.ArtSource)
{
if (Builder.Instance.CurBuilderData.bundleConfig != null)
{
foreach (var item in Builder.Instance.CurBuilderData.bundleConfig.filters)
{
if (assetPath.IndexOf(item.filterPath) != -1)
{
assetBundleName = assetPath.Remove(0, item.filterPath.Length + 1);
}
}
}
else
{
assetBundleName = assetPath.Remove(0, FileSystem.ArtResourcePath.Length + 1);
}
int dotIndex = assetBundleName.LastIndexOf('.');
if (dotIndex > 0)
return $"_{assetBundleName.Substring(0, dotIndex).Replace('/', '_').Replace('.', '_')}";
else
return $"_{assetBundleName.Replace('/', '_').Replace('.', '_')}";
}
else
{
UnityEngine.Debug.LogError("Not support AssetSourceType");
return null;
}
}
protected virtual void HandleOriginAssetBundleOutput(bool bDevelopment, bool bCleanCache = false)
{
string outputPath = GetAssetBundleOutputPath();
if (bCleanCache && bDevelopment)
{
if (!Directory.Exists(outputPath))
Directory.CreateDirectory(outputPath);
}
else
{
if (Directory.Exists(outputPath))
Directory.Delete(outputPath, true);
Directory.CreateDirectory(outputPath);
}
}
protected virtual string GetAssetBundleOutputPath()
{
return $"{FileSystem.AssetBundleBuildPath}/{EditorUserBuildSettings.activeBuildTarget.ToString()}"; ;
}
public class AbBuildInfo
{
/// <summary>
/// ab包名
/// </summary>
public string AbName { get; private set; }
/// <summary>
/// 资源路径
/// </summary>
public string Path { get; private set; }
/// <summary>
/// 父物体
/// </summary>
public List<AbBuildInfo> Parents;
/// <summary>
/// 直接子对象
/// </summary>
public List<AbBuildInfo> DirectChilds;
/// <summary>
/// 是否是auto进包资源
/// </summary>
public bool IsAuto { get; private set; }
/// <summary>
/// 是否是单包
/// </summary>
public bool IsSingle { get; private set; }
/// <summary>
/// 是否是顶层资源
/// </summary>
public bool IsInTopRes { get; private set; }
/// <summary>
/// 是否是场景资源
/// </summary>
public bool IsScene { get; private set; }
/// <summary>
/// 是否属于图集子资源
/// </summary>
public bool IsInSpriteAtlas { get; private set; }
/// <summary>
/// 是否属于环
/// </summary>
public bool IsInCircle { get; private set; }
/// <summary>
/// 只作为记录
/// </summary>
public bool OnlyRecord = false;
public AbBuildInfo(bool isInTopRes, string assetPath)
{
AbName = string.Empty;
Parents = new List<AbBuildInfo>();
DirectChilds = new List<AbBuildInfo>();
IsInTopRes = isInTopRes;
Path = assetPath;
IsAuto = false;
OnlyRecord = false;
}
public void AddChild(AbBuildInfo info)
{
if (!DirectChilds.Contains(info))
DirectChilds.Add(info);
}
public List<AbBuildInfo> GetChilds()
{
if (DirectChilds.Count > 0)
return DirectChilds;
return null;
}
public void SetAsTopRes()
{
IsInTopRes = true;
}
public void SetCircleInfo(string name)
{
AbName = name.ToLower();
IsInCircle = true;
}
public void SetSpriteAtlasInfo(string name)
{
AbName = name.ToLower();
IsInSpriteAtlas = true;
}
public void SetAsScene()
{
IsScene = true;
}
public void SetOnlyRecord(bool enable = true)
{
OnlyRecord = enable;
}
public void SetABName(string name, string path = null)
{
if (IsInCircle)
return;
name = name?.ToLower();
if (!string.IsNullOrEmpty(AbName))
{
if (!string.IsNullOrEmpty(name) && !AbName.Equals(name))
{
//Debug.LogWarning($"重复设置AB包名!原:{AbName},尝试设为:{name}" + path);
AbName = name;
}
return;
}
AbName = name;
}
/// <summary>
/// 获得该对象的子对象所对应的父对象
/// </summary>
/// <returns></returns>
public (AbBuildInfo, List<AbBuildInfo>) GetChildParent()
{
//如果本身是GameRes 或 是一个场景资源下自己就是子的Parent
if (IsInTopRes || IsScene)
return (this, null);
//非GameRes下直接给自己的父
return (null, Parents);
}
public void AddMyParent(AbBuildInfo parent)
{
if (parent == null)
return;
if (!Parents.Contains(parent))
Parents.Add(parent);
}
public void AddMyParent(List<AbBuildInfo> parents)
{
if (parents == null || parents.Count < 1)
return;
foreach (var abBuildInfo in parents)
{
AddMyParent(abBuildInfo);
}
}
/// <summary>
/// 最终设置AB名主要针对非顶层资源
/// </summary>
/// <param name="assetPath"></param>
public void FixAbName(string assetPath)
{
/*特殊:环,
如果在GameRes或场景下必已有AB名否则有问题
否则在ArtRaw下
在ArtRaw下
如果没有父物体,怎么进来的?
如果父物体有多个父物体AB名字一样设置包名
ab名字不一样单包
如果只有一个父物体且父物体无父auto
父物体有父且父物体ab名字不一样设为父物体的ab名
一样auto
*/
if (IsScene)
AbName = ("scene" + Path.Substring(0, Path.LastIndexOf('.')).Substring(Path.LastIndexOf('/') + 1)).ToLower();
if (IsInTopRes || IsScene || IsInCircle || IsInSpriteAtlas)
{
if (AbName == null)
{
if (System.IO.Path.GetExtension(assetPath).Equals(""))
Debug.LogWarning("不正常的AB名检测到文件夹" + assetPath);
else
Debug.LogError("不正常的AB名Gamres下资源无AB名" + assetPath);
}
return;
}
if (Parents == null || Parents.Count == 0)
{
Debug.LogError("不正常的ParentArtRaw下资源无父物体" + assetPath);
AbName = string.Empty;
return;
}
//多个父物体
if (Parents.Count > 1)
{
string abName = Parents[0].AbName;
if (abName == null)
{
Debug.LogError("不正常的AB名其路径" + Parents[0].Path);
return;
}
for (int i = 1; i < Parents.Count; ++i)
{
if (!abName.Equals(Parents[i].AbName))
{
//单包
IsSingle = true;
return;
}
}
if (Parents[0].IsScene)
AbName = ("_scenedeps_" + abName).ToLower();
else
AbName = abName;
}
else
{
if (Parents[0].Parents == null || Parents[0].Parents.Count == 0)
{
//auto
if (Parents[0].IsScene)
AbName = ("_scenedeps_" + Parents[0].AbName).ToLower();
else
AbName = Parents[0].AbName;
}
else
{
//todo 判断父的父如果ab都一样auto
if (Parents[0].IsScene)
AbName = ("_scenedeps_" + Parents[0].AbName).ToLower();
else
AbName = Parents[0].AbName;
}
}
}
}
}
public static class BuildHelper
{
[MenuItem("Assets/Get Asset Path", priority = 3)]
static void GetAssetPath()
{
UnityEngine.Object selObj = Selection.activeObject;
if (selObj != null)
{
string assetPath = AssetDatabase.GetAssetPath(selObj);
int sindex = assetPath.IndexOf(AssetConfig.AssetRootPath);
int eindex = assetPath.LastIndexOf('.');
if (sindex >= 0 && eindex >= 0)
{
string resPath = assetPath.Substring(sindex + AssetConfig.AssetRootPath.Length + 1);
if (selObj is Sprite)
resPath += $"#{selObj.name}";
Debug.Log($"Asset path is {resPath}");
EditorGUIUtility.systemCopyBuffer = resPath;
}
else if (assetPath.StartsWith("Packages/"))
{
Debug.Log($"Packages Asset path is {assetPath}");
EditorGUIUtility.systemCopyBuffer = assetPath;
}
else
{
Debug.Log("This is not a Asset file!");
}
}
}
}
}