Skip to content

Instantly share code, notes, and snippets.

@twsiyuan
Created April 20, 2017 07:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save twsiyuan/b919031bd50f1b9bc7aa9cd06762079f to your computer and use it in GitHub Desktop.
Save twsiyuan/b919031bd50f1b9bc7aa9cd06762079f to your computer and use it in GitHub Desktop.
using System;
[Serializable]
class AssetBundleLoadConfigs
{
public AssetBundleLoadData[] AssetBundles;
}
using System;
using UnityEngine;
[Serializable]
struct AssetBundleLoadData
{
public string Name;
public string Hash;
public uint CRC;
public Hash128 Hash128
{
get
{
return Hash128.Parse(this.Hash);
}
set
{
this.Hash = value.ToString();
}
}
}
static class BuildExample
{
[MenuItem("Tools/Build Windows64")]
static void BuildWindows64()
{
var path = Path.GetFullPath(Application.dataPath + "/../Builds/Windows64/" + Application.productName + ".exe");
BuildProject(path, BuildTarget.StandaloneWindows64);
}
[MenuItem("Tools/Build Android")]
static void BuildAndroid()
{
var path = Path.GetFullPath(Application.dataPath + "/../Builds/Android/" + Application.productName + ".apk");
BuildProject(path, BuildTarget.Android);
}
const string AssetBundleExtension = ".assetbundle";
const string DefaultAssetBundleName = "base";
const string ScenesAssetBundleName = "scenes";
static string[] CollectScenesPathWithoutEntry()
{
var paths = new List<string>(EditorBuildSettings.scenes.Where(v => v.enabled).Select(v => v.path));
paths.RemoveAt(0);
return paths.ToArray();
}
static AssetBundleManifest BuildAssetBundles(string outputPath, BuildTarget buildTarget, BuildAssetBundleOptions buildAssetBundleOptions = BuildAssetBundleOptions.None)
{
// Data strcuture & function define
var buildAssetBundles = new Dictionary<string, List<string>>();
var isAssetCollected = (Func<string, bool>)((assetPath) => {
foreach (var assetBundle in buildAssetBundles)
{
if (assetBundle.Value.Contains(assetPath))
{
return true;
}
}
return false;
});
var collectAsset = (Action<string, string>)((assetPath, assetBundleName) =>
{
if (!isAssetCollected(assetPath))
{
var assetsList = (List<string>)null;
if (!buildAssetBundles.TryGetValue(assetBundleName, out assetsList))
{
assetsList = new List<string>();
buildAssetBundles.Add(assetBundleName, assetsList);
}
assetsList.Add(assetPath);
}
});
var toAssetBundleBuilds = (Func<AssetBundleBuild[]>)(() =>
{
var builds = new AssetBundleBuild[buildAssetBundles.Count];
var i = 0;
foreach (var itr in buildAssetBundles)
{
builds[i] = new AssetBundleBuild()
{
assetBundleName = itr.Key,
assetNames = itr.Value.ToArray(),
};
i += 1;
}
return builds;
});
// For each dependencies assets
var scenePaths = CollectScenesPathWithoutEntry();
foreach (var assetPath in AssetDatabase.GetDependencies(scenePaths))
{
if (assetPath.StartsWith("Assets/") && Path.GetExtension(assetPath) != ".cs")
{
var importer = AssetImporter.GetAtPath(assetPath);
var bundleName = string.IsNullOrEmpty(importer.assetBundleName) ? DefaultAssetBundleName : importer.assetBundleName;
collectAsset(assetPath, bundleName + AssetBundleExtension);
}
}
var assetBundleBuilds = new List<AssetBundleBuild>(toAssetBundleBuilds());
assetBundleBuilds.Add(new AssetBundleBuild
{
assetBundleName = ScenesAssetBundleName + AssetBundleExtension,
assetNames = CollectScenesPathWithoutEntry(),
});
return BuildPipeline.BuildAssetBundles(outputPath, assetBundleBuilds.ToArray(), buildAssetBundleOptions, buildTarget);
}
static string[] GetAssetBundleNamesInLoadOrder(AssetBundleManifest manifest)
{
var assetBundleRefs = new Dictionary<string, int>();
foreach (var assetBundle in manifest.GetAllAssetBundles())
{
assetBundleRefs.Add(assetBundle, 0);
}
foreach (var assetBundle in manifest.GetAllAssetBundles())
{
foreach (var depAssetBundle in manifest.GetAllDependencies(assetBundle))
{
assetBundleRefs[depAssetBundle] += 1;
}
}
return assetBundleRefs.OrderByDescending(kp => kp.Value).Select(v => v.Key).ToArray();
}
static bool GetAssetBundleHashAndCRC(string outputPath, string assetBundleName, out Hash128 hash, out uint crc)
{
var assetBundlePath = Path.GetFullPath(outputPath) + Path.DirectorySeparatorChar + assetBundleName;
if (!BuildPipeline.GetHashForAssetBundle(assetBundlePath, out hash))
{
crc = 0;
return false;
}
if (!BuildPipeline.GetCRCForAssetBundle(assetBundlePath, out crc))
{
return false;
}
return true;
}
static AssetBundleLoadConfigs GenerateLoadConfigs(string manifestDir, AssetBundleManifest manifest, string configsName = "configs.json")
{
var assetBundleNames = GetAssetBundleNamesInLoadOrder(manifest);
var assetBundleLoads = new List<AssetBundleLoadData>(assetBundleNames.Length);
foreach (var assetBundleName in assetBundleNames)
{
Hash128 hash;
uint crc;
if (!GetAssetBundleHashAndCRC(manifestDir, assetBundleName, out hash, out crc))
{
throw new Exception("Cannot find assetBundle - " + assetBundleName);
}
assetBundleLoads.Add(new AssetBundleLoadData()
{
Name = assetBundleName,
Hash128 = hash,
CRC = crc,
});
}
var configs = new AssetBundleLoadConfigs()
{
AssetBundles = assetBundleLoads.ToArray(),
};
var configsPath = Path.GetFullPath(manifestDir) + Path.DirectorySeparatorChar + configsName;
File.WriteAllText(configsPath, JsonUtility.ToJson(configs));
return configs;
}
static void BuildProject(string outputPath, BuildTarget buildTarget = BuildTarget.Android, BuildOptions buildOptions = BuildOptions.None, BuildAssetBundleOptions buildAssetBundleOptions = BuildAssetBundleOptions.None)
{
// Check output path
var bundleOutputDir = Path.GetFullPath(Path.GetDirectoryName(outputPath));
var playerOutputPath = outputPath;
if (!Directory.Exists(bundleOutputDir))
{
Directory.CreateDirectory(bundleOutputDir);
}
// Build assetbundles
var manifest = BuildAssetBundles(bundleOutputDir, buildTarget, buildAssetBundleOptions);
GenerateLoadConfigs(bundleOutputDir, manifest);
// Build Player
var bundleManifestPath = bundleOutputDir + Path.DirectorySeparatorChar + Path.GetFileName(bundleOutputDir) + ".manifest";
var buildPlayerOptions = new BuildPlayerOptions()
{
target = buildTarget,
scenes = new string[] { EditorBuildSettings.scenes.Where(v => v.enabled).Select(v => v.path).First() },
options = buildOptions,
locationPathName = playerOutputPath,
assetBundleManifestPath = bundleManifestPath,
};
var result = BuildPipeline.BuildPlayer(buildPlayerOptions);
if (!string.IsNullOrEmpty(result))
{
throw new Exception(result);
}
// Open folder if not batch mode
if (!UnityEditorInternal.InternalEditorUtility.inBatchMode)
{
System.Diagnostics.Process.Start(bundleOutputDir);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment