Unity2019吧-- Addressable Assets System 1的理解

孙广东  2018.7.21

原文 : Addressable Assets Systemを完全に理解する 

Unite 2018 Tokyo上有一个关于 Addressable Assets System的演讲!

演讲资料:(要翻墙) 【Unite 2018 Tokyo】そろそろ楽がしたい!新アセットバンドルワークフロー&リソースマネージャー詳細解説

日文肯定是不好看懂!

两个命名空间 :

using UnityEngine.AddressableAssets; 

using UnityEngine.ResourceManagement;

你可以使用 Addressable Assets System 做些什么?

到目前为止,我们如何处理Resources&AssetBundle的结构?

观点如下:

  • 你能做什么
    • 使用任意字符串(地址)加载资产
    • 您可以等效地处理Resources和AssetBundle(您可以自由更改与该地址对应的资产的位置)
    • 使用引用计数器卸载管理
    • AssetBundle的模拟模式(移动AssetBundle,无需构建)
    • 通过分析器可视化负载情况
  • 你不能做什么
  • 它与Variants不对应(因为它可以通过设计地址解析来对应,所以它首先变得不必要)

实际上,它似乎能够解决每个人独立实施的所有事情。

Addressable Assets System的 内部结构

  • Addressable Assets System 是由 Addressables和ResourceManager组成的系统!
    • Addressables和ResourceManager位于不同的包中
    • Addressables 依赖于 ResourceManager
    • 在PackageManager中引入Addressables时,ResourceManager会自动包含
    • 全部用C#编写
  • ResourceManager是一个用于正确管理资源加载和卸载的框架
    • 使用引用计数执行卸载管理
    • DI可以定制加载过程等
    • 它具有方便的功能,例如显示资源加载情况的分析器
  • Addressables是一个使用地址管理资产的系统

我上次在Unite之前写了这样一篇文章《Unity2018.2から搭載される(らしい)Resource ManagerとAddressable Asset Systemについて調べてみた》,但此时我只是在调查ResourceManager,它处于单方面状态...... (这很有意义,因为我知道ResourceManager的内部机制)

Addressables的概念

Addressables是一种将资产视为Addressable Assets(可寻址资产)并显着改变现有工作流程的系统。

到目前为止,您已经抛弃了AssetBundle的知识,让我们记住以下概念。

  • 所有资产都属于集团
    • 对于每个组,您可以更改包装方法 和放置(本地,远程等)等
    • 除非移动组,否则无需更改代码,除非地址更改
  • 所有资产都有地址
    • 地址可以任意指定
    • 从脚本加载资产时使用地址
    • 默认情况下,路径是地址
  • 通过使用AssetReference,您可以在不使用地址的情况下从Inspector上设置引用
    • 即使地址已更改,您也可以关注
  • 地址和资产之间的对应关系记录在内容目录中
    • 由json序列化
    • 包括依赖性信息等
    • 在初始化时加载
  • 您可以更改每个Asset的设置
    • 您可以分析配置文件,例如构建目标和加载源,并可以切换
    • 例如,您可以将资产加载源切换到本地存储/本地服务器/登台服务器/生产服务器等。
  • 您可以在Play模式下更改操作方法
    • 不需要AssetBundle构建的模式,从构建的AssetBundle加载的模式等。

ResourceManager的设计

ResourceManager是Addressables后端的系统,由以下类组成。

  • ResourceLocation:具有资产位置和依赖性
  • ResourceProvider:实现资源加载/卸载
  • SceneProvider:具有场景加载/卸载实现
  • InstanceProvider:具有实例创建/管理的实现

ResourceManager在初始化时注册要使用的提供程序,并在加载时传递ResourceLocation,使用适当的Provider执行加载处理。

除了内置的,您可以注册提供商的实现,也可以自己注册。

Addressables以下列方式与ResourceManager一起使用。

  • 在初始化时注册相应的Provider
  • 将地址转换为ResourceLocation并传递它ResourceManager

操作流程

我将以易于理解的图表显示一系列操作的流程。

初始化处理

应用程序启动时设置Addressables(RuntimeInitializeOnLoadMethod)

  • 加载内容目录
  • 将各种提供程序注册到ResourceManager(DI)

我会的。

加载资产

资产的加载在以下流程中完成。

  1. 将地址(字符串或AssetReference)传递给Addressables
  2. Addressables将传递的地址转换为ResourceLocation并将其传递给ResourceManager
  3. 为了正确加载传递的ResourceLocation,ResourceManager调用Provider来构造并返回AsyncOperation
  4. 在收到的AsyncOperation中注册加载完成时的处理

换句话说,我们实际上实现了第1部分和第4部分。

完全理解Addressable Assets System可寻址资产系统

从这里,让我们实际触摸并尝试完全理解它。

安装方法

使用Unity 2018.2或更高版本创建新项目,并使用PackageManager进行安装。

但是,由于它现在是预览版,因此无法从PackageManager安装。

               因此,编辑manifest.json如下。

(注 上面的0.1.2 版本是我在柏林的Unite视屏中看到的最新版本, 但是这个博文中提到的是 0.0.22)

manifest.json
{
  "dependencies": {
    "com.unity.addressables": "0.0.22-preview"
  }
}

准备资产

一切都没问题,我们将准备资产进行测试。

〜/ Assets / AddressableAssets下面列出了以下资产。

  • boys
    • boys_01.png
    • boys_02.png
    • boys_03.png
    • boys_04.png
    • boys_05.png
  • girls
    • girls_01.png
    • girls_02.png
    • girls_03.png
    • girls_04.png
    • girls_05.png
  • ui
    • buttons
    • ok.png
    • cancel.png

我借用了稻林先生的照片。

https://www.irasutoya.com/2014/10/faceicons.html

https://www.irasutoya.com/2013/10/blog-post_5077.html 男孩

https://www.irasutoya.com/2013/10/blog-post_3974.html 女孩

为资产提供地址

初始设定

您可以从 Window -> Asset Management -> Addressable Assets 中打开管理工具。

单击“Create Addressable Settings”时,将创建“Addressable Assets”的设置。

设置保存在 Assets / AddressableAssetsData 下。

请注意,删除此数据将删除地址等的设置。

创建数据时,将显示以下显示。

从Addressables窗口设置

树中的Default Local Group 资产。

插入目录时会出现这种情况,但在此状态下无法处理 。

灰色资产未得到解决,无法访问它们。

要分配地址,每个资产必须直接放在该组下。

目前,没有准备的功能,称为“可寻址一次都某个目录下的资产”,手动或做什么,你需要在脚本设置它。

我制作了一个脚本来转换目录条目,因为它很麻烦。如果插入资产的根目录并执行以下脚本,则所有资产都直接在组下移动。

using UnityEditor.AddressableAssets;

using System.IO;

public static AddressableAssetsUtil

{

[UnityEditor.MenuItem("Addressable/MoveSubEntryToRoot")]

public static void MoveSubEntryToRoot()

{

var aaSettings = AddressableAssetSettings.GetDefault(false, false);

// 直接在组下移动所有子条目

var entries = aaSettings.GetAllAssets(true, true);

foreach (var entry in entries)

{

entry.address = entry.assetPath;

aaSettings.CreateOrMoveEntry(entry.guid, entry.parentGroup);

}

// 删除变为空的目录条目

entries = aaSettings.GetAllAssets(true, false);

foreach (var entry in entries)

{

var isDirectory = Directory.Exists(entry.assetPath);

if (isDirectory) aaSettings.RemoveAssetEntry(entry.guid);

}

}

}

如果变成如下那就OK。

然而,仍然有一个问题,如果它包含两个或两个以上/(斜杠)要解决,还有的是,有时并不在某些情况下 以及工作中的错误。

出于这个原因,让我们通过右键单击所选的所有资产条目来运行Simplify Entry Names 。

您现在可以使用地址访问资产了。

从Inspector设置地址

虽然我没有使用上图的过程设置,但也可以从Inspector编辑资产地址。

加载资产

制作一个正确放置Image的场景。

让我们用另一个加载了Addressables的资产替换这个Image。

使用地址加载

首先将新脚本附加到Image对象上。

制作以下脚本。一个简单的例子,在启动和替换m_img的精灵时加载boy_02。

using UnityEngine;

using UnityEngine.AddressableAssets;

using UnityEngine.UI;

public class TestMonoBehavior : MonoBehaviour {

public Image m_img;

void Start()

{

// //加载指定的地址

Addressables.LoadAsset<Sprite>("boy_02")

.Completed += op => { m_img.sprite = op.Result; };

}

}

对于m_img,将Image的引用在 Inspector上设置为:

如果这样做,您可以看到Sprite被替换。

使用AssetReference加载

使用AssetReference,您可以从Inspector设置可寻址资产引用,而无需对地址进行硬编码。

让我们改变上面的脚本如下。

  • 变化
    • 将 AssetReference 添加到作为 成员
    • 将LoadAsset的参数更改为AssetReference

using UnityEngine;

using UnityEngine.AddressableAssets;

using UnityEngine.UI;

public class TestMonoBehavior : MonoBehaviour {

// AssetReferenceTypeRestriction:可以限制可以设置的资产类型

    //因为没有Sprite选项,所以我将其设置为Texture 2D

[AssetReferenceTypeRestriction(typeof(Texture2D))]

public AssetReference m_ref;

public Image m_img;

void Start()

{

// 使用AssetReference作为参数读取

Addressables.LoadAsset<Sprite>(m_ref)

.Completed += op => { m_img.sprite = op.Result; };

}

}

接下来,从Inspector设置AssetReference。让我们建立一个不同的资产。

可从下拉菜单中选择。

如果这样做,您可以看到Sprite已更改为设置资产。

作为AssetReference的一项功能,即使地址发生变化,也可以遵循更改。 AssetReference因为它拥有国内资产的GUID,只要GUID不会改变,你可以遵循。

使用Profiler分析器检查负载状态

Window -> Asset Management -> Resource Profiler 打开。

如果在打开Profiler的情况下运行它,则会显示加载状态。

可以从分析器中读取以下信息。

  • 发生加载事件时
  • 已加载资产的地址
  • 资产参考计数
  • 加载资产来源,加载方法

Play模式下改变

您可以从Addressables窗口右上角的设置图标更改播放模式。

  • Fast Mode: 快速模式:直接加载文件而不打包,快速但Profiler获取的信息较少
  • Virtual Mode:虚拟模式:在不打包的情况下模拟靠近AssetBundle的操作
  • Packed Mode:打包模式:实际上是从AssetBundle打包和​​加载

让我们实际改变播放模式,看看探查器中的显示是如何变化的。

Fast Mode快速模式

在此模式下,我们使用 AssetDatabase.LoadAssetAtPath 直接加载文件。虽然AssetBundle构建可以不要求立即执行的,它不能在探查证实,如果他们被列入其中AssetBundle资产。

Virtual Mode虚拟模式

与FastMode不同,您可以查看哪个AssetBundle包含资产。

此模式最终还加载了AssetDatabase.LoadAssetAtPath

虽然AssetBundle构建是没有必要的,第一为了构建AssetBundle和资产之间的对应关系。

还可以通过设置虚拟通信速度来模拟加载时间。

Packed Mode打包模式

在这种模式下,实际构建并加载AssetBundle。在编辑器中运行时,它会在不构建 时自动构建。 (判决方法等细节尚未调查)

分析器中显示的信息与VirtualMode几乎相同。

在结束时,你必须加载使用 AssetBundle.LoadFromFileAsyncUnityWebRequestAssetBundle.GetAssetBundle(URI uri) 的AssetBundle。

更改资产的目标(加载源)

通过改变资产的GroupType场,它能够改变要在其中放置详细(负载源)的资产。

通过右键单击“Addressables”窗口上的组,可以从上下文菜单更改GroupType。

GroupType有以下三种。

  • Local Packed Content(默认设置)
    • 只读当地环境
    • 构建StreamingAssets并从StreamingAssets加载
    • LocalAssetBundleProvider用于加载
    • 在内部,我们使用AssetBundle.LoadFromFileAsync
    • 无法进行详细设置
  • Remote Packed Content远程打包内容
    • 与Local不同,您可以设置构建和加载源
    • RemoteAssetBundleProvider用于加载
    • 我们使用UnityWebRequestAssetBundle.GetAssetBundle(URI uri)内部
    • 由于未传递版本version和哈希hash,因此不会进行缓存
    • 由于UnityWebRequest也可以加载本地文件,加载源文件:也可以从本地加载它//
  • Advanced Packed Content高级打包内容
    • 与Remote一样,您可以设置构建目标和加载源
    • 您可以指定要用于加载的提供程序
    • 当您想要自定义加载处理(加密,专有协议等)时使用

从服务器加载资产

这次我将在本地启动服务器并从那里加载。

小组设定

在“Addressables”窗口中,右键单击该组并将其更改为“Remote Packed Content远程打包内容”。

如果选择处于此状态的组,则设置项将显示在窗口的底部。

  • Packing Mode包装方式:选择AssetBundle的包装方式。
    • Pack Together: 打包在一起:放在一个AssetBundle中
    • Pack Separately: 单独打包:单独的AssetBundle
  • Build Path:构建路径:在哪里构建AssetBundle
  • Load Prefix:加载前缀:用于加载的路径
    • 在模式字符串传递给短提供商(UnityWebRequest),{Load Prefix负载前缀} / {路径AssetBundle}变为最终URI

让我们将Packing Mode打包模式更改为Separately单独,Build Path构建路径,将Load Prefix加载前缀到Remote远程。

Load Prefix加载前缀现在是localhost。

如果您在此状态下播放Play模式设置为“Packed已打包”模式,则加载将失败。

将AssetBundle放在服务器上

在这里,确保AssetBundle是在ServerData下构建的。

如果未正确构建,则可能由于缓存的影响而无法构建。让我们准备并执行以下脚本 。

using UnityEditor.AddressableAssets;

public static AddressableAssetsUtil

{

[UnityEditor.MenuItem("Addressable/Build")]

public static void Build()

{

//应该预留BuildTarget

BuildScript.PrepareRuntimeData(true, false, true, true, false, UnityEditor.BuildTargetGroup.Standalone, UnityEditor.BuildTarget.StandaloneOSX);

}

}

准备好AssetBundle时,让我们在本地设置AssetBundle分发服务器。这是一个很好的东西,甚至连nginx的Apache的,但在设置文档根目录建立{项目的路径}目的地/ ServerData。

点是http://localhost/{BuildTarget}/{AssetBundle的路径} 命中的文件的AssetBundle状态。

将它与Apache标准配合使用会很有趣。 (当我们谈论如何设置服务器时,我们不会在这里详细说明)

如果您在服务器上运行它并且可以正常加载它,您可以看到它是使用分析器从localhost加载的。

使用配置文件切换负载源

可以使用Profile更改Build Path构建路径,Load Prefix加载前缀设置。

这允许,例如,负载的源文件 file://localhost,而暂存服务器,即,例如在每个切换到生产服务器,也能够通过简单地切换配置文件来实现。

从窗口左上角选择“Manage Profiles管理配置文件”以打开配置文件设置窗口。

在文件file:// 和 localhost 之间切换

让我们尝试使用profile在file://和localhost之间切换。

您可以从“Manage Profile 管理配置文件”列表按钮添加配置文件。添加「Remote」配置文件并尝试以下设置。

MyLoadPrefix条目已添加 并设置如下。

  • Default6:file://[AssetPath.ProjectPath]/ServerData/[UnityEditor.EditorUserBuildSettings.activeBuildTarget]
    • 由于API获取项目路径不标准,我们自己准备(见下文)
  • Remote: http://localhost/[UnityEditor.EditorUserBuildSettings.activeBuildTarget]

可以通过在 [] 中包含配置文件的值来使用静态变量。如果要从脚本控制配置文件的值,可以通过自己编写静态属性来编写它。

由于我们需要获取项目的路径以获取ServerData目录的路径,因此我们实现了以下属性。

public static class AssetPath

{

public static string ProjectPath

{

get

{

return System.IO.Directory.GetCurrentDirectory();

}

}

}

设置配置文件后,让我们将组LoadPrefix更改为MyLoadPrefix。

在此状态下,如果从左上角的菜单中选择配置文件,则可以看到“Load Prefix”切换。如果运行它并使用Profiler进行检查,则可以检查负载源是否正常切换。

带标签的批量装载

标签可以附加到资产和地址,例如“加载指定了特定标签的所有资产”。

尝试并按如下方式加载试用版中的「girls」标签。

将以下代码放在适当的位置。

Addressables.PreloadDependencies("girls", null);

.Completed += op => {

Addressables.LoadAsset<Sprite>("girl_01");

};

您可以看到已标记资产的AssetBundle立即加载和卸载。

在此状态下加载 girl_01 会从下载的AssetBundle加载AssetBundle,因为已经下载了AssetBundle。

它可能用于将标签附加到您要预先下载的AssetBundle并使用它加载它。

内容目录的工作原理

使用构建的AssetBundle,您会注意到有一个名为 catalog_1.json 的有意义的文件。

我猜这是内容目录的标识。但是,我也记得有一个名为 catalog_1.hash 的神秘文件。

所以我们将看一下内容目录的机制。

内容目录的内容

总之,每个文件的标识如下。

  • catalog_1.json:具有JSONUtility的序列化内容目录(ResourceLocationList)
  • catalog_1.hash:上面json的MD5哈希值

内容是这样的。 (Json正在塑造正在缩小的东西,以便于查看)

catalog_1.json

{

"locations":[

{

"m_address":"SampleScene",

"m_guid":"2cda990e2423bbf4892e6590ba056729",

"m_type":0,

"m_typeName":"UnityEngine.SceneManagement.Scene",

"m_internalId":"Scenes/SampleScene",

"m_provider":"UnityEngine.ResourceManagement.SceneProvider",

"m_labelMask":0,

"m_dependencies":[

],

"m_isLoadable":true

},

{

"m_address":"0",

"m_guid":"2cda990e2423bbf4892e6590ba056729",

"m_type":1,

"m_typeName":"UnityEngine.SceneManagement.Scene",

"m_internalId":"Scenes/SampleScene",

"m_provider":"UnityEngine.ResourceManagement.SceneProvider",

"m_labelMask":0,

"m_dependencies":[

],

"m_isLoadable":true

},

{

"m_address":"defaultlocalgroup_assets_boy_01.bundle",

"m_guid":"",

"m_type":0,

"m_typeName":"UnityEngine.AssetBundle",

"m_internalId":"file:///Users/user/Addressable Test/ServerData/StandaloneOSX/defaultlocalgroup_assets_boy_01.bundle",

"m_provider":"UnityEngine.ResourceManagement.RemoteAssetBundleProvider",

"m_labelMask":0,

"m_dependencies":[

],

"m_isLoadable":false

},

{

"m_address":"boy_01",

"m_guid":"7276a10699bf545ab88993a205239a28",

"m_type":0,

"m_typeName":"UnityEngine.Texture2D",

"m_internalId":"Assets/AddressableAssets/boys/boy_01.png",

"m_provider":"UnityEngine.ResourceManagement.BundledAssetProvider",

"m_labelMask":0,

"m_dependencies":[

"defaultlocalgroup_assets_boy_01.bundle"

],

"m_isLoadable":true

}

//~中略~//

],

"labels":[

"default",

"girls"

]

}

catalog_1.hash

ac03d2db95e6f339f366f0cc39218c87

内容目录加载处理

作为一个流程我觉得如下。

  • 在启动时使用 RuntimeInitializeOnLoadMethod 调用加载过程
  • 去阅读远程 catalog_1.hash
  • 与本地缓存的哈希值进行比较并加载 catalog_1.json
    • 如果哈希值与本地缓存中的相同,就Load
    • 如果哈希值不同,请从远程加载它并将其缓存在 Application.persistentDataPath

通过哈希值完成缓存的点就是重点。除非更新,否则仅对哈希值进行通信。

关于解析内容目录本身的位置

我理解加载内容目录的机制,但是如何解决内容目录本身的位置?我认为它是否似乎有疑问。

有关放置内容目录的位置的信息存储在 ResourceManagerRuntimeData 中。 ResourceManagerRuntimeData在构建时生成,在json处序列化,并放在StreamingAssets下。

换句话说,在构建应用程序时,有必要预先生成 ResourceManagerRuntimeData。

如果您使用UnityEditor.AddressableAssets.BuildScript.BuildPlayer,ResourceManagerRuntimeData将预先构建,然后构建应用程序。

(在编辑器中运行时,每次自动生成读取)

ResourceManagerRuntimeData的位置已成为 StreamingAssets/ResourceManagerRuntimeData_settings.json 在跳动决定的,你必须加载内容目录之前加载它。

实际内容如下,您可以看到内容目录的位置已标记。

ResourceManagerRuntimeData_settings.json

{

"settingsHash":"13f9f4a40ed6bec5d9ad350892e96fdf4",

"catalogLocations":{

"locations":[

{

"m_address":"0_RemoteCatalog_102d3183f9bcc41febf5fd015f404569",

"m_guid":"",

"m_type":0,

"m_typeName":"",

"m_internalId":"http://localhost/StandaloneOSX/catalog_{ContentVersion}.json",

"m_provider":"UnityEngine.AddressableAssets.ContentCatalogProvider",

"m_labelMask":1,

"m_dependencies":[

"LocalCatalogHash102d3183f9bcc41febf5fd015f404569",

"RemoteCatalogHash102d3183f9bcc41febf5fd015f404569"

],

"m_isLoadable":true

},

{

"m_address":"LocalCatalogHash102d3183f9bcc41febf5fd015f404569",

"m_guid":"",

"m_type":0,

"m_typeName":"",

"m_internalId":"{UnityEngine.Application.persistentDataPath}/catalog_{ContentVersion}.hash",

"m_provider":"UnityEngine.ResourceManagement.TextDataProvider",

"m_labelMask":0,

"m_dependencies":[

],

"m_isLoadable":false

},

{

"m_address":"RemoteCatalogHash102d3183f9bcc41febf5fd015f404569",

"m_guid":"",

"m_type":0,

"m_typeName":"",

"m_internalId":"http://localhost/StandaloneOSX/catalog_{ContentVersion}.hash",

"m_provider":"UnityEngine.ResourceManagement.TextDataProvider",

"m_labelMask":0,

"m_dependencies":[

],

"m_isLoadable":false

}

],

"labels":[

]

},

"usePooledInstanceProvider":false,

"profileEvents":true,

"contentVersion":"1",

"assetCacheSize":25,

"assetCacheAge":5.0,

"bundleCacheSize":5,

"bundleCacheAge":5.0

}

http://localhost/StandaloneOSX/catalog_{ContentVersion}.json ,因为它包含了自己在ResourceManagerRuntimeData ContentVersion,内容目录,除非你建立在当前形势下的应用程序不会允许你改变的负载目的地 。

我觉得我能在这里改变一下。

最后

我认为目前可以使用的功能受到相当大的限制。 。

至今以来的AssetBundle关系可能的麻烦要解决大分,我想期待正式发布7!

就个人而言,我认为我想要以下功能。

  • 能够在节点的基础上设置地址,如AssetBundleGraphTool
  • 一种功能,可以在任意时间轻松执行构建
    • 它很麻烦,因为它是在未经许可的情况下构建的,并且内容目录已更新
  • 能够在加载时任意改变AssetBundle的路径模式(Load Suffix like)
    • 我想在最后添加版本号和哈希
    • AssetBundle缓存支持
  • 如果您自己编写Provider,似乎可以处理它,但我希望您按标准进行响应

我很高兴与AssetBundleGraphTool整合......我很高兴......

总之 在Unity 2018.2 已经可以测试它了!!!!

猜你喜欢

转载自blog.csdn.net/u010019717/article/details/81146982