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
4 years ago
|
#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;
|
||
|
// }
|
||
|
}
|