阅前提示
该系列为Unity AddressableAssetSystem 学习笔记,记录源码阅读的理解。
适合人群:All
阅读方式:浏览
如描述有误请指教,如果对你有所帮助,点赞收藏吧:)
简介
Addressable Asset system 是Unity推出的新的资产管理加载打包的插件。它主打的特点在于:便捷
相较于传统的资产加载方式(Resources/AssetBundle),AddressableAssest拥有了完备的可视化编辑窗口以及内存管理。
- VS Resources:不需要对资产路径要求的那么严格,做到了任何地方都可能加载。
- VS AssetBundle:不需要复杂的准备过程,开发即用,并且不需要单独的进行内存管理,总结下来就是比较无脑。
其他关于Addressable的基础介绍这里就不再赘述了,作者在写这篇文章之前浏览了网上其他有关AddressableAsset的文章,发现存在很多关于此的基础介绍和简单使用,所以这里就不提了。接下来我们就从源码出发,好好看看AddressableAsset。
组成
Addressable Asset System,我把它的组成分为以下几个部分:
- Manager
- AddressablesImpl :AddressableAsset使用的接口本口
- ResourceManager:管理资源
- Operation
- AsyncOperation :Addressable 核心异步操作流,一切的加载都源于其异步链式操作(AsyncOperation + ChainOperation)
- ProviderOperation:资源加载操作,异步操作真正执行者,实现资源的异步加载。
- Locator
- ResourceLocationBase:资产定位的组成部分
这三个部分就包含了整个系统的所有脚本了,这里只列举了具有代表性的部分脚本,以便大家找到阅读源码的位置。
简单的来说,Addressable Asset System 的优势在于它统一了资源加载的入口与资源加载的方式。
可想而知,在使用Resources或者AssetBundle时我们关心的地方在哪?我们希望加载资源的Key是一致的,不希望它在情况A的时候要使用KeyA来加载,情况B的时候使用KeyB来加载,这很烦不利于资源的管理。
我们还要考虑加载方式,是同步加载还是异步加载。Addressable 全都给你省了~
生命周期
初始化:
public AsyncOperationHandle<IResourceLocator> InitializeAsync(string runtimeDataPath, string providerSuffix = null, bool autoReleaseHandle = true)
m_InitializationOperation:初始化的异步操作处理
添加各类Provider:ResourceManager.ResourceProviders.Add(...)
Update:
internal void Update(float unscaledDeltaTime)
资源加载的本质
private AsyncOperationHandle ProvideResource(IResourceLocation location, Type desiredType = null)
{
if (location == null)
throw new ArgumentNullException("location");
IResourceProvider provider = null;
if (desiredType == null)
{
provider = GetResourceProvider(desiredType, location);
if (provider == null)
return CreateCompletedOperation<object>(null, new UnknownResourceProviderException(location).Message);
desiredType = provider.GetDefaultType(location);
}
IAsyncOperation op;
int hash = location.Hash(desiredType);
if (m_AssetOperationCache.TryGetValue(hash, out op))
{
op.IncrementReferenceCount();
return new AsyncOperationHandle(op);
}
Type provType;
if (!m_ProviderOperationTypeCache.TryGetValue(desiredType, out provType))
m_ProviderOperationTypeCache.Add(desiredType, provType = typeof(ProviderOperation<>).MakeGenericType(new Type[] { desiredType }));
op = CreateOperation<IAsyncOperation>(provType, provType.GetHashCode(), hash, m_ReleaseOpCached);
// Calculate the hash of the dependencies
int depHash = location.DependencyHashCode;
var depOp = location.HasDependencies ? ProvideResourceGroupCached(location.Dependencies, depHash, null, null) : default(AsyncOperationHandle<IList<AsyncOperationHandle>>);
if (provider == null)
provider = GetResourceProvider(desiredType, location);
((IGenericProviderOperation)op).Init(this, provider, location, depOp);
var handle = StartOperation(op, depOp);
if (depOp.IsValid())
depOp.Release();
return handle;
}
核心就是这一部分,返回 AsyncOperationHandle 用于做异步逻辑的节点触发。资源由ResourceProviderBase来完成加载,AsyncOperationBase 为 Provider 与 Handle的桥梁
工作流程
1.key -> IResourceProvider : 唯一路径 对应 IResourceProvider
2. ProvideResource :IResourceProvider 生成 AsyncOperationHandle
private AsyncOperationHandle ProvideResource(IResourceLocation location, Type desiredType = null)
细节:
-
provider 是执行资源加载的真正位置(继承ResourceProviderBase):根据location 数据获取对应的provider
public IResourceProvider GetResourceProvider(Type t, IResourceLocation location)
-
op 是存放provider的操作流(ProviderOperation), 以 location.Hash为key -> m_AssetOperationCache
-
depOp 是依赖资源的加载操作
-
根据 location 获取等到 provider :
provider = GetResourceProvider(desiredType, location);
-
op init 时绑定 provider :
((IGenericProviderOperation)op).Init(this, provider, location, depOp);
-
链式操作 执行 op.Execute :
var handle = StartOperation(op, depOp);
-
op.Execute 时进行provider的资源加载,并将自身绑定在ProvideHandle中 :
m_Provider.Provide(new ProvideHandle(m_ResourceManager, this));
-
provider 资源加载完毕之后执行 ProvideHandle.Complete -> 即 op.ProviderCompleted :
InternalOp.ProviderCompleted<T>(result, status, exception);
result 就是加载的资源 -
最终执行 AsyncOperationBase.Complete
3. StartOperation(operation,dependency) : 链式操作dependency若没完成则等待dependency结束
var handle = StartOperation(op, depOp);
internal void Start(ResourceManager rm, AsyncOperationHandle dependency, DelegateList<float> updateCallbacks)
{
...
if (dependency.IsValid() && !dependency.IsDone)
dependency.Completed += m_dependencyCompleteAction;
else
InvokeExecute();
}
**4. InvokeExecute : 执行Execute() 并在ResourceManager帧更新中添加回调 **
ResourceManager.m_UpdateCallbacks.Add(AsyncOperationBase.UpdateCallback)
ResourceManager.m_UpdateCallbacks 基于 MonoBehaviourCallbackHooks
5.ProviderOperation.Execute
protected override void Execute()
{
...
try
{
m_Provider.Provide(new ProvideHandle(m_ResourceManager, this));
}
catch (Exception e)
{
ProviderCompleted(default(TObject), false, e);
}
}
执行各类Provider的Provide 进行资源加载,例如 AssetBundleProvider.Provide
m_RequestOperation = AssetBundle.LoadFromFileAsync(path, m_Options == null ? 0 : m_Options.Crc);
m_RequestOperation.completed += LocalRequestOperationCompleted;
private void LocalRequestOperationCompleted(AsyncOperation op)
{
m_AssetBundle = (op as AssetBundleCreateRequest).assetBundle;
m_ProvideHandle.Complete(this, m_AssetBundle != null, null);
}
InternalOp.ProviderCompleted<T>(result, status, exception);
.
.
.
.
.
嗨,我是作者Vin129,逐儿时之梦正在游戏制作的技术海洋中漂泊。知道的越多,不知道的也越多。希望我的文章对你有所帮助:)