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<string, byte[]> bundleCache = new Dictionary<string, byte[]>();// 加密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<DownloadProgress> 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<AssetBundle, Stream> activeStreams = new Dictionary<AssetBundle, Stream>();// 分块加载大文件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时关闭对应的Streampublic 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(){// 清理所有活跃的Streamforeach (var stream in activeStreams.Values){stream?.Close();}activeStreams.Clear();}
}
性能对比和选择建议
性能排序(从快到慢)
- LoadFromFile - 内存映射,性能最佳
- LoadFromStream - 流式访问,适中
- LoadFromMemory - 需要数据拷贝,较慢
- UnityWebRequest - 网络延迟,最慢
选择合适的 AssetBundle 加载方式需根据项目需求和资源特点进行权衡。
加载方式 | 场景 | 优点 | 缺点 |
---|---|---|---|
同步加载 LoadFromFile | 小型资源、预加载 | 简单、快速 | 阻塞主线程 |
异步加载 LoadFromFileAsync | 大型资源、后台加载 | 不阻塞主线程、支持进度 | 实现复杂 |
从内存加载 LoadFromMemory | 网络下载、加密资源 | 灵活、支持加密 | 内存占用高 |
从网络加载 UnityWebRequestAssetBundle | 热更新、在线资源 | 支持异步下载和加载 | 依赖网络 |
流式加载 LoadFromStream | 流式传输、大文件加载 | 支持分块加载 | 实现复杂 |
在实际开发中,开发者应结合内存管理、性能优化和用户体验需求选择加载方式。小型资源可用同步加载快速处理,大型资源或动态加载场景推荐异步加载,而热更新和在线资源则优先考虑网络加载方式。通过合理规划,可确保游戏运行流畅并提升用户体验。