Hi, all.
For those of you who lack the tool to get better understanding what is inside your asset bundle, I introduce “Asset Bundle Analyzer”. Tool is completely free of charge and you can integrate it in your project by simply copying this source code in your “Editor” folder. After that you can open utility window from Unity menu (Tools->Analyze Asset Bundle). Than you should select asset bundle you want to analyze in your project tab and press ‘analyze’ button. Also you can specify folder for temp files.
Utility output looks like:
Here you can analyze what types of resources present in your bundle and what is the amount of storage space they occupy.
This is very useful if you want to understand why your asset bundle is so large or if you want to check that all shaders are extracted into separate asset bundle.
Also note ‘Compress’ checkbox. Let’s assume that all your bundles are compressed. So you can check this box and analyze approximate size of resource in you bundle. If you uncheck it you can analyze approximate run-time memory usage for resources in bundle.
Unsolved problems:
By the moment tool doesn’t analyze memory that used for components, only assets and Transform components are taken into account.
Platform detection of incoming asset bundle required. By the moment tool supports autodetection for android and ios by asset bundle name (if it contains ‘-android’ or ‘-ios’ in it’s name).
Special notes:
Resource sizes returned by the tool are just an approximation, but in practice precision for everything which is more than 1 Kb is good enough.
AssetBundleAnalyzer.cs:
using System.IO;
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
public class AssetBundleAnalyzer : EditorWindow {
private struct StatData {
public string typeName;
public string objName;
public int size;
}
private class StatDataComparer : IComparer<StatData> {
Dictionary<string, int> _typeSizeMap;
public StatDataComparer(Dictionary<string, int> typeSizeMap)
{
_typeSizeMap = typeSizeMap;
}
public int Compare(StatData stat1, StatData stat2)
{
int typeSize1 = _typeSizeMap[stat1.typeName];
int typeSize2 = _typeSizeMap[stat2.typeName];
int stringCompare = stat1.typeName.CompareTo(stat2.typeName);
if (typeSize1 > typeSize2) {
return -1;
} else if (typeSize1 < typeSize2) {
return +1;
} else if (stringCompare != 0) {
return stringCompare;
} else if (stat1.size > stat2.size) {
return -1;
} else if (stat1.size < stat2.size) {
return +1;
} else {
return 0;
}
}
}
private Vector2 scrollPosition = Vector2.zero;
private bool compressed = false;
private string outputPath = "";
private string selectedPath = "";
private Object analyzedObject = null;
private List<StatData> statistics = new List<StatData>();
private Dictionary<string, int > typeSizeMap = new Dictionary<string, int>();
private Dictionary<string, bool> typeStatusMap = new Dictionary<string, bool>();
[MenuItem("Tools/Analyze Asset Bundle")]
public static void ShowWindow()
{
EditorWindow.GetWindow(typeof(AssetBundleAnalyzer));
}
void OnGUI()
{
Object currentObject = null;
string assetPath = "";
if (Selection.activeObject != null) {
assetPath = Path.GetFullPath(AssetDatabase.GetAssetPath(Selection.activeObject));
if (assetPath.ToLower().Contains(".bytes")) {
currentObject = Selection.activeObject;
}
}
GUILayout.Label ("Asset bundle to analyze", EditorStyles.boldLabel);
if (currentObject != null) {
FileInfo file = new FileInfo(assetPath);
GUILayout.Label(" file: " + assetPath);
GUILayout.Label(" size: " + file.Length/1024 + " Kb");
} else {
GUILayout.Label(" file: None (select in project)");
GUILayout.Label(" size: 0 Kb");
}
GUILayout.Label ("Settings", EditorStyles.boldLabel);
compressed = EditorGUILayout.Toggle (" Compress", compressed);
EditorGUILayout.BeginHorizontal();
{
selectedPath = EditorGUILayout.TextField(" Output path: ", selectedPath);
if (GUILayout.Button("select", GUILayout.Width(50))) {
selectedPath = EditorUtility.SaveFolderPanel ("Select output directory", "", selectedPath);
}
outputPath = selectedPath + "/.analyze";
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
if (GUILayout.Button ("analyze", GUILayout.Width (100)) currentObject != null) {
if (outputPath.Length == 0) {
Debug.LogError("Please select valid output path");
return;
}
try {
if (Directory.Exists(outputPath)) {
Directory.Delete(outputPath, true);
}
} finally {
Directory.CreateDirectory(outputPath);
}
if (!Directory.Exists(outputPath)) {
Debug.LogError("Please select valid output path");
return;
}
// detect platform
BuildTarget buildTarget = BuildTarget.Android;
if (assetPath.Contains("-ios")) {
buildTarget = BuildTarget.iPhone;
} else if (assetPath.Contains("-android")) {
buildTarget = BuildTarget.Android;
} else {
Debug.LogWarning("Original asset bundle platform not detected. Android selected as default");
}
analyzeAssetBundle(currentObject, buildTarget);
}
if (analyzedObject != currentObject) {
statistics.Clear();
analyzedObject = null;
}
if (analyzedObject != null) {
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
{
string curType = "";
foreach(StatData data in statistics) {
if (curType != data.typeName) {
EditorGUILayout.BeginHorizontal();
{
typeStatusMap[data.typeName] = EditorGUILayout.Foldout(typeStatusMap[data.typeName], data.typeName);
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUI.skin.label.fontStyle = FontStyle.Bold;
GUILayout.Label(typeSizeMap[data.typeName]/1024 + " Kb");
GUILayout.Space(400);
}
EditorGUILayout.EndHorizontal();
curType = data.typeName;
}
if (typeStatusMap[data.typeName]) {
EditorGUILayout.BeginHorizontal();
{
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUI.skin.label.fontStyle = FontStyle.Normal;
GUILayout.Label(" " + data.objName);
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label(data.size/1024 + " Kb");
GUILayout.Space(400);
}
EditorGUILayout.EndHorizontal();
}
}
}
EditorGUILayout.EndScrollView();
}
}
private void analyzeAssetBundle(Object obj, BuildTarget buildTarget)
{
typeStatusMap.Clear();
typeSizeMap.Clear();
statistics.Clear();
analyzedObject = obj;
string assetPath = Path.GetFullPath(AssetDatabase.GetAssetPath(obj));
WWW www = new WWW("file:///" + assetPath);
Object[] loadedObjects = www.assetBundle.LoadAll();
foreach(Object loadedObj in loadedObjects) {
string directory = outputPath + "/" + loadedObj.GetType().FullName + "/";
if (!Directory.Exists(directory)) {
Directory.CreateDirectory(directory);
}
string bundlePath = directory + loadedObj.name.Replace("/", ".") + "." + loadedObj.GetInstanceID() + ".bytes";
BuildPipeline.BuildAssetBundle(loadedObj, null, bundlePath,
compressed ? 0 : BuildAssetBundleOptions.UncompressedAssetBundle,
buildTarget);
if (File.Exists(bundlePath)) {
StatData stat = new StatData();
stat.objName = loadedObj.name;
stat.typeName = loadedObj.GetType().FullName;
FileInfo fileInfo = new FileInfo(bundlePath);
stat.size = (int) fileInfo.Length;
statistics.Add(stat);
}
}
www.assetBundle.Unload(true);
www.Dispose();
// fill type size map
foreach(StatData data in statistics) {
if (typeSizeMap.ContainsKey(data.typeName)) {
typeSizeMap[data.typeName] += data.size;
} else {
typeSizeMap.Add(data.typeName, data.size);
}
}
// fill type status map
foreach(string typeName in typeSizeMap.Keys) {
typeStatusMap.Add(typeName, false);
}
statistics.Sort(new StatDataComparer(typeSizeMap));
}
}
I had the same problem at the start, but if you look at his comments you’ll see that he only had it set up for ios or android.
It should not take you much to adjust it to your needs. Try this:
Comment out lines #66#68.
Then comment out the line at #123:
Debug.LogWarning("Original asset bundle platform not detected. Android selected as default");
and then insert this in the else block:
buildTarget = BuildTarget.StandaloneWindows;
and it should work for you in the editor (although there may be something else I missed, but I think that’s it).
You can then adjust based on your project’s needs.
It has certainly been a helpful tool for me.
It throws warnings about missing components and that I should use BuildAssetBundleOptions.CollectDependencies (which I am). A visual verification seems to indicate that everything is there and the warning is wrong.
Ya it does not seem to work with unity 5. i have received the following error…,…
Assets/AssetBundleManager/AssetBundleAnalyzer.cs(179,53): error CS0619: UnityEngine.AssetBundle.LoadAll()' is obsolete: Method LoadAll has been deprecated. Script updater cannot update it as the loading behaviour has changed. Please use LoadAllAssets instead and check the documentation for details.’
Pardon for bumping. Just adding useful info to internet traveller.
Since this thread appear very early on google search asset bundle size analyze
Hey, just to remind everyone that when building an asset bundle, editor will produce editor log file similar to when building a standalone player. You can watch size composition there too. No special tool. No magic. No 200-lines script is needed.
As to my knowledge, these editor log cannot be turned off, (It is always output) it is part of editor core function.
Be noted that asset bundle system will NOT rebuild asset bundle if it is up to date, thus no log output even if you command them to build. Try remove the output files and start build fresh.
Or check the location of editor log file manually. Mine is at C:\Users\{USERNAME}\AppData\Local\Unity\Editor
Or maybe try close editor, delete log files, and start again to see if it is happily outputting the log?