AssetBundle 是 Unity 提供的一种资源打包和加载方式,特别适用于需要动态加载资源的场景,如大型游戏、DLC 或内容更新等。本文将详细介绍如何通过 AssetBundle 加载和释放资源,并提供完整的代码示例。
主要是表明思路和测试,如果文本自定义修改。。。。。。。✌️✌️✌️✌️✌️
写完还没测试 😂😂😂😂😂
1. 准备工作
首先,确保你的资源已经放置在项目的某个文件夹中,比如 "Assets/Res"。假设有一个名为 "cube.prefab" 的资源位于 "Assets/Res/cube.prefab"。
2. 打包 AssetBundle
使用 Unity 的打包工具将资源打包成 AssetBundle。
- 将下述代码保存为
CreateAssetBundles.cs
并放置在Editor
文件夹中。 - 选择资源 "Assets/Res/cube.prefab" 并在 Inspector 窗口中设置 AssetBundle 名称为
cube_bundle
。 - 在 Unity 菜单中选择
Assets -> Build AssetBundles
来生成 AssetBundle。
using UnityEditor;
using UnityEngine;
public class CreateAssetBundles
{
[MenuItem("Assets/Build AssetBundles")]
static void BuildAllAssetBundles()
{
string assetBundleDirectory = "Assets/AssetBundles";
if (!System.IO.Directory.Exists(assetBundleDirectory))
{
System.IO.Directory.CreateDirectory(assetBundleDirectory);
}
BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
}
}
3. AssetBundle 管理器
AssetBundle 管理器,用于加载和释放 AssetBundle 资源。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEditor;
public class AssetBundleManager : MonoBehaviour
{
private Dictionary<string, AssetBundle> loadedAssetBundles = new Dictionary<string, AssetBundle>();
private Dictionary<string, int> bundleReferenceCount = new Dictionary<string, int>();
private Dictionary<string, string> bundleNameDic = new Dictionary<string, string>();
private AssetBundleManifest manifest;
public void LoadManifest(string manifestBundlePath)
{
var manifestBundle = AssetBundle.LoadFromFile(manifestBundlePath);
if (manifestBundle != null)
{
manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
foreach (string bunldename in manifest.GetAllAssetBundles())
{
string[] assetpaths = AssetDatabase.GetAssetPathsFromAssetBundle(bunldename);
foreach (var path in assetpaths)
{
if (!bundleNameDic.ContainsKey(path))
bundleNameDic.Add(path, bunldename);
}
}
manifestBundle.Unload(false);
}
else
{
Debug.LogError("Failed to load AssetBundleManifest from path: " + manifestBundlePath);
}
}
public AssetBundle GetAssetBundle(string bundleName)
{
if (loadedAssetBundles.ContainsKey(bundleName))
{
return loadedAssetBundles[bundleName];
}
return null;
}
public void AddAssetBundle(string bundleName, AssetBundle bundle)
{
if (!loadedAssetBundles.ContainsKey(bundleName))
{
loadedAssetBundles.Add(bundleName, bundle);
}
}
public void RemoveAssetBundle(string bundleName)
{
if (loadedAssetBundles.ContainsKey(bundleName))
{
loadedAssetBundles[bundleName].Unload(false);
loadedAssetBundles.Remove(bundleName);
}
}
public void IncrementReferenceCount(string bundleName)
{
if (bundleReferenceCount.ContainsKey(bundleName))
{
bundleReferenceCount[bundleName]++;
}
else
{
bundleReferenceCount[bundleName] = 1;
}
}
public void DecrementReferenceCount(string bundleName)
{
if (bundleReferenceCount.ContainsKey(bundleName))
{
bundleReferenceCount[bundleName]--;
if (bundleReferenceCount[bundleName] <= 0)
{
RemoveAssetBundle(bundleName);
bundleReferenceCount.Remove(bundleName);
}
}
}
public IEnumerator LoadAssetBundleWithDependencies(string assetpath)
{
if (manifest == null)
{
Debug.LogError("Manifest is not loaded. Please load the manifest first.");
yield break;
}
string basePath = GetBasePath();
string bundleName = string.Empty;
if (!bundleNameDic.TryGetValue(assetpath, out bundleName))
yield break;
string[] dependencies = manifest.GetAllDependencies(bundleName);
foreach (var dependency in dependencies)
{
if (GetAssetBundle(dependency) == null)
{
yield return LoadAssetBundleFromURL(dependency, basePath + dependency);
}
IncrementReferenceCount(dependency);
}
if (GetAssetBundle(bundleName) == null)
{
yield return LoadAssetBundleFromURL(bundleName, basePath + bundleName);
IncrementReferenceCount(bundleName);
}
}
public IEnumerator LoadAssetBundleFromURL(string bundleName, string url)
{
if (GetAssetBundle(bundleName) == null)
{
using (UnityWebRequest uwr = UnityWebRequestAssetBundle.GetAssetBundle(url))
{
yield return uwr.SendWebRequest();
if (uwr.isNetworkError || uwr.isHttpError)
{
Debug.LogError("Failed to load AssetBundle from URL: " + url + " Error: " + uwr.error);
yield break;
}
else
{
AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(uwr);
if (bundle != null)
{
AddAssetBundle(bundleName, bundle);
IncrementReferenceCount(bundleName);
}
else
{
Debug.LogError("Failed to get AssetBundle content from URL: " + url);
yield break;
}
}
}
}
}
public void UnloadAssetBundle(string bundleName)
{
DecrementReferenceCount(bundleName);
}
public void DisposeAllAsset()
{
try
{
// 逐一释放所有加载过的AssetBundle包中的资源
foreach (var bundle in loadedAssetBundles.Values)
{
bundle.Unload(true);
}
loadedAssetBundles.Clear();
Resources.UnloadUnusedAssets();
System.GC.Collect();
}
finally
{
loadedAssetBundles.Clear();
Resources.UnloadUnusedAssets();
System.GC.Collect();
}
}
public string GetBasePath()
{
// 根据平台设置 AssetBundle 基础路径
string basePath = "";
#if UNITY_ANDROID
basePath = "jar:file://" + Application.dataPath + "!/assets/";
#elif UNITY_IOS
basePath = Application.dataPath + "/Raw/";
#else
basePath = "file://" + Application.streamingAssetsPath + "/";
#endif
return basePath;
}
}
public class LoadRequest
{
public string bundleName;
public string AssetPath;
public System.Action<LoadRequest> callback;
public bool IsCompleted;
public LoadRequest(string AssetPath, System.Action<LoadRequest> callback)
{
this.AssetPath = AssetPath;
this.callback = callback;
this.IsCompleted = false;
}
/// <summary>
/// 卸载内存镜像资源
/// </summary>
public void Dispose(AssetBundle bundle)
{
bundle.Unload(false);
}
/// <summary>
/// 释放当前 AssetBundle 内存镜像资源,且释放内存资源
/// </summary>
public void DisposeAll(AssetBundle bundle)
{
bundle.Unload(true);
}
}
4. 队列实现加在逻辑
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LoadQueueManager : MonoBehaviour
{
private Queue<LoadRequest> loadQueue = new Queue<LoadRequest>();
private bool isLoading = false;
public int maxConcurrentLoads = 3;
public void EnqueueLoad(string assetpath, System.Action<LoadRequest> callback = null)
{
loadQueue.Enqueue(new LoadRequest(assetpath, callback));
if (!isLoading)
{
StartCoroutine(ProcessQueue());
}
}
private IEnumerator ProcessQueue()
{
isLoading = true;
while (loadQueue.Count > 0)
{
int currentLoads = 0;
List<LoadRequest> currentRequests = new List<LoadRequest>();
while (currentLoads < maxConcurrentLoads && loadQueue.Count > 0)
{
var request = loadQueue.Dequeue();
currentRequests.Add(request);
StartCoroutine(LoadAssetBundleWithCallback(request));
currentLoads++;
}
while (currentRequests.Count > 0)
{
yield return null;
currentRequests.RemoveAll(r => r.IsCompleted);
}
}
isLoading = false;
}
private IEnumerator LoadAssetBundleWithCallback(LoadRequest request)
{
yield return StartCoroutine(FindObjectOfType<AssetBundleManager>().LoadAssetBundleWithDependencies(request.AssetPath));
request.IsCompleted = true;
request.callback?.Invoke(request);
request = null;
}
}
5. 加载和实例化资源
测试加载 AssetBundle 并实例化资源。
using System.Collections.Generic;
using UnityEngine;
public class ResourceManager : MonoBehaviour
{
private AssetBundleManager assetBundleManager;
private LoadQueueManager loadQueueManager;
private Dictionary<GameObject, string> instantiatedObjects = new Dictionary<GameObject, string>();
void Awake()
{
assetBundleManager = FindObjectOfType<AssetBundleManager>();
loadQueueManager = FindObjectOfType<LoadQueueManager>();
string assetpath = "res/cube.prefab";
loadQueueManager.EnqueueLoad(assetpath, (a) => InstantiateObject(a));
}
public void InstantiateObject(LoadRequest request)
{
AssetBundle bundle = assetBundleManager.GetAssetBundle(request.bundleName);
if (bundle == null)
{
Debug.LogError("AssetBundle not loaded: " + request.bundleName);
return;
}
GameObject obj = bundle.LoadAsset<GameObject>(request.AssetPath);
if (obj == null)
{
Debug.LogError("Asset not found: " + request.AssetPath);
return;
}
GameObject instance = Instantiate(obj);
instantiatedObjects.Add(instance, request.bundleName);
assetBundleManager.IncrementReferenceCount(request.bundleName);
}
public void DestroyObject(GameObject obj)
{
if (instantiatedObjects.ContainsKey(obj))
{
string bundleName = instantiatedObjects[obj];
Destroy(obj);
instantiatedObjects.Remove(obj);
assetBundleManager.DecrementReferenceCount(bundleName);
}
else
{
Debug.LogWarning("Object not managed by ResourceManager: " + obj.name);
}
}
public void DestroyAllObjects()
{
List<GameObject> keys = new List<GameObject>(instantiatedObjects.Keys);
foreach (GameObject obj in keys)
{
DestroyObject(obj);
}
}
}