You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
529 lines
18 KiB
529 lines
18 KiB
#if UNITY_EDITOR |
|
using UnityEditor; |
|
#endif |
|
using UnityEngine; |
|
using System.Collections; |
|
using System.Collections.Generic; |
|
using System; |
|
using System.IO; |
|
|
|
// 已加载的assetBundle引用计数 |
|
public class LoadedAssetBundle |
|
{ |
|
public AssetBundle m_AssetBundle; |
|
public int m_ReferencedCount; |
|
|
|
public LoadedAssetBundle(AssetBundle assetBundle) |
|
{ |
|
m_AssetBundle = assetBundle; |
|
m_ReferencedCount = 1; |
|
} |
|
} |
|
|
|
// AB管理类 |
|
public class AssetBundleManager : Singleton<AssetBundleManager> |
|
{ |
|
static string m_BaseDownloadingURL = ""; |
|
static string[] m_Variants = { }; |
|
static AssetBundleManifest m_AssetBundleManifest = null; |
|
#if UNITY_EDITOR |
|
static int m_SimulateAssetBundleInEditor = -1; |
|
const string kSimulateAssetBundles = "SimulateAssetBundles"; |
|
#endif |
|
|
|
static Dictionary<string, LoadedAssetBundle> m_LoadedAssetBundles = new Dictionary<string, LoadedAssetBundle>(); |
|
static Dictionary<string, WWW> m_DownloadingWWWs = new Dictionary<string, WWW>(); |
|
static Dictionary<string, string> m_DownloadingErrors = new Dictionary<string, string>(); |
|
static List<AssetBundleLoadOperation> m_InProgressOperations = new List<AssetBundleLoadOperation>(); |
|
static Dictionary<string, string[]> m_Dependencies = new Dictionary<string, string[]>(); |
|
|
|
// 下载路径 |
|
public static string BaseDownloadingURL |
|
{ |
|
get { return m_BaseDownloadingURL; } |
|
set { m_BaseDownloadingURL = value; } |
|
} |
|
|
|
// |
|
public static string[] Variants |
|
{ |
|
get { return m_Variants; } |
|
set { m_Variants = value; } |
|
} |
|
|
|
// AssetBundleManifest |
|
public static AssetBundleManifest AssetBundleManifestObject |
|
{ |
|
set { m_AssetBundleManifest = value; } |
|
} |
|
const string AssetBundlesPath = "/AssetBundles/"; |
|
|
|
|
|
//初始化 |
|
protected IEnumerator Initialize() |
|
{ |
|
#if UNITY_EDITOR |
|
//Debug.Log("当前模式:" + (SimulateAssetBundleInEditor ? "模拟模式" : "正常模式")); |
|
#endif |
|
|
|
string platformFolderForAssetBundles = |
|
#if UNITY_EDITOR |
|
GetPlatformFolderForAssetBundles(EditorUserBuildSettings.activeBuildTarget); |
|
#else |
|
GetPlatformFolderForAssetBundles(Application.platform); |
|
#endif |
|
|
|
//下载路径 |
|
string relativePath = GetRelativePath(); |
|
BaseDownloadingURL = relativePath + AssetBundlesPath + platformFolderForAssetBundles + "/"; |
|
var request = Initialize(platformFolderForAssetBundles); |
|
if (request != null) |
|
{ |
|
yield return StartCoroutine(request); |
|
} |
|
Initializing = false; |
|
} |
|
// 真实路径 |
|
public string GetRelativePath() |
|
{ |
|
if (Application.isEditor) |
|
//return "file://" + System.Environment.CurrentDirectory.Replace("\\", "/"); |
|
return "file://" + Application.streamingAssetsPath; |
|
//else if (Application.isWebPlayer) |
|
// return Path.GetDirectoryName(Application.absoluteURL).Replace("\\", "/") + "/StreamingAssets"; |
|
else if (Application.isMobilePlatform || Application.isConsolePlatform) |
|
return Application.streamingAssetsPath; |
|
else |
|
return "file://" + Application.streamingAssetsPath; |
|
} |
|
|
|
#if UNITY_EDITOR |
|
public static string GetPlatformFolderForAssetBundles(BuildTarget target) |
|
{ |
|
switch (target) |
|
{ |
|
case BuildTarget.Android: |
|
return "Android"; |
|
case BuildTarget.iOS: |
|
return "iOS"; |
|
case BuildTarget.WebGL: |
|
return "WebGL"; |
|
case BuildTarget.StandaloneWindows: |
|
case BuildTarget.StandaloneWindows64: |
|
return "Windows"; |
|
case BuildTarget.StandaloneOSXIntel: |
|
case BuildTarget.StandaloneOSXIntel64: |
|
case BuildTarget.StandaloneOSX: |
|
return "OSX"; |
|
default: |
|
return null; |
|
} |
|
} |
|
#endif |
|
|
|
static string GetPlatformFolderForAssetBundles(RuntimePlatform platform) |
|
{ |
|
switch (platform) |
|
{ |
|
case RuntimePlatform.Android: |
|
return "Android"; |
|
case RuntimePlatform.IPhonePlayer: |
|
return "iOS"; |
|
case RuntimePlatform.WebGLPlayer: |
|
return "WebGL"; |
|
case RuntimePlatform.WindowsPlayer: |
|
return "Windows"; |
|
case RuntimePlatform.OSXPlayer: |
|
return "OSX"; |
|
default: |
|
return null; |
|
} |
|
} |
|
//IEnumerator Start() |
|
//{ |
|
// yield return StartCoroutine(Initialize()); |
|
//} |
|
public bool Initializing = false; |
|
public IEnumerator Load<T>(string assetBundleName, string assetName, Action<T> callback) |
|
where T : UnityEngine.Object |
|
{ |
|
while (Initializing) |
|
{ |
|
yield return new WaitForEndOfFrame(); |
|
} |
|
if (m_AssetBundleManifest == null) |
|
{ |
|
//Debug.Log("未初始化!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); |
|
Initializing = true; |
|
yield return StartCoroutine(Initialize()); |
|
} |
|
//Debug.Log(string.Format("在第{0}帧开始加载{1}", Time.frameCount, assetName)); |
|
|
|
// 从AB中加载资源 |
|
AssetBundleLoadAssetOperation request = LoadAssetAsync(assetBundleName, assetName, typeof(T)); |
|
if (request == null) |
|
yield break; |
|
yield return StartCoroutine(request); |
|
|
|
// 获取资源T |
|
T asset = request.GetAsset<T>(); |
|
//Debug.Log(string.Format("加载{0}在第{1}帧时{2}", assetName, Time.frameCount, (asset == null ? "失败" : "成功"))); |
|
if (asset != null) |
|
{ |
|
callback(asset); |
|
//添加到资源缓存池 |
|
ResourceManager.AddAsset(asset.name, asset); |
|
UnloadAssetBundle(assetBundleName); |
|
} |
|
} |
|
//protected IEnumerator LoadLevel(string assetBundleName, string levelName, bool isAdditive) |
|
//{ |
|
// Debug.Log("Start to load scene " + levelName + " at frame " + Time.frameCount); |
|
|
|
// // Load level from assetBundle. |
|
// AssetBundleLoadOperation request = AssetBundleManager.LoadLevelAsync(assetBundleName, levelName, isAdditive); |
|
// if (request == null) |
|
// yield break; |
|
// yield return StartCoroutine(request); |
|
|
|
// // This log will only be output when loading level additively. |
|
// Debug.Log("Finish loading scene " + levelName + " at frame " + Time.frameCount); |
|
//} |
|
#if UNITY_EDITOR |
|
// 是否要在编辑器中模拟assetbundle,而不实际构建它们 |
|
public static bool SimulateAssetBundleInEditor |
|
{ |
|
get |
|
{ |
|
if (m_SimulateAssetBundleInEditor == -1) |
|
m_SimulateAssetBundleInEditor = EditorPrefs.GetBool(kSimulateAssetBundles, true) ? 1 : 0; |
|
|
|
return m_SimulateAssetBundleInEditor != 0; |
|
} |
|
set |
|
{ |
|
int newValue = value ? 1 : 0; |
|
if (newValue != m_SimulateAssetBundleInEditor) |
|
{ |
|
m_SimulateAssetBundleInEditor = newValue; |
|
EditorPrefs.SetBool(kSimulateAssetBundles, value); |
|
} |
|
} |
|
} |
|
#endif |
|
|
|
// 获取已加载的AssetBundle,只在成功下载所有依赖项时返回vaild对象。 |
|
static public LoadedAssetBundle GetLoadedAssetBundle(string assetBundleName, out string error) |
|
{ |
|
if (m_DownloadingErrors.TryGetValue(assetBundleName, out error)) |
|
return null; |
|
|
|
LoadedAssetBundle bundle = null; |
|
m_LoadedAssetBundles.TryGetValue(assetBundleName, out bundle); |
|
if (bundle == null) |
|
return null; |
|
|
|
// No dependencies are recorded, only the bundle itself is required. |
|
string[] dependencies = null; |
|
if (!m_Dependencies.TryGetValue(assetBundleName, out dependencies)) |
|
return bundle; |
|
|
|
// Make sure all dependencies are loaded |
|
foreach (var dependency in dependencies) |
|
{ |
|
if (m_DownloadingErrors.TryGetValue(assetBundleName, out error)) |
|
return bundle; |
|
|
|
// Wait all the dependent assetBundles being loaded. |
|
LoadedAssetBundle dependentBundle; |
|
m_LoadedAssetBundles.TryGetValue(dependency, out dependentBundle); |
|
if (dependentBundle == null) |
|
return null; |
|
} |
|
|
|
return bundle; |
|
} |
|
|
|
// Load AssetBundleManifest. |
|
static public AssetBundleLoadManifestOperation Initialize(string manifestAssetBundleName) |
|
{ |
|
#if UNITY_EDITOR |
|
// If we're in Editor simulation mode, we don't need the manifest assetBundle. |
|
if (SimulateAssetBundleInEditor) |
|
return null; |
|
#endif |
|
|
|
LoadAssetBundle(manifestAssetBundleName, true); |
|
var operation = new AssetBundleLoadManifestOperation(manifestAssetBundleName, "AssetBundleManifest", typeof(AssetBundleManifest)); |
|
m_InProgressOperations.Add(operation); |
|
return operation; |
|
} |
|
|
|
// Load AssetBundle and its dependencies. |
|
static protected void LoadAssetBundle(string assetBundleName, bool isLoadingAssetBundleManifest = false) |
|
{ |
|
#if UNITY_EDITOR |
|
// If we're in Editor simulation mode, we don't have to really load the assetBundle and its dependencies. |
|
if (SimulateAssetBundleInEditor) |
|
return; |
|
#endif |
|
|
|
if (!isLoadingAssetBundleManifest) |
|
assetBundleName = RemapVariantName(assetBundleName); |
|
|
|
// Check if the assetBundle has already been processed. |
|
bool isAlreadyProcessed = LoadAssetBundleInternal(assetBundleName, isLoadingAssetBundleManifest); |
|
|
|
// Load dependencies. |
|
if (!isAlreadyProcessed && !isLoadingAssetBundleManifest) |
|
LoadDependencies(assetBundleName); |
|
} |
|
|
|
// Remaps the asset bundle name to the best fitting asset bundle variant. |
|
static protected string RemapVariantName(string assetBundleName) |
|
{ |
|
string[] bundlesWithVariant = m_AssetBundleManifest.GetAllAssetBundlesWithVariant(); |
|
|
|
// If the asset bundle doesn't have variant, simply return. |
|
if (System.Array.IndexOf(bundlesWithVariant, assetBundleName) < 0) |
|
return assetBundleName; |
|
|
|
string[] split = assetBundleName.Split('.'); |
|
|
|
int bestFit = int.MaxValue; |
|
int bestFitIndex = -1; |
|
// Loop all the assetBundles with variant to find the best fit variant assetBundle. |
|
for (int i = 0; i < bundlesWithVariant.Length; i++) |
|
{ |
|
string[] curSplit = bundlesWithVariant[i].Split('.'); |
|
if (curSplit[0] != split[0]) |
|
continue; |
|
|
|
int found = System.Array.IndexOf(m_Variants, curSplit[1]); |
|
if (found != -1 && found < bestFit) |
|
{ |
|
bestFit = found; |
|
bestFitIndex = i; |
|
} |
|
} |
|
|
|
if (bestFitIndex != -1) |
|
return bundlesWithVariant[bestFitIndex]; |
|
else |
|
return assetBundleName; |
|
} |
|
|
|
// Where we actuall call WWW to download the assetBundle. |
|
static protected bool LoadAssetBundleInternal(string assetBundleName, bool isLoadingAssetBundleManifest) |
|
{ |
|
// Already loaded. |
|
LoadedAssetBundle bundle = null; |
|
m_LoadedAssetBundles.TryGetValue(assetBundleName, out bundle); |
|
if (bundle != null) |
|
{ |
|
bundle.m_ReferencedCount++; |
|
return true; |
|
} |
|
|
|
// @TODO: Do we need to consider the referenced count of WWWs? |
|
// In the demo, we never have duplicate WWWs as we wait LoadAssetAsync()/LoadLevelAsync() to be finished before calling another LoadAssetAsync()/LoadLevelAsync(). |
|
// But in the real case, users can call LoadAssetAsync()/LoadLevelAsync() several times then wait them to be finished which might have duplicate WWWs. |
|
if (m_DownloadingWWWs.ContainsKey(assetBundleName)) |
|
return true; |
|
|
|
WWW download = null; |
|
string url = m_BaseDownloadingURL + assetBundleName; |
|
|
|
// For manifest assetbundle, always download it as we don't have hash for it. |
|
if (isLoadingAssetBundleManifest) |
|
download = new WWW(url); |
|
else |
|
download = WWW.LoadFromCacheOrDownload(url, m_AssetBundleManifest.GetAssetBundleHash(assetBundleName), 0); |
|
|
|
m_DownloadingWWWs.Add(assetBundleName, download); |
|
|
|
return false; |
|
} |
|
|
|
// Where we get all the dependencies and load them all. |
|
static protected void LoadDependencies(string assetBundleName) |
|
{ |
|
if (m_AssetBundleManifest == null) |
|
{ |
|
Debug.LogError("Please initialize AssetBundleManifest by calling AssetBundleManager.Initialize()"); |
|
return; |
|
} |
|
|
|
// Get dependecies from the AssetBundleManifest object.. |
|
string[] dependencies = m_AssetBundleManifest.GetAllDependencies(assetBundleName); |
|
if (dependencies.Length == 0) |
|
return; |
|
|
|
for (int i = 0; i < dependencies.Length; i++) |
|
dependencies[i] = RemapVariantName(dependencies[i]); |
|
|
|
// Record and load all dependencies. |
|
m_Dependencies.Add(assetBundleName, dependencies); |
|
for (int i = 0; i < dependencies.Length; i++) |
|
LoadAssetBundleInternal(dependencies[i], false); |
|
} |
|
|
|
// Unload assetbundle and its dependencies. |
|
static public void UnloadAssetBundle(string assetBundleName) |
|
{ |
|
#if UNITY_EDITOR |
|
// If we're in Editor simulation mode, we don't have to load the manifest assetBundle. |
|
if (SimulateAssetBundleInEditor) |
|
return; |
|
#endif |
|
|
|
//Debug.Log(m_LoadedAssetBundles.Count + " assetbundle(s) in memory before unloading " + assetBundleName); |
|
|
|
UnloadAssetBundleInternal(assetBundleName); |
|
UnloadDependencies(assetBundleName); |
|
|
|
//Debug.Log(m_LoadedAssetBundles.Count + " assetbundle(s) in memory after unloading " + assetBundleName); |
|
} |
|
|
|
static protected void UnloadDependencies(string assetBundleName) |
|
{ |
|
string[] dependencies = null; |
|
if (!m_Dependencies.TryGetValue(assetBundleName, out dependencies)) |
|
return; |
|
|
|
// Loop dependencies. |
|
foreach (var dependency in dependencies) |
|
{ |
|
UnloadAssetBundleInternal(dependency); |
|
} |
|
|
|
m_Dependencies.Remove(assetBundleName); |
|
} |
|
|
|
static protected void UnloadAssetBundleInternal(string assetBundleName) |
|
{ |
|
string error; |
|
LoadedAssetBundle bundle = GetLoadedAssetBundle(assetBundleName, out error); |
|
if (bundle == null) |
|
return; |
|
|
|
if (--bundle.m_ReferencedCount == 0) |
|
{ |
|
bundle.m_AssetBundle.Unload(false); |
|
m_LoadedAssetBundles.Remove(assetBundleName); |
|
//Debug.Log("AssetBundle " + assetBundleName + " has been unloaded successfully"); |
|
} |
|
} |
|
|
|
void Update() |
|
{ |
|
// Collect all the finished WWWs. |
|
var keysToRemove = new List<string>(); |
|
foreach (var keyValue in m_DownloadingWWWs) |
|
{ |
|
WWW download = keyValue.Value; |
|
|
|
// If downloading fails. |
|
if (download.error != null) |
|
{ |
|
m_DownloadingErrors.Add(keyValue.Key, download.error); |
|
keysToRemove.Add(keyValue.Key); |
|
continue; |
|
} |
|
|
|
// If downloading succeeds. |
|
if (download.isDone) |
|
{ |
|
//Debug.Log("Downloading " + keyValue.Key + " is done at frame " + Time.frameCount); |
|
m_LoadedAssetBundles.Add(keyValue.Key, new LoadedAssetBundle(download.assetBundle)); |
|
keysToRemove.Add(keyValue.Key); |
|
} |
|
} |
|
|
|
// Remove the finished WWWs. |
|
foreach (var key in keysToRemove) |
|
{ |
|
WWW download = m_DownloadingWWWs[key]; |
|
m_DownloadingWWWs.Remove(key); |
|
download.Dispose(); |
|
} |
|
|
|
// Update all in progress operations |
|
for (int i = 0; i < m_InProgressOperations.Count;) |
|
{ |
|
if (!m_InProgressOperations[i].Update()) |
|
{ |
|
m_InProgressOperations.RemoveAt(i); |
|
} |
|
else |
|
i++; |
|
} |
|
} |
|
|
|
// Load asset from the given assetBundle. |
|
static public AssetBundleLoadAssetOperation LoadAssetAsync(string assetBundleName, string assetName, System.Type type) |
|
{ |
|
AssetBundleLoadAssetOperation operation = null; |
|
#if UNITY_EDITOR |
|
if (SimulateAssetBundleInEditor) |
|
{ |
|
string[] assetPaths = AssetDatabase.GetAssetPathsFromAssetBundleAndAssetName(assetBundleName, assetName); |
|
if (assetPaths.Length == 0) |
|
{ |
|
Debug.LogError("There is no asset with name \"" + assetName + "\" in " + assetBundleName); |
|
return null; |
|
} |
|
|
|
// 根据类型从AssetDatabase中读取资源 |
|
UnityEngine.Object target = AssetDatabase.LoadAssetAtPath(assetPaths[0], type); |
|
// 加载 |
|
operation = new AssetBundleLoadAssetOperationSimulation(target); |
|
} |
|
else |
|
#endif |
|
{ |
|
LoadAssetBundle(assetBundleName); |
|
operation = new AssetBundleLoadAssetOperationFull(assetBundleName, assetName, type); |
|
|
|
m_InProgressOperations.Add(operation); |
|
} |
|
|
|
return operation; |
|
} |
|
|
|
// // Load level from the given assetBundle. |
|
// static public AssetBundleLoadOperation LoadLevelAsync(string assetBundleName, string levelName, bool isAdditive) |
|
// { |
|
// AssetBundleLoadOperation operation = null; |
|
//#if UNITY_EDITOR |
|
// if (SimulateAssetBundleInEditor) |
|
// { |
|
// string[] levelPaths = AssetDatabase.GetAssetPathsFromAssetBundleAndAssetName(assetBundleName, levelName); |
|
// if (levelPaths.Length == 0) |
|
// { |
|
// ///@TODO: The error needs to differentiate that an asset bundle name doesn't exist |
|
// // from that there right scene does not exist in the asset bundle... |
|
|
|
// Debug.LogError("There is no scene with name \"" + levelName + "\" in " + assetBundleName); |
|
// return null; |
|
// } |
|
|
|
// if (isAdditive) |
|
// EditorApplication.LoadLevelAdditiveInPlayMode(levelPaths[0]); |
|
// else |
|
// EditorApplication.LoadLevelInPlayMode(levelPaths[0]); |
|
|
|
// operation = new AssetBundleLoadLevelSimulationOperation(); |
|
// } |
|
// else |
|
//#endif |
|
// { |
|
// LoadAssetBundle(assetBundleName); |
|
// operation = new AssetBundleLoadLevelOperation(assetBundleName, levelName, isAdditive); |
|
|
|
// m_InProgressOperations.Add(operation); |
|
// } |
|
|
|
// return operation; |
|
// } |
|
} |