#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 { 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 m_LoadedAssetBundles = new Dictionary(); static Dictionary m_DownloadingWWWs = new Dictionary(); static Dictionary m_DownloadingErrors = new Dictionary(); static List m_InProgressOperations = new List(); static Dictionary m_Dependencies = new Dictionary(); // 下载路径 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(string assetBundleName, string assetName, Action 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(); //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(); 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; // } }