前面自定义了Bundle的打包方式,现在就要自定义加载方式了,不然就会走原来设置的远程加载的原本逻辑,我们多做了一个内置包列表和复制移动Bundle到Library中的准备工作就没有用了。
注意如果是UnityWebRequestAssetBundle,则不会支持访问data去保存,不过自行缓存,此时只需要判断是不是内置Bundle就行,其他走web。需要自己缓存Bundle的,需要修改UnityWebRequestAssetBundle为UnityWebRequest
新建一个cs文件MyAssetBundleProvider,复制原内容的AssetBundlePrivider到新建cs脚本中,修改相关类名为自己自定义类名。主要修改IAssetBundleResource的BeginOperation方法和WebRequestOperationCompleted方法
//MyAssetBundleProvider.cs
private void BeginOperation()
{
string path = m_ProvideHandle.Location.InternalId;
var url = m_ProvideHandle.ResourceManager.TransformInternalId(m_ProvideHandle.Location);
string bundleName = Path.GetFileName(url);
// if a path starts with jar:file, it is an android embeded resource. The resource is a local file but cannot be accessed by
// FileStream(called in LoadWithDataProc) directly
// Need to use webrequest's async call to get the content.
if (AssetBundleManager.GetInstance().buildInData != null && AssetBundleManager.GetInstance().buildInData.BuildInBundleNames.Contains(bundleName))//本地资源,内置包
{
string streamPath = UnityEngine.AddressableAssets.Addressables.RuntimePath + "/" + PlatformMappingService.GetPlatformPathSubFolder() + "/" + bundleName;
Debug.Log("LoadOne:" + streamPath);
var crc = m_Options == null ? 0 : m_Options.Crc;
CompleteBundleLoad(AssetBundle.LoadFromFile(streamPath));
}
else if (AssetBundleManager.GetInstance().IsCache(bundleName)) //已经下载过 缓存到本地的Bundle
{
string cachePath = Path.Combine(AssetBundleManager.GetInstance().GetBundleCachePath(), bundleName);
Debug.Log("LoadTwo:" + cachePath);
var crc = m_Options == null ? 0 : m_Options.Crc;
CompleteBundleLoad(AssetBundle.LoadFromFile(cachePath));
}
else if (ResourceManagerConfig.ShouldPathUseWebRequest(path)) //真正需要下载的Bundle
{
Debug.Log("DownloadThree:" + url);
var req = CreateWebRequest(m_ProvideHandle.Location);
req.disposeDownloadHandlerOnDispose = false;
m_WebRequestQueueOperation = WebRequestQueue.QueueRequest(req);
if (m_WebRequestQueueOperation.IsDone)
{
m_RequestOperation = m_WebRequestQueueOperation.Result;
m_RequestOperation.completed += WebRequestOperationCompleted;
}
else
{
m_WebRequestQueueOperation.OnComplete += asyncOp =>
{
m_RequestOperation = asyncOp;
m_RequestOperation.completed += WebRequestOperationCompleted;
};
}
}
else
{
m_RequestOperation = null;
m_ProvideHandle.Complete<MyAssetBundleResource>(null, false, new Exception(string.Format("Invalid path in AssetBundleProvider: '{0}'.", path)));
}
}
private void WebRequestOperationCompleted(AsyncOperation op)
{
UnityWebRequestAsyncOperation remoteReq = op as UnityWebRequestAsyncOperation;
var webReq = remoteReq.webRequest;
if (!UnityWebRequestUtilities.RequestHasErrors(webReq, out UnityWebRequestResult uwrResult))
{
m_downloadHandler = webReq.downloadHandler;
string path = m_ProvideHandle.ResourceManager.TransformInternalId(m_ProvideHandle.Location);
string bundleName = Path.GetFileName(path);
AssetBundleManager.GetInstance().CacheBundle(bundleName, m_downloadHandler.data);//主要是在这里加了一个保存到本地的方法
if (!m_Completed)
{
m_ProvideHandle.Complete(this, true, null);
m_Completed = true;
}
}
else
{
m_downloadHandler = webReq.downloadHandler;
m_downloadHandler.Dispose();
m_downloadHandler = null;
string message = string.Format("Web request {0} failed with error '{1}', retrying ({2}/{3})...", webReq.url, webReq.error, m_Retries, m_Options.RetryCount);
if (m_Retries < m_Options.RetryCount)
{
Debug.LogFormat(message);
BeginOperation();
m_Retries++;
}
else
{
var exception = new Exception(string.Format(
"RemoteAssetBundleProvider unable to load from url {0}, result='{1}'.", webReq.url,
webReq.error));
m_ProvideHandle.Complete<MyAssetBundleResource>(null, false, exception);
}
}
webReq.Dispose();
}读取内置包列表,缓存Bundle到本地,检查是否有缓存,都是使用 AssetBundleManager.cs
using System.Collections.Generic;
using UnityEngine;
using System;
using System.IO;
[Serializable]
public class BuildInBundleData
{
public List<string> BuildInBundleNames = new List<string>();
}
public class AssetBundleManager
{
private static AssetBundleManager _instance = null;
private string cachePath = "";
public BuildInBundleData buildInData;
public static AssetBundleManager GetInstance()
{
if (_instance == null)
_instance = new AssetBundleManager();
return _instance;
}
AssetBundleManager()
{
cachePath = Path.Combine(Application.persistentDataPath, "ab");
if (!Directory.Exists(cachePath))
{
Directory.CreateDirectory(cachePath);
}
}
public void Init()
{
#if !UNITY_EDITOR
var json = Resources.Load<TextAsset>("Version/BuildInBundleName");
buildInData = JsonUtility.FromJson<BuildInBundleData>(json.text);
#endif
}
public String GetBundleCachePath()
{
return cachePath;
}
public bool IsCache(string bundleName)
{
string filePath = Path.Combine(AssetBundleManager.GetInstance().GetBundleCachePath(), bundleName);
return File.Exists(filePath);
}
public void CacheBundle(string bundlename, byte[] bytes)
{
string filePath = Path.Combine(GetBundleCachePath(), bundlename);
if (File.Exists(filePath))
File.Delete(filePath);
File.WriteAllBytes(filePath, bytes);
string realName = GetRealBundleName(bundlename);
string oldPath = PlayerPrefs.GetString(realName, "");
if (oldPath != "")
File.Delete(oldPath);
PlayerPrefs.SetString(realName, filePath);
}
public string GetRealBundleName(string bundlename)
{
if (string.IsNullOrEmpty(bundlename))
return "";
int index = bundlename.LastIndexOf("_");
return bundlename.Substring(0, index);
}
}自定义写完后,需要应用使用上这个自定义加载Provider
public static void SetAllGroupToRemote()
{
var s = AASUtility.GetSettings();
var groups = s.groups;
foreach (var group in groups)
{
BundledAssetGroupSchema bundledAssetGroupSchema = group.GetSchema<BundledAssetGroupSchema>();
if (bundledAssetGroupSchema == null)
{
bundledAssetGroupSchema = group.AddSchema<BundledAssetGroupSchema>();
}
bundledAssetGroupSchema.BuildPath.SetVariableByName(group.Settings, AddressableAssetSettings.kRemoteBuildPath);
bundledAssetGroupSchema.LoadPath.SetVariableByName(group.Settings, AddressableAssetSettings.kRemoteLoadPath);
bundledAssetGroupSchema.SetAssetBundleProviderType(typeof(MyAssetBundleProvider)); //主要是这
EditorUtility.SetDirty(bundledAssetGroupSchema);
}
AssetDatabase.Refresh();
}