> 技术文档 > 了解一下Unity AssetBundle 的几种加载方式_unity assetbundle.loadfromfile

了解一下Unity AssetBundle 的几种加载方式_unity assetbundle.loadfromfile

Unity 的 AssetBundle 系统提供了多种加载方式,以满足不同场景下的资源管理和性能需求。


同步加载(LoadFromFile)

同步加载使用 AssetBundle.LoadFromFile 方法从文件系统中直接加载 AssetBundle。这种方式会阻塞主线程,直到加载完成。

基本用法

// 同步加载AssetBundle bundle = AssetBundle.LoadFromFile(path);

使用场景

  • 小型 AssetBundle:适用于加载速度快、不会导致明显卡顿的资源。
  • 预加载:在游戏启动或场景切换时加载必要的资源。
  • 测试和调试:开发阶段快速验证资源加载。

优缺点

  • 优点
    • 实现简单,代码量少。
    • 加载速度快,适合小型资源。
  • 缺点
    • 阻塞主线程,可能导致游戏卡顿。
    • 不适合加载大型 AssetBundle。

应用建议

  • 建议在加载小型资源或加载屏幕期间使用,避免在游戏运行时加载大型 AssetBundle,以免影响用户体验。

异步加载(LoadFromFileAsync)

异步加载使用 AssetBundle.LoadFromFileAsync 方法,加载过程在后台进行,不会阻塞主线程。返回一个 AssetBundleCreateRequest 对象,可通过协程或回调监控加载进度。

基本用法

// 异步加载AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(path);yield return request;AssetBundle bundle = request.assetBundle;

使用场景

  • 大型 AssetBundle:加载大文件时避免游戏卡顿。
  • 后台加载:在游戏运行时动态加载资源。
  • 加载进度显示:需要展示加载进度条的场景。

优缺点

  • 优点
    • 不阻塞主线程,保持游戏流畅。
    • 支持加载进度监控。
  • 缺点
    • 实现相对复杂,需要处理异步逻辑。
    • 加载速度可能略慢于同步加载。

应用建议

  • 推荐在加载大型资源时使用,结合协程或回调函数处理加载完成后的逻辑,是游戏中常用的加载方式。

内存加载(LoadFromMemory)

从内存中的字节数组加载 AssetBundle,使用 AssetBundle.LoadFromMemory 方法。通常用于从网络下载的字节数据直接加载。

基本用法

// 同步加载byte[] bundleData = GetBundleBytes(); // 从某处获取字节数据AssetBundle bundle = AssetBundle.LoadFromMemory(bundleData);// 异步加载AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(bundleData);yield return request;AssetBundle bundle = request.assetBundle;

使用场景

  • 网络下载后处理:下载完成后立即加载
  • 加密资源:解密后的数据直接加载
  • 内存缓存:将常用Bundle缓存在内存中
  • 自定义存储:从数据库或特殊格式中读取

优缺点

  • 优点
    • 灵活性高:可以从任何数据源加载
    • 支持加密:可以对Bundle数据进行加密处理
    • 无文件依赖:不需要实际的文件系统
    • 即时加载:数据已在内存中,加载速度快
  • 缺点
    • 内存占用大:需要同时存储原始数据和Bundle
    • GC压力:大量字节数组可能触发垃圾回收
    • 不支持大文件:受限于可用内存大小
    • 数据拷贝开销:需要完整拷贝Bundle数据

应用建议

  • 适用于需要从网络下载并立即加载的场景,注意内存管理以避免泄漏。

应用示例

public class MemoryBundleLoader : MonoBehaviour{ private Dictionary bundleCache = new Dictionary(); // 加密Bundle加载 public IEnumerator LoadEncryptedBundle(string bundleName, string encryptionKey) { // 从网络或本地获取加密数据 byte[] encryptedData = yield return DownloadEncryptedBundle(bundleName); // 解密数据 byte[] decryptedData = DecryptBundle(encryptedData, encryptionKey); // 从内存加载 AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(decryptedData); yield return request; if (request.assetBundle != null) { Debug.Log($\"Encrypted bundle loaded: {bundleName}\"); } // 清理敏感数据 Array.Clear(encryptedData, 0, encryptedData.Length); Array.Clear(decryptedData, 0, decryptedData.Length); } // 内存缓存系统 public IEnumerator PreloadBundleToMemory(string bundleName) { if (bundleCache.ContainsKey(bundleName)) yield break;  string path = GetBundlePath(bundleName); byte[] bundleData = File.ReadAllBytes(path); bundleCache[bundleName] = bundleData; Debug.Log($\"Bundle cached to memory: {bundleName}\"); } public AssetBundle LoadFromCache(string bundleName) { if (bundleCache.ContainsKey(bundleName)) { return AssetBundle.LoadFromMemory(bundleCache[bundleName]); } return null; }}

从网络加载(UnityWebRequestAssetBundle)

使用 UnityWebRequestAssetBundle 从 URL 下载并加载 AssetBundle,集成了网络请求和资源加载,适合热更新场景。

基本用法

public IEnumerator LoadBundleFromWeb(string url){ UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(url); yield return request.SendWebRequest(); if (request.result == UnityWebRequest.Result.Success) { AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request); // 使用bundle... } else { Debug.LogError($\"Download failed: {request.error}\"); }}

使用场景

  • 热更新:从服务器下载最新资源
  • 按需加载:用户触发时才下载特定内容
  • CDN分发:利用CDN加速资源分发
  • A/B测试:动态加载不同版本的资源

优缺点

  • 优点
    • 支持网络协议:HTTP/HTTPS等标准协议
    • 内置缓存:自动处理HTTP缓存机制
    • 进度监控:可以获取下载进度
    • 错误处理:完善的网络错误处理机制
  • 缺点
    • 网络依赖:需要稳定的网络连接
    • 下载延迟:首次加载需要等待下载完成
    • 带宽消耗:消耗用户流量
    • 缓存管理:需要处理缓存清理和版本控制

应用建议

  • 在热更新系统中使用,建议结合版本管理和哈希校验,确保加载正确的资源,同时保证网络请求的稳定性。

应用示例

public class WebBundleLoader : MonoBehaviour{ [System.Serializable] public class DownloadProgress { public float progress; public long downloadedBytes; public long totalBytes; public string status; } public IEnumerator LoadBundleWithProgress(string url, System.Action onProgress) { DownloadProgress progress = new DownloadProgress(); using (UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(url)) { // 设置超时时间 request.timeout = 30; // 开始下载 request.SendWebRequest(); // 监控下载进度 while (!request.isDone) { progress.progress = request.downloadProgress; progress.downloadedBytes = (long)(request.downloadedBytes); progress.status = $\"Downloading... {progress.progress * 100:F1}%\"; onProgress?.Invoke(progress); yield return null; } // 检查结果 if (request.result == UnityWebRequest.Result.Success) { AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request); progress.status = \"Download completed\"; onProgress?.Invoke(progress); // 使用bundle... } else { progress.status = $\"Download failed: {request.error}\"; onProgress?.Invoke(progress); } } } // 带重试机制的下载 public IEnumerator LoadBundleWithRetry(string url, int maxRetries = 3) { int retryCount = 0; while (retryCount < maxRetries) { using (UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(url)) { yield return request.SendWebRequest(); if (request.result == UnityWebRequest.Result.Success) {  AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);  Debug.Log($\"Bundle downloaded successfully: {url}\");  yield break; } else {  retryCount++;  Debug.LogWarning($\"Download attempt {retryCount} failed: {request.error}\");  if (retryCount < maxRetries)  { yield return new WaitForSeconds(Mathf.Pow(2, retryCount)); // 指数退避  } } } } Debug.LogError($\"Failed to download bundle after {maxRetries} attempts: {url}\"); }}

流式加载(LoadFromStream)

从流中加载 AssetBundle,使用 AssetBundle.LoadFromStream 方法,支持从文件流或网络流加载,适合分块加载或流式传输。

基本用法

public IEnumerator LoadBundleFromStream(){ // 创建文件流 FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read); // 异步加载 AssetBundleCreateRequest request = AssetBundle.LoadFromStreamAsync(stream); yield return request; AssetBundle bundle = request.assetBundle; // 注意:stream需要保持打开状态直到bundle卸载}

使用场景

  • 大文件分块加载:逐步加载大型Bundle
  • 自定义数据源:从数据库或网络流加载
  • 内存控制:精确控制内存使用
  • 特殊格式处理:处理压缩或加密的流数据

优缺点

  • 优点
    • 内存效率:只加载需要的部分到内存
    • 支持大文件:适合处理超大Bundle
    • 灵活的数据源:可以从任何Stream加载
    • 精确控制:可以控制加载的时机和方式
  • 缺点
    • 复杂性高:需要管理Stream的生命周期
    • 平台限制:某些平台可能不支持
    • 调试困难:Stream相关的问题不容易定位
    • 性能开销:可能比直接文件加载慢

应用建议

  • 适用于需要流式加载大型资源的场景,确保流的稳定性和错误处理机制。

应用示例

public class StreamBundleLoader : MonoBehaviour{ private Dictionary activeStreams = new Dictionary(); // 分块加载大文件 public IEnumerator LoadLargeBundleFromStream(string path) { FileStream stream = null; try { stream = new FileStream(path, FileMode.Open, FileAccess.Read); // 检查文件大小 long fileSize = stream.Length; Debug.Log($\"Loading large bundle: {fileSize / (1024 * 1024)}MB\"); // 异步加载 AssetBundleCreateRequest request = AssetBundle.LoadFromStreamAsync(stream); // 显示加载进度(这里是模拟,实际Stream加载没有内置进度) float startTime = Time.time; while (!request.isDone) { float elapsed = Time.time - startTime; Debug.Log($\"Loading... {elapsed:F1}s\"); yield return null; } if (request.assetBundle != null) { // 保存Stream引用,Bundle卸载前不能关闭 activeStreams[request.assetBundle] = stream; Debug.Log(\"Large bundle loaded from stream\"); } else { stream?.Close(); } } catch (System.Exception e) { Debug.LogError($\"Failed to load bundle from stream: {e.Message}\"); stream?.Close(); } } // 自定义数据源加载 public IEnumerator LoadBundleFromCustomSource(string bundleId) { // 假设从某个自定义数据源获取流 Stream customStream = GetCustomDataStream(bundleId); if (customStream != null) { AssetBundleCreateRequest request = AssetBundle.LoadFromStreamAsync(customStream); yield return request; if (request.assetBundle != null) { activeStreams[request.assetBundle] = customStream; Debug.Log($\"Bundle loaded from custom source: {bundleId}\"); } else { customStream.Close(); } } } // 卸载Bundle时关闭对应的Stream public void UnloadBundleWithStream(AssetBundle bundle) { if (activeStreams.ContainsKey(bundle)) { activeStreams[bundle].Close(); activeStreams.Remove(bundle); } bundle.Unload(false); } private Stream GetCustomDataStream(string bundleId) { // 这里实现你的自定义数据源逻辑 // 比如从数据库、网络服务或加密文件中获取数据 return null; } void OnDestroy() { // 清理所有活跃的Stream foreach (var stream in activeStreams.Values) { stream?.Close(); } activeStreams.Clear(); }}

性能对比和选择建议

性能排序(从快到慢)

  1. LoadFromFile - 内存映射,性能最佳
  2. LoadFromStream - 流式访问,适中
  3. LoadFromMemory - 需要数据拷贝,较慢
  4. UnityWebRequest - 网络延迟,最慢

选择合适的 AssetBundle 加载方式需根据项目需求和资源特点进行权衡。

加载方式 场景 优点 缺点 同步加载

LoadFromFile

小型资源、预加载 简单、快速 阻塞主线程 异步加载

LoadFromFileAsync

大型资源、后台加载 不阻塞主线程、支持进度 实现复杂

从内存加载

LoadFromMemory

网络下载、加密资源 灵活、支持加密 内存占用高

从网络加载

UnityWebRequestAssetBundle

热更新、在线资源 支持异步下载和加载 依赖网络

流式加载

LoadFromStream

流式传输、大文件加载 支持分块加载 实现复杂

在实际开发中,开发者应结合内存管理、性能优化和用户体验需求选择加载方式。小型资源可用同步加载快速处理,大型资源或动态加载场景推荐异步加载,而热更新和在线资源则优先考虑网络加载方式。通过合理规划,可确保游戏运行流畅并提升用户体验。