Unity 2017.3中AssetBundle下载路径、加载及Manifest使用问题咨询
嘿,针对你在Unity 2017.3里遇到的AssetBundle问题,我给你逐一梳理清楚:
首先明确:Application.streamingAssetsPath是打包时嵌入到安装包的只读目录,空目录说明你没往里面放任何打包好的资源,而UnityWebRequest.GetAssetBundle下载的AssetBundle默认存在Unity的缓存目录里,不是streamingAssets。
这个缓存目录由Unity的Caching系统管理,不同平台路径不一样:
- Windows:
C:\Users\<你的用户名>\AppData\LocalLow\<公司名>\<项目名>\UnityCache - Mac:
~/Library/Application Support/<公司名>/<项目名>/UnityCache - 移动端:对应平台的应用专属缓存目录(比如Android的
/data/data/<包名>/cache)
你可以通过代码Debug.Log(Caching.defaultCache.path)打印出当前项目的缓存路径,但一般不需要手动操作这个目录里的文件——Unity会自动管理缓存的增删和版本匹配。
分两种场景:
场景1:下载后直接加载
用DownloadHandlerAssetBundle.GetContent(webRequest)直接获取AssetBundle对象,示例代码:
IEnumerator DownloadAndLoadAB(string uri, uint version) { using (UnityWebRequest webRequest = UnityWebRequest.GetAssetBundle(uri, version, 0)) { yield return webRequest.SendWebRequest(); if (webRequest.isNetworkError || webRequest.isHttpError) { Debug.LogError($"下载失败:{webRequest.error}"); } else { // 获取AssetBundle实例 AssetBundle targetAB = DownloadHandlerAssetBundle.GetContent(webRequest); if (targetAB == null) { Debug.LogError("加载AssetBundle失败"); yield break; } // 加载AB内的资源(比如一个Prefab) GameObject prefab = targetAB.LoadAsset<GameObject>("MyTestPrefab"); if (prefab != null) { Instantiate(prefab); } // 用完记得卸载AB(false表示不卸载已加载的资源,true会清空所有关联资源) targetAB.Unload(false); } } }
场景2:从缓存加载已下载的AB
UnityWebRequest.GetAssetBundle会自动检查缓存:如果指定的uri和version在缓存中存在,就不会发起网络请求,直接从缓存读取。你也可以提前用Caching.IsVersionCached(uri, version)判断缓存是否存在:
if (Caching.IsVersionCached(uri, targetVersion)) { Debug.Log("AssetBundle已在缓存中,直接加载"); StartCoroutine(LoadABFromCache(uri, targetVersion)); } else { Debug.Log("AssetBundle不在缓存中,开始下载"); StartCoroutine(DownloadAndLoadAB(uri, targetVersion)); } // 从缓存加载的逻辑和下载逻辑几乎一致,只是不会走网络请求 IEnumerator LoadABFromCache(string uri, uint version) { using (UnityWebRequest webRequest = UnityWebRequest.GetAssetBundle(uri, version, 0)) { yield return webRequest.SendWebRequest(); AssetBundle targetAB = DownloadHandlerAssetBundle.GetContent(webRequest); // 后续加载资源逻辑同上... } }
AssetBundleManifest是你打包AssetBundle时生成的主Bundle里的核心资源,这个主Bundle是打包过程的产物:
当你用BuildPipeline.BuildAssetBundles(outputPath, ...)打包时,Unity会在outputPath目录下生成一个和目录同名的Bundle文件(比如你输出到./AssetBundles目录,主Bundle就是AssetBundles文件,没有后缀),这个文件就是manifestBundlePath指向的目标。
访问主Bundle的两种情况:
情况1:主Bundle放在StreamingAssets里(打包时嵌入)
如果你把主Bundle复制到Assets/StreamingAssets目录下,路径需要结合Application.streamingAssetsPath拼接,但注意平台差异:
- Windows/Mac:可以直接用
LoadFromFile加载
string manifestBundlePath = Path.Combine(Application.streamingAssetsPath, "AssetBundles"); AssetBundle manifestBundle = AssetBundle.LoadFromFile(manifestBundlePath);
- Android:
Application.streamingAssetsPath是jar:file://开头的URL,不能直接用LoadFromFile,需要用UnityWebRequest加载
IEnumerator LoadManifestFromStreamingAssets() { string uri = Path.Combine(Application.streamingAssetsPath, "AssetBundles"); using (UnityWebRequest webRequest = UnityWebRequest.GetAssetBundle(uri)) { yield return webRequest.SendWebRequest(); if (webRequest.isNetworkError || webRequest.isHttpError) { Debug.LogError(webRequest.error); } else { AssetBundle manifestBundle = DownloadHandlerAssetBundle.GetContent(webRequest); AssetBundleManifest manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); // 用manifest获取依赖、版本等信息,比如获取某个AB的所有依赖 string[] dependencies = manifest.GetAllDependencies("PlayerCharacter"); foreach (string depName in dependencies) { // 加载依赖的AB AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, depName)); } manifestBundle.Unload(false); } } }
情况2:主Bundle从服务器下载
和下载普通AB一样,先通过UnityWebRequest.GetAssetBundle下载主Bundle,再从中加载AssetBundleManifest,逻辑和上面的下载示例一致。
因为Unity的缓存目录是内部管理的,你不能直接读取缓存里的文件(文件名和路径都是Unity加密/自定义的),所以正确的方式还是依赖UnityWebRequest.GetAssetBundle:
- 只要你传入的uri和version和之前下载的一致,Unity会自动从缓存读取,不会重复下载
- 如果你想提前确认缓存是否存在,用
Caching.IsVersionCached(uri, version)判断即可
如果不想用Unity的缓存系统,你可以换一种方式:用UnityWebRequest.Get把AB文件下载到你指定的路径(比如Application.persistentDataPath,这个目录是可读写的),然后用AssetBundle.LoadFromFile加载这个文件,示例:
IEnumerator DownloadABToPersistent(string uri, string saveFileName) { string savePath = Path.Combine(Application.persistentDataPath, saveFileName); // 先检查本地是否已存在该文件 if (File.Exists(savePath)) { Debug.Log("本地已存在AB,直接加载"); AssetBundle ab = AssetBundle.LoadFromFile(savePath); // 加载资源逻辑... yield break; } // 不存在则下载 using (UnityWebRequest webRequest = UnityWebRequest.Get(uri)) { yield return webRequest.SendWebRequest(); if (webRequest.isNetworkError || webRequest.isHttpError) { Debug.LogError(webRequest.error); } else { // 保存到PersistentDataPath File.WriteAllBytes(savePath, webRequest.downloadHandler.data); // 加载AB AssetBundle ab = AssetBundle.LoadFromFile(savePath); // 加载资源逻辑... } } }
这种方式你可以完全控制AB的存储位置,随时通过savePath加载已下载的AB。
内容的提问来源于stack exchange,提问作者BlueStar

