Created
April 11, 2017 02:23
-
-
Save benloong/23cff5efca16f09a3f6e7856882d9c28 to your computer and use it in GitHub Desktop.
Unity4.x Build AssetBundle and generate dependence
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections.Generic; | |
using UnityEditor; | |
using System.Linq; | |
public class AssetBundleManifest | |
{ | |
public string AssetPath; | |
public HashSet<AssetBundleManifest> Parents = new HashSet<AssetBundleManifest>(); | |
public HashSet<AssetBundleManifest> Children = new HashSet<AssetBundleManifest>(); | |
public int pushCount = 0; | |
public bool IsAtlas = false; | |
public string AtlasName = string.Empty; | |
public bool IsScene = false; | |
/// <summary> | |
/// 是否需要重新导出 | |
/// 根据timestamp比较导出的AssetBundle和资源是否需要重新导出 | |
/// </summary> | |
public bool NeedExport | |
{ | |
get; | |
set; | |
} | |
public bool Push() | |
{ | |
if (pushCount == 0) | |
{ | |
BuildPipeline.PushAssetDependencies(); | |
Debug.Log("Push :" + AssetPath); | |
} | |
pushCount++; | |
return pushCount == 1; | |
} | |
public void Pop() | |
{ | |
pushCount--; | |
if (pushCount == 0) | |
{ | |
BuildPipeline.PopAssetDependencies(); | |
Debug.Log("Pop : " + AssetPath); | |
} | |
} | |
public long GetTimeStamp() | |
{ | |
return 0; | |
} | |
} | |
public class AssetBundleEditorHelper | |
{ | |
#if UNITY_ANDROID | |
static BuildTarget target = BuildTarget.Android; | |
#elif UNITY_IPHONE | |
static BuildTarget target = BuildTarget.iPhone; | |
#else | |
static BuildTarget target = BuildTarget.StandaloneWindows; | |
#endif | |
[MenuItem("Tools/Atlas/ShowUsedAtlasName")] | |
public static void FilterSelectionAtlas() | |
{ | |
if (Selection.gameObjects == null) | |
{ | |
return; | |
} | |
var allAssets = Selection.gameObjects.Select(go => AssetDatabase.GetAssetPath(go)); | |
List<string> packedSprites = new List<string>(); | |
System.Text.StringBuilder sb = new System.Text.StringBuilder(); | |
HashSet<string> atlas = new HashSet<string>(); | |
foreach (var item in allAssets) | |
{ | |
var dependencies = AssetDatabase.GetDependencies(new string[] { item }); | |
foreach (var path in dependencies) | |
{ | |
var sprite = AssetDatabase.LoadAssetAtPath(path, typeof(Sprite)) as Sprite; | |
if (sprite) | |
{ | |
TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter; | |
if (importer && !string.IsNullOrEmpty(importer.spritePackingTag)) | |
{ | |
sb.Append(importer.spritePackingTag); | |
sb.Append(" ").Append(path); | |
sb.Append("\n"); | |
atlas.Add(importer.spritePackingTag); | |
packedSprites.Add(path); | |
} | |
} | |
} | |
} | |
Debug.Log(sb.ToString()); | |
Debug.Log("all used atlas:\n" + string.Join("\n", atlas.ToArray())); | |
} | |
static BuildAssetBundleOptions options = BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.DeterministicAssetBundle; | |
public static Dictionary<string, AssetBundleManifest> BuildDependencyTree(List<string> allAssetPathList) | |
{ | |
Dictionary<string, AssetBundleManifest> bundles = new Dictionary<string, AssetBundleManifest>(); | |
//var allAssets = AssetDatabase.FindAssets("l:AssetBundle").Select(p => AssetDatabase.GUIDToAssetPath(p)); | |
var allAssets = allAssetPathList; | |
List<string> packedSprites = new List<string>(); | |
foreach (var item in allAssets) | |
{ | |
var dependencies = AssetDatabase.GetDependencies(new string[] { item }); | |
foreach (var path in dependencies) | |
{ | |
var sprite = AssetDatabase.LoadAssetAtPath(path, typeof(Sprite)) as Sprite; | |
if (sprite) | |
{ | |
TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter; | |
if (importer && !string.IsNullOrEmpty(importer.spritePackingTag)) | |
{ | |
packedSprites.Add(path); | |
} | |
} | |
} | |
} | |
foreach (var asset in allAssets) | |
{ | |
AssetBundleManifest ab = new AssetBundleManifest(); | |
ab.AssetPath = asset; | |
bundles[asset] = ab; | |
var obj = AssetDatabase.LoadAssetAtPath(asset, typeof(Object)); | |
if (obj && obj.ToString().Contains("UnityEngine.SceneAsset")) | |
{ | |
ab.IsScene = true; | |
} | |
} | |
foreach (var path in packedSprites) | |
{ | |
AssetBundleManifest ab = new AssetBundleManifest() { AssetPath = path }; | |
bundles[path] = ab; | |
} | |
foreach (var path in bundles) | |
{ | |
var dependencies = AssetDatabase.GetDependencies(new string[] { path.Value.AssetPath }); | |
UpdateDependencyLink(bundles, path.Value.AssetPath, dependencies); | |
} | |
// 合并同图集的Sprite到一个AssetBundle中 | |
// 找到所有的Packed Sprite | |
List<AssetBundleManifest> allPackedSprites = new List<AssetBundleManifest>(); | |
foreach (var item in bundles.Values) | |
{ | |
var sprite = AssetDatabase.LoadAssetAtPath(item.AssetPath, typeof(Sprite)) as Sprite; | |
if (sprite) | |
{ | |
TextureImporter importer = AssetImporter.GetAtPath(item.AssetPath) as TextureImporter; | |
if (importer && !string.IsNullOrEmpty(importer.spritePackingTag)) | |
{ | |
allPackedSprites.Add(item); | |
} | |
} | |
} | |
// 图集AssetBundle | |
Dictionary<string, AssetBundleManifest> packingTagDict = new Dictionary<string, AssetBundleManifest>(); | |
foreach (var item in allPackedSprites) | |
{ | |
var sprite = AssetDatabase.LoadAssetAtPath(item.AssetPath, typeof(Sprite)) as Sprite; | |
if (sprite) | |
{ | |
TextureImporter importer = AssetImporter.GetAtPath(item.AssetPath) as TextureImporter; | |
if (importer && !string.IsNullOrEmpty(importer.spritePackingTag)) | |
{ | |
if (!packingTagDict.ContainsKey(importer.spritePackingTag)) | |
{ | |
packingTagDict[importer.spritePackingTag] = new AssetBundleManifest(); | |
packingTagDict[importer.spritePackingTag].AssetPath = importer.spritePackingTag; | |
packingTagDict[importer.spritePackingTag].IsAtlas = true; | |
packingTagDict[importer.spritePackingTag].AtlasName = importer.spritePackingTag; | |
} | |
var atlasAssetBundle = packingTagDict[importer.spritePackingTag]; | |
foreach (var child in item.Children) | |
{ | |
// 子对象改变依赖 | |
child.Parents.Remove(item); | |
child.Parents.Add(atlasAssetBundle); | |
// 图集增加被依赖项 | |
atlasAssetBundle.Children.Add(child); | |
} | |
} | |
} | |
} | |
// 移除原有的Packed Sprite | |
foreach (var item in allPackedSprites) | |
{ | |
bundles.Remove(item.AssetPath); | |
} | |
// 添加所有的Atlas | |
foreach (var kv in packingTagDict) | |
{ | |
bundles.Add(kv.Key, kv.Value); | |
} | |
/// TODO:检测所有的AssetBundle是否是需要重新导出 | |
return bundles; | |
} | |
private static void UpdateDependencyLink(Dictionary<string, AssetBundleManifest> allAssets, string asset, string[] dependencies) | |
{ | |
AssetBundleManifest abManifest; | |
if (allAssets.TryGetValue(asset, out abManifest)) | |
{ | |
foreach (var dependent in dependencies) | |
{ | |
if (dependent == asset) | |
{ | |
continue; | |
} | |
AssetBundleManifest dependentManifest; | |
if (allAssets.TryGetValue(dependent, out dependentManifest)) | |
{ | |
abManifest.Parents.Add(dependentManifest); | |
dependentManifest.Children.Add(abManifest); | |
} | |
} | |
} | |
} | |
public static string savePath = string.Empty; | |
public static void BuildAllAssetBundle() | |
{ | |
savePath = AssetBundleTool.SelectSaveFolderPath(); | |
if (string.IsNullOrEmpty(savePath)) | |
{ | |
return; | |
} | |
var pathList = AssetDatabase.FindAssets("l:AssetBundle").Select(p => AssetDatabase.GUIDToAssetPath(p)).ToList(); | |
BuildAssetBundle(pathList); | |
} | |
public static void BuildAssetBundle(List<string> pathList) | |
{ | |
var allAssets = BuildDependencyTree(pathList); | |
var leafs = allAssets.Values.Where(a => a.Children.Count == 0); | |
/// 打包所有的AssetBundle | |
foreach (var leaf in leafs) | |
{ | |
BuildAssetBundle(leaf); | |
} | |
var metaDB = System.IO.Path.Combine(savePath, "AssetBundleMetaList.txt"); | |
SimpleJSON.JSONClass jsonClass = new SimpleJSON.JSONClass(); | |
if (System.IO.File.Exists(metaDB)) | |
{ | |
var reader = System.IO.File.OpenText(metaDB); | |
string json = reader.ReadToEnd(); | |
reader.Close(); | |
jsonClass = SimpleJSON.JSON.Parse(json).AsObject; | |
} | |
foreach (var item in allAssets) | |
{ | |
var assetMeta = new SimpleJSON.JSONClass(); | |
foreach (var dependent in item.Value.Parents) | |
{ | |
assetMeta["dependencies"].Add(dependent.AssetPath); | |
} | |
jsonClass[item.Key] = assetMeta; | |
} | |
var temp = System.IO.Path.Combine(Application.dataPath, "../Temp/" + System.Guid.NewGuid().ToString("n")); | |
using (var fs = System.IO.File.OpenWrite(temp)) | |
{ | |
byte[] data = System.Text.Encoding.UTF8.GetBytes(jsonClass.ToString()); | |
fs.Write(data, 0, data.Length); | |
} | |
if (System.IO.File.Exists(metaDB)) | |
{ | |
System.IO.File.Delete(metaDB); | |
} | |
System.IO.File.Move(temp, metaDB); | |
} | |
private static bool BuildAssetBundle(AssetBundleManifest manifest) | |
{ | |
foreach (var item in manifest.Parents) | |
{ | |
BuildAssetBundle(item); | |
} | |
string path = savePath + manifest.AssetPath + ".ab"; | |
bool exist = System.IO.Directory.GetParent(path).Exists; | |
if (!exist) | |
{ | |
System.IO.Directory.CreateDirectory(System.IO.Directory.GetParent(path).FullName); | |
} | |
if (manifest.pushCount == 0) | |
{ | |
foreach (var item in manifest.Children) | |
{ | |
manifest.Push(); | |
} | |
if (manifest.Children.Count == 0) | |
{ | |
Debug.Log("Push :" + manifest.AssetPath); | |
BuildPipeline.PushAssetDependencies(); | |
} | |
Debug.Log("Build assetbundle: " + manifest.AssetPath); | |
if (manifest.IsAtlas) | |
{ | |
BuildAtlasAssetBundle(manifest.AssetPath, path); | |
} | |
else if (manifest.IsScene) | |
{ | |
string[] levels = new string[] { manifest.AssetPath }; | |
BuildPipeline.BuildStreamedSceneAssetBundle(levels, path, target, BuildOptions.BuildAdditionalStreamedScenes); | |
} | |
else | |
{ | |
bool result = BuildAssetBundle(manifest.AssetPath, path); | |
//if (!result) | |
//{ | |
// return result; | |
//} | |
} | |
if (manifest.Children.Count == 0) | |
{ | |
Debug.Log("Pop :" + manifest.AssetPath); | |
BuildPipeline.PopAssetDependencies(); | |
} | |
} | |
foreach (var item in manifest.Parents) | |
{ | |
item.Pop(); | |
} | |
return true; | |
} | |
private static Sprite[] GetAllSpriteInPackingTag(string packingTag) | |
{ | |
var allTextures = AssetDatabase.FindAssets("t:Sprite").Select(guid=>AssetDatabase.GUIDToAssetPath(guid)); | |
var sprites = (from path in allTextures select AssetImporter.GetAtPath(path) as TextureImporter into importer where importer.spritePackingTag == packingTag select AssetDatabase.LoadAssetAtPath(importer.assetPath, typeof(Sprite)) as Sprite).ToArray(); | |
return sprites; | |
} | |
private static bool BuildAtlasAssetBundle(string atlasName, string savePath) | |
{ | |
var sprites = GetAllSpriteInPackingTag(atlasName); | |
Debug.Log(string.Join(" ", sprites.Select(s => s.name).ToArray())); | |
var allTextures = AssetDatabase.FindAssets("t:Sprite").Select(guid => AssetDatabase.GUIDToAssetPath(guid)); | |
var atlasTextures = (from path in allTextures select AssetImporter.GetAtPath(path) as TextureImporter into importer where importer.spritePackingTag == atlasName select AssetDatabase.LoadAssetAtPath(importer.assetPath, typeof(Sprite)) as Object).ToArray(); | |
return BuildPipeline.BuildAssetBundle(atlasTextures[0], atlasTextures, savePath, options, target); | |
} | |
private static bool BuildAssetBundle(string assetPath, string savePath) | |
{ | |
var asset = AssetDatabase.LoadAssetAtPath(assetPath, typeof(UnityEngine.Object)); | |
if (asset.ToString().Contains("UnityEngine.DefaultAsset")) | |
{ | |
var oldActive = Selection.activeObject; | |
Selection.activeObject = asset; | |
var allAssets = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.DeepAssets); | |
Selection.activeObject = oldActive; | |
return BuildPipeline.BuildAssetBundle(null, allAssets, savePath, options, target); | |
} | |
else | |
{ | |
return BuildPipeline.BuildAssetBundle(asset, null, savePath, options, target); | |
} | |
} | |
private static long GetTimeStampAtPath(string path) | |
{ | |
return 0; | |
} | |
} | |
public class AssetBundleTool : Editor | |
{ | |
[MenuItem("Tools/BuildAssetbundle")] | |
static public void BuildAssetbundle() | |
{ | |
string savepath = SelectSaveFolderPath(); | |
if (string.IsNullOrEmpty(savepath)) | |
{ | |
return; | |
} | |
BuildTarget target = BuildTarget.StandaloneWindows; | |
#if UNITY_ANDROID | |
target = BuildTarget.Android; | |
#elif UNITY_IPHONE | |
target = BuildTarget.iPhone; | |
#endif | |
List<Object> assets = new List<Object>(); | |
foreach (Object o in Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets)) | |
{ | |
assets.Add(o); | |
} | |
BuildPipeline.BuildAssetBundle(Selection.activeObject, assets.ToArray(), savepath + "/" + Selection.activeObject.name + ".ab", BuildAssetBundleOptions.CollectDependencies, target); | |
} | |
[MenuItem("Tools/生成AssetBundle依赖")] | |
public static void BuildDependencies() | |
{ | |
var allAssets = AssetDatabase.FindAssets("l:AssetBundle").Select(p => AssetDatabase.GUIDToAssetPath(p)).ToList(); | |
var dependency = AssetBundleEditorHelper.BuildDependencyTree(allAssets); | |
SimpleJSON.JSONClass json = new SimpleJSON.JSONClass(); | |
foreach (var item in dependency.Values) | |
{ | |
var newAsset = new SimpleJSON.JSONClass(); | |
foreach (var parent in item.Parents) | |
{ | |
newAsset["dependencies"].Add(parent.AssetPath); | |
} | |
json[item.AssetPath] = newAsset; | |
} | |
Debug.Log("Dependencies :" + json.ToString()); | |
} | |
[MenuItem("Tools/生成所有AssetBundle")] | |
public static void BuildAllAssetBundle() | |
{ | |
AssetBundleEditorHelper.BuildAllAssetBundle(); | |
} | |
[MenuItem("Tools/生成选中的AssetBundle")] | |
public static void BuildAssetBundleSelected() | |
{ | |
string savepath = SelectSaveFolderPath(); | |
if (string.IsNullOrEmpty(savepath)) | |
{ | |
return; | |
} | |
AssetBundleEditorHelper.savePath = savepath; | |
var path = AssetDatabase.GetAssetPath(Selection.activeObject); | |
bool isMarkedAssetBundle = AssetDatabase.GetLabels(Selection.activeObject).Contains("AssetBundle"); | |
if (!isMarkedAssetBundle) | |
{ | |
Debug.LogError("选择的对象没有被标记为assetbundle, " + path); | |
return; | |
} | |
List<string> pathList = new List<string> { path }; | |
AssetBundleEditorHelper.BuildAssetBundle(pathList); | |
} | |
public static string SelectSaveFolderPath() | |
{ | |
string lastSavePath = EditorPrefs.GetString("LastAssetBundleSavePath", ""); | |
string path = EditorUtility.SaveFolderPanel("保存到", lastSavePath, ""); | |
EditorPrefs.SetString("LastAssetBundleSavePath", path); | |
if (string.IsNullOrEmpty(path)) | |
{ | |
return string.Empty; | |
} | |
return path + "/"; | |
} | |
[MenuItem("Tools/BuildStreamingScene")] | |
public static void BuildStreamingScene() | |
{ | |
var path = SelectSaveFolderPath(); | |
if (string.IsNullOrEmpty(path)) | |
{ | |
return; | |
} | |
BuildTarget target = BuildTarget.StandaloneWindows; | |
#if UNITY_ANDROID | |
target = BuildTarget.Android; | |
#elif UNITY_IPHONE | |
target = BuildTarget.iPhone; | |
#endif | |
List<string> scenePaths = new List<string>(); | |
var objs = Selection.objects; | |
for (int i = 0; i < objs.Length; i++) | |
{ | |
if (objs[i] && objs[i].ToString().Contains("UnityEngine.SceneAsset")) | |
{ | |
scenePaths.Add(AssetDatabase.GetAssetOrScenePath(objs[i])); | |
} | |
} | |
if (scenePaths.Count > 0) | |
{ | |
BuildPipeline.BuildStreamedSceneAssetBundle(scenePaths.ToArray(), path + "Scenes.ab", target, BuildOptions.BuildAdditionalStreamedScenes); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment