Unity Webgl 加载AB包

导入Addressables包

创建资源管理配置。这里引入参考链接Unity简单使用 Addressables远端打包流程 - 剑起苍穹 - 博客园

此时会生成如下图文件夹里面很多配置文件 (如下图所示)

配置远程加载资源服务器的路径,这里也可以用Unity自带的快速本地服务测试。

参考 Addressable(8)Addressable Hosting可寻址托管窗口配置_addressables hosting services-优快云博客

这里涉及服务器的地址。我用nodejs 临时搭了一个。

过程也记录一下。引入参考链接 http-server:Node.js下的轻量级静态服务器工具

npm install -g http-server

安装报错,大概是版本问题。

然后我根据提示修复了一下,感觉不用修复也行。

到这个地方http sever 服务就启动起来了。

后面遇到跨域问题。加--cors启动就行。

http-server -p 8000 --cors

这里的服务器路径填进去。

下面就是将预制体打包成ab了

简单做几个预制体。放进去。

我们需要将Addressables配置改成从远程加载

打包一份。

本地选择选择加载方式。

默认打包就行。

在项目Assets同级目录下会生成对应包体。

将里面的文件放到,上面做好的服务器磁盘路径里。

加载ab资源代码示例。

// 定义一个类来存储资源信息和优先级
using System.Collections.Generic;

[System.Serializable]
public class AssetInfo
{
    public string assetBundleName;
    public string assetName;
    public int priority;

    // 重写 Equals 方法用于对比
    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
            return false;

        AssetInfo other = (AssetInfo)obj;
        return assetBundleName == other.assetBundleName &&
               assetName == other.assetName &&
               priority == other.priority;
    }

    // 重写 GetHashCode 方法
    public override int GetHashCode()
    {
        return (assetBundleName != null ? assetBundleName.GetHashCode() : 0) ^
               (assetName != null ? assetName.GetHashCode() : 0) ^
               priority.GetHashCode();
    }
}

// 用于存储 JSON 配置的类
[System.Serializable]
public class AssetConfig
{
    public List<AssetInfo> assets;
}
// 定义一个 ScriptableObject 类来存储配置
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "AssetConfigSO", menuName = "Asset Bundle/Asset Config ScriptableObject")]
public class AssetConfigSO : ScriptableObject
{
    public List<AssetInfo> assets;
}


 

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System;
using System.IO;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
#if UNITY_EDITOR_WIN
using UnityEditor;
#endif

// 异步加载事件委托定义
public delegate void LoadingStartedHandler();
public delegate void LoadingUpdatedHandler(float progress);
public delegate void LoadingCompletedHandler();
// 新增配置加载完成事件委托
public delegate void ConfigLoadedHandler();

// 新增枚举类型表示数据源
public enum DataSource
{
    JSON,
    ScriptableObject
}

public class AssetBundleManager : MonoBehaviour
{
    // 用于存储待加载的资源信息的队列
    private List<AssetInfo> assetQueue = new List<AssetInfo>();

    // 事件声明
    public event LoadingStartedHandler OnLoadingStarted;
    public event LoadingUpdatedHandler OnLoadingUpdated;
    public event LoadingCompletedHandler OnLoadingCompleted;
    // 新增配置加载完成事件
    public event ConfigLoadedHandler OnConfigLoaded;

    // 根据平台和数据源选择加载方式,并对比更新
    public IEnumerator LoadConfig(string urlOrPath, DataSource source)
    {
        AssetConfig jsonConfig = null;
        AssetConfigSO soConfig = null;
        if (source == DataSource.JSON)
        {
            if (Application.platform == RuntimePlatform.WebGLPlayer)
            {
                // WebGL 平台从网页加载
                using (UnityWebRequest uwr = UnityWebRequest.Get(new Uri(urlOrPath)))
                {
                    yield return uwr.SendWebRequest();

                    if (uwr.result == UnityWebRequest.Result.Success)
                    {
                        string json = uwr.downloadHandler.text;
                        jsonConfig = JsonUtility.FromJson<AssetConfig>(json);
                    }
                    else
                    {
                        Debug.LogError($"Error loading config from {urlOrPath}: {uwr.error}");
                    }
                }
            }
            else
            {
                // 其他平台从文件加载
                if (File.Exists(urlOrPath))
                {
                    string json = File.ReadAllText(urlOrPath);
                    jsonConfig = JsonUtility.FromJson<AssetConfig>(json);
                }
                else
                {
                    Debug.LogError($"Config file {urlOrPath} not found.");
                }
            }

            soConfig = Resources.Load<AssetConfigSO>("AssetConfigSO");
            if (soConfig != null)
            {
#if UNITY_EDITOR_WIN
                if (!File.Exists(urlOrPath))
                {
                    // Convert ScriptableObject config to JSON and save it
                    jsonConfig = new AssetConfig { assets = soConfig.assets };
                    string jsonContent = JsonUtility.ToJson(jsonConfig, true);
                    File.WriteAllText(urlOrPath, jsonContent);
                }
#endif
                if (!IsConfigsEqual(jsonConfig.assets, soConfig.assets))
                {
                    UpdateScriptableObjectConfig(soConfig, jsonConfig.assets);
                }
            }
            else
            {
#if UNITY_EDITOR_WIN
                soConfig = ScriptableObject.CreateInstance<AssetConfigSO>();
                soConfig.assets = jsonConfig.assets;
                AssetDatabase.CreateAsset(soConfig, "Assets/Resources/AssetConfigSO.asset");
                AssetDatabase.SaveAssets();
#endif
            }

            foreach (var info in jsonConfig.assets)
            {
                AddToQueue(info);
            }
        }
        else if (source == DataSource.ScriptableObject)
        {
            string soConfigname = Path.GetFileNameWithoutExtension(urlOrPath);
            Debug.Log(soConfigname);
            soConfig = Resources.Load<AssetConfigSO>("AssetConfigSO");
            if (soConfig != null)
            {
                foreach (var info in soConfig.assets)
                {
                    AddToQueue(info);
                }
            }
            else
            {
                Debug.LogError($"ScriptableObject {urlOrPath} not found.");
            }
        }
        yield return null;
       // 触发配置加载完成事件
       OnConfigLoaded?.Invoke();
    }

    // 对比两个配置列表是否一致
    private bool IsConfigsEqual(List<AssetInfo> list1, List<AssetInfo> list2)
    {
        if (list1.Count != list2.Count)
            return false;

        for (int i = 0; i < list1.Count; i++)
        {
            if (!list1[i].Equals(list2[i]))
                return false;
        }

        return true;
    }

    // 更新 ScriptableObject 配置
    private void UpdateScriptableObjectConfig(AssetConfigSO soConfig, List<AssetInfo> newAssets)
    {
#if UNITY_EDITOR_WIN
        soConfig.assets = newAssets;
        EditorUtility.SetDirty(soConfig);
        AssetDatabase.SaveAssets();
#endif
    }

    // 向队列中添加资源信息
    public void AddToQueue(AssetInfo info)
    {
        assetQueue.Add(info);
        // 每次添加后按优先级排序
        assetQueue = assetQueue.OrderBy(item => item.priority).ToList();
    }

    // 开始加载队列中的资源
    public IEnumerator StartLoading()
    {
        if (assetQueue.Count == 0)
        {
            Debug.LogError("No configuration data loaded. Please load the configuration file first.");
            yield break;
        }

        // 触发开始事件
        OnLoadingStarted?.Invoke();

        int totalAssets = assetQueue.Count;
        int loadedAssets = 0;
        
        foreach (var info in assetQueue)
        {
            AsyncOperationHandle<GameObject> assetRequest = Addressables.LoadAssetAsync<GameObject>(info.assetBundleName);
            while (!assetRequest.IsDone)
            {
                // 触发更新事件
                float progress = (loadedAssets + assetRequest.PercentComplete) / totalAssets;
                OnLoadingUpdated?.Invoke(progress);
                yield return null;
            }
            if (assetRequest.Status == AsyncOperationStatus.Succeeded)
            {
                GameObject asset = assetRequest.Result as GameObject;
                if (asset != null)
                {
                    Instantiate(asset);
                }

            }
            else
                Addressables.Release(assetRequest);

            loadedAssets++;
        }

        // 触发完成事件
        OnLoadingCompleted?.Invoke();

        // 加载完成后清空队列
        assetQueue.Clear();
    }

    string GetAssetBundleURL(string bundleName)
    {
        switch (Application.platform)
        {
            case RuntimePlatform.WebGLPlayer:
                return Application.streamingAssetsPath + "/AssetBundles/" + bundleName;
            case RuntimePlatform.WindowsPlayer:
            case RuntimePlatform.OSXPlayer:
            case RuntimePlatform.LinuxPlayer:
                return Application.dataPath + "/StreamingAssets/AssetBundles/" + bundleName;
            default:
                Debug.LogError("Unsupported platform for AssetBundle loading.");
                return "";
        }
    }
}

在Resources里右键建一个加载顺序和优先级的表。

这里我用一个测试脚本,尝试加载配置表。

using System;
using System.IO;
using UnityEngine;

public class ExampleUsage : MonoBehaviour
{
    public AssetBundleManager manager;

    void Start()
    {
        manager.OnConfigLoaded += OnConfigLoaded;
        string configPath = Application.streamingAssetsPath + "/asset_config.json";        
        StartCoroutine(manager.LoadConfig(configPath, DataSource.ScriptableObject));
      
    }

    void OnConfigLoaded()
    {
        StartCoroutine(manager.StartLoading());
    }
}

在编辑器里播放是红的,不影响,打包出来的效果。

为了后面方便对接后端数据,我们可以把配置表导出json。改写加载方式。

打包编译出来。

这里为了方便暂时不压缩。

这里我直接用rider 测试一下。

如果不用rider,就按照刚刚搭建http server的方式,重新给Output搭一个服务,是一样的。

复制链接在浏览器里面访问

这里再引入跨项目资源更新使用方式。

Unity Addressables跨工程加载资源_remoteproviderexception : invalid path in assetbun-优快云博客

 在新项目中同样引入addressable包

这里addressable报错。引入Addressable报错缺少内置的GUI_missing built-in guistyle toolbarseachtextfieldpop-优快云博客

可以升级版本,报错只是编辑器内的ui错误。

com.unity.addressables

1.21.17

新建的项目想要使用A项目的包。

同样的代码,改写ExampleUsage 使用类。就是需要先加载A项目的资源目录,然后才能使用目录里的ab包。

using System;
using System.IO;
using UnityEngine;
using UnityEngine.AddressableAssets;

public class ExampleUsage : MonoBehaviour
{
    public AssetBundleManager manager;
    
    void Start()
    {
        manager.OnConfigLoaded += OnConfigLoaded;
        var catalogPath = "http://192.168.1.2:8000/catalog_2025.02.08.03.44.08.json";
        // 加载catalog,并在加载完成事件回调中进行资源加载
        Addressables.LoadContentCatalogAsync(catalogPath).Completed += (resLocatorAopHandler) =>
        {
            string configPath = Application.streamingAssetsPath + "/asset_config.json";
            StartCoroutine(manager.LoadConfig(configPath, DataSource.JSON));
        };
    }

    void OnConfigLoaded()
    {
        StartCoroutine(manager.StartLoading());
    }
}

这里测试加载进B项目。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值