使用 AssetBundle 加载和释放资源的完整指南

AssetBundle 是 Unity 提供的一种资源打包和加载方式,特别适用于需要动态加载资源的场景,如大型游戏、DLC 或内容更新等。本文将详细介绍如何通过 AssetBundle 加载和释放资源,并提供完整的代码示例。

主要是表明思路和测试,如果文本自定义修改。。。。。。。✌️✌️✌️✌️✌️

写完还没测试  😂😂😂😂😂

1. 准备工作

首先,确保你的资源已经放置在项目的某个文件夹中,比如 "Assets/Res"。假设有一个名为 "cube.prefab" 的资源位于 "Assets/Res/cube.prefab"。

2. 打包 AssetBundle

使用 Unity 的打包工具将资源打包成 AssetBundle。

  1. 将下述代码保存为 CreateAssetBundles.cs 并放置在 Editor 文件夹中。
  2. 选择资源 "Assets/Res/cube.prefab" 并在 Inspector 窗口中设置 AssetBundle 名称为 cube_bundle
  3. 在 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);
        }
    }
}

  • 9
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值