您的位置:首页 > 移动开发 > Unity3D

Unity5打包assetbundle

2016-04-29 15:42 477 查看

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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: