Unity5打包assetbundle
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
public class AssetBundleExporterGUI
{
private static readonly string BUNDLE_PATH_ART = System.Environment.CurrentDirectory +
"/../../../../../starts-art/Assetbundles/";
private static readonly string BUNDLE_PATH_GAME = System.Environment.CurrentDirectory +
"/../../../../../starts-game-assets/trunk/assetbundles/";
private const string IOS_BUNDLE_PATH = "ios/";
private const string ANDROID_BUNDLE_PATH = "android/";
private const string WEBPLAYER_BUNDLE_PATH = "webplayer/";
private const string ASSETBUNDLE_EXT = ".assetbundle";
private const string PREFAB_PATH = "Assets/_prefabs/{0}.prefab";
// This is our one-and-only dependency bundle. Any downloaded bundle can reference this,
// which means any assets within it won't be duplicated in any dependent bundle.
private static string PREFAB_EXT = ".prefab";
private static string SHARED_PREFAB = "gui_shared";
private static string SHARED_PREFAB_PATH = "Assets/_prefabs/" + SHARED_PREFAB;
private const string SHARED_BUNDLE = "gui_shared";
// These are the bundles that do not have any dependency. These bundles are intended to be
// loaded on-demand on the client because they can contain textures that we do not want to
// have always loaded in memory.
//
// IMPORTANT: None of these bundles should be in GUI_PRELOADED_SCREENS in AssetConstants.cs
// located in the starts client codebase. If you are adding a new independent bundle, please
// ensure this or communicate this to the dev implementing it.
//
// Bundles come in two categories:
// 1. always-loaded, does not contain its own textures, depends on gui_shared
// 2. loaded on-demand, does not depend on gui_shared
// All of the following are true:
// "If a bundle is always loaded, then it's dependent on gui_shared"
// "If a bundle is not always loaded, then it's not dependent on gui_shared"
// "If a bundle is dependent on gui_shared, then it's always loaded"
// "If a bundle is not dependent on gui_shared, then it's not always loaded"
// We have 1 exception:
// gui_hud depends on gui_shared and also has a few textures
//
private static readonly string[] independentBundles = new string[]
{
// The shared bundle doesn't depend on itself.
SHARED_BUNDLE,
"gui_xxxxxxxxx",
};
private const string HUD_BUNDLE = "gui_hud";
private static string selectedPath;
[MenuItem("Assets/StaRTS/Build Selected AssetBundle(s) - Android", false, 1)]
public static void ExportSelectionAndroid()
{
ExportSelectionTarget(BuildTarget.Android);
}
[MenuItem("Assets/StaRTS/Build Selected AssetBundle(s) - iOS", false, 2)]
public static void ExportSelectionIOS()
{
ExportSelectionTarget(BuildTarget.iOS);
}
[MenuItem("Assets/StaRTS/Build Selected AssetBundle(s) - WebPlayer", false, 3)]
public static void ExportSelectionWebPlayer()
{
ExportSelectionTarget(BuildTarget.WebPlayer);
}
// Useful for when you only want to export to a single target and no others.
private static void ExportSelectionTarget(BuildTarget target)
{
if (!Start())
{
return;
}
/*UnityEngine.Object[] prefabs;
List<string> dependencies;
if (!GetSelectedPrefabs(out prefabs, out dependencies))
{
return;
}*/
UnityEngine.Object[] prefabs = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.DeepAssets);
if (!ExportPrefabs(prefabs, target))
{
return;
}
Success();
}
// Useful for when you want all targets. It makes sure to order the targets smartly.
[MenuItem("Assets/StaRTS/Build Selected AssetBundle(s) - All Platforms", false, 4)]
public static void ExportSelectionAll()
{
if (!Start())
{
return;
}
/*UnityEngine.Object[] prefabs;
List<string> dependencies;
if (!GetSelectedPrefabs(out prefabs, out dependencies))
{
return;
}*/
List<BuildTarget> targets = new List<BuildTarget>();
targets.Add(BuildTarget.Android);
targets.Add(BuildTarget.iOS);
targets.Add(BuildTarget.WebPlayer);
// Ensure that the current build target is the first target in the
// list to minimize the amount of asset re-importing that is done.
// Whichever was used last will be used first in the subsequent export
for (int i = 0; i < targets.Count; i++)
{
if (targets[i] == EditorUserBuildSettings.activeBuildTarget)
{
if (i != 0)
{
targets.Insert(0, targets[i]);
targets.RemoveAt(i + 1);
}
break;
}
}
UnityEngine.Object[] prefabs = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.DeepAssets);
for (int i = 0; i < targets.Count; i++)
{
if (!ExportPrefabs(prefabs, targets[i]))
{
return;
}
}
Success();
}
static private bool GetSelectedPrefabs(out UnityEngine.Object[] prefabs, out List<string> dependencies)
{
dependencies = new List<string>();
// Begin() already ensured this was non-empty.
UnityEngine.Object[] selection = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.DeepAssets);
// Store prefab objects in a new array for multiple target
// exports. When the editor re-imports assets for a new target,
// the selection is lost.
prefabs = new UnityEngine.Object[selection.Length];
for (int i = 0; i < selection.Length; i++)
{
string bundleName = selection[i].name;
List<string> deps = new List<string>();
deps.Add(SHARED_BUNDLE);
for (int j = 0, jlen = independentBundles.Length; j < jlen; j++)
{
if (bundleName == independentBundles[j])
{
deps.Clear();
break;
}
}
if (i == 0)
{
dependencies.AddRange(deps);
}
else
{
bool matches = deps.Count == dependencies.Count;
if (matches)
{
for (int j = 0; j < deps.Count; j++)
{
if (dependencies[j].IndexOf(deps[j]) < 0)
{
matches = false;
break;
}
}
}
if (!matches)
{
// We require that all bundles to be built have the same dependencies.
Error("The selected bundles don't have the same dependencies.\n" +
"Try building self-contained bundles separately " +
"from those that depend on the shared bundle.");
return false;
}
}
prefabs[i] = selection[i];
}
return true;
}
private static bool ExportPrefabs(
UnityEngine.Object[] prefabs, BuildTarget target)
{
Debug.Log("Exporting bundles for " + target);
EditorUserBuildSettings.SwitchActiveBuildTarget(target);
SetPathAndExportBundle(prefabs, target);
return true;
}
// Returns true if the given prefab has no textures of its own,
// outside of the given dependency prefab.
// If returns false, assetNames will be set to a newline-separated list of offending assets.
private static bool IsValidDependency(UnityEngine.Object prefab, UnityEngine.Object dependencyPrefab,
out string assetNames)
{
bool valid = true;
assetNames = null;
string[] prefabPaths = new string[] { string.Format(PREFAB_PATH, prefab.name) };
string[] prefabAssets = AssetDatabase.GetDependencies(prefabPaths);
string[] dependencyPaths = new string[]
{ string.Format(PREFAB_PATH, dependencyPrefab.name) };
string[] dependencyAssets = AssetDatabase.GetDependencies(dependencyPaths);
// If everything in the prefab is also in the dependency bundle, then none of it will
// actually get bundled with the prefab, and that's the goal.
for (int i = 0, iCount = prefabAssets.Length; i < iCount; i++)
{
string assetName = prefabAssets[i];
// These are the legal asset types. Anything else is considered illegal to have in an
// depdendent bundle that isn't also inside the depdendency bundle.
// NOTE: Mainly we want to check .png, .ttf, .prefab, .shader, but rather than only
// checking those, it's safer to do the reverse: allow known legal assets to be skipped.
// That way, any new type of asset that we find will not be skipped and either it's a
// proper dependency, or it'll cause the error to pop, and we'll consider what to do
// about it. Either fix the asset, or add the new extension to our "legal" list.
if (assetName == prefabPaths[0] || // Ignore the root object.
assetName.EndsWith(".cs") || // Scripts only have references bundled.
assetName.EndsWith(".mat") || // Materials are lightweight, they reference shaders
// and textures, which themselves are prevented.
assetName.EndsWith(".controller") ||
assetName.EndsWith(".anim") ||
assetName.EndsWith(".fbx"))
{
continue;
}
bool found = false;
for (int j = 0, jCount = dependencyAssets.Length; j < jCount; j++)
{
if (assetName == dependencyAssets[j])
{
found = true;
break;
}
}
if (!found)
{
valid = false;
assetNames = assetNames == null ? assetName : assetNames + "\n" + assetName;
}
}
return valid;
}
private static bool Start()
{
UnityEngine.Object[] selection = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.DeepAssets);
if (selection.Length < 1)
{
Debug.Log("No prefabs were selected to bundle!");
return false;
}
return AskForExportPath();
}
private static void Success()
{
EditorUtility.DisplayDialog("Export complete!", string.Empty , "OK", string.Empty );
}
private static void Error(string error)
{
Debug.LogError(error);
EditorUtility.DisplayDialog("Error", error, "OK", string.Empty);
}
private static bool AskForExportPath()
{
int option = EditorUtility.DisplayDialogComplex(
"Where would you like the bundles saved?",
"Choose starts-art to export the bundle for an engineer to grab " +
"for integration. Choose starts-game-assets to test the bundle " +
"locally and potentially commit to production.",
"starts-art",
"Other",
"starts-game-assets");
string baseDirectory = string.Empty ;
switch (option)
{
case 0:
baseDirectory = BUNDLE_PATH_ART;
break;
case 1:
baseDirectory = EditorUtility.OpenFolderPanel(
"Select the base export directory", string.Empty , string.Empty) + "/";
break;
case 2:
baseDirectory = BUNDLE_PATH_GAME;
break;
default:
Debug.Log("Invalid selection for export."); // Shouldn't be possible.
return false;
}
// Verify path exists
if (baseDirectory == string.Empty || !Directory.Exists(baseDirectory))
{
Error("Base directory does not exist: " + baseDirectory);
return false;
}
selectedPath = baseDirectory;
// Create subdirectories if they do not exist
if (!Directory.Exists(baseDirectory + ANDROID_BUNDLE_PATH))
{
Directory.CreateDirectory(baseDirectory + ANDROID_BUNDLE_PATH);
}
if (!Directory.Exists(baseDirectory + IOS_BUNDLE_PATH))
{
Directory.CreateDirectory(baseDirectory + IOS_BUNDLE_PATH);
}
if (!Directory.Exists(baseDirectory + WEBPLAYER_BUNDLE_PATH))
{
Directory.CreateDirectory(baseDirectory + WEBPLAYER_BUNDLE_PATH);
}
return true;
}
private static void SetPathAndExportBundle(UnityEngine.Object[] prefabs, BuildTarget target)
{
string path = selectedPath;
switch (target)
{
case BuildTarget.iOS:
path += IOS_BUNDLE_PATH;
break;
case BuildTarget.Android:
path += ANDROID_BUNDLE_PATH;
break;
case BuildTarget.WebPlayer:
path += WEBPLAYER_BUNDLE_PATH;
break;
}
ExportBundle(prefabs, path, target, false);
}
static private bool ExportBundle(UnityEngine.Object[] prefab, string path, BuildTarget target, bool combined)
{
string directoryPath = Path.GetDirectoryName(path);
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
AssetBundleBuild[] buildMap;
List<UnityEngine.Object> independentBundles = new List<UnityEngine.Object>();
string prefabPath;
if (combined)
{
buildMap = new AssetBundleBuild[2];
buildMap[0].assetBundleName = SHARED_PREFAB + ASSETBUNDLE_EXT;
buildMap[0].assetNames = AssetDatabase.GetDependencies(new string[]{SHARED_PREFAB_PATH + PREFAB_EXT});
buildMap[1].assetBundleName = prefab[0].name + ASSETBUNDLE_EXT;
for (int i = 0; i < prefab.Length; i++)
{
prefabPath = AssetDatabase.GetAssetPath(prefab[i]);
buildMap[1].assetNames = new string[]{prefabPath};
}
}
else
{
buildMap = new AssetBundleBuild[prefab.Length + 1];
buildMap[0].assetBundleName = SHARED_PREFAB + ASSETBUNDLE_EXT;
buildMap[0].assetNames = AssetDatabase.GetDependencies(new string[] {SHARED_PREFAB_PATH + PREFAB_EXT});
for (int i = 0; i < prefab.Length; i++)
{
prefabPath = AssetDatabase.GetAssetPath(prefab[i]);
if (IsIndependentBundle(prefab[i].name))
{
independentBundles.Add(prefab[i]);
}
else
{
buildMap[i + 1].assetBundleName = prefab[i].name + ASSETBUNDLE_EXT;
buildMap[i + 1].assetNames = new string[]{prefabPath};
}
}
}
BuildPipeline.BuildAssetBundles(directoryPath, buildMap, BuildAssetBundleOptions.ForceRebuildAssetBundle, target);
if (independentBundles.Count > 0)
{
buildMap = new AssetBundleBuild[independentBundles.Count];
for (int i = 0; i < independentBundles.Count; i++)
{
prefabPath = AssetDatabase.GetAssetPath(independentBundles[i]);
buildMap[i].assetBundleName = independentBundles[i].name + ASSETBUNDLE_EXT;
buildMap[i].assetNames = new string[]{prefabPath};
}
BuildPipeline.BuildAssetBundles(directoryPath, buildMap, BuildAssetBundleOptions.ForceRebuildAssetBundle, target);
}
return true;
}
static private bool IsIndependentBundle(string prefabName)
{
int index = Array.FindIndex(independentBundles, item => item.Equals(prefabName));
if (index > 0)
{
return true;
}
return false;
}
}