前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Unity-Assets/Resources and AssetBundles

Unity-Assets/Resources and AssetBundles

作者头像
祝你万事顺利
发布2019-07-10 17:17:52
1.8K0
发布2019-07-10 17:17:52
举报
文章被收录于专栏:Unity游戏开发Unity游戏开发

1.1.Inside Assets and Objects

Distinction betwwen Assets and UnityEngine.Objects. An Asset is a file on disk,stored in the Assets folder of a Unity project. Texture,3D models , or audio clips are common types of Assets. A UnityEngine.Object, or Object is a set of serialized data collectively describing a specific instance of a resource.All objects are subclasses of the UnityEngine.Object base class.

Two special types:

  1. A ScriptableObject provides a convenient system for developers to define their own data types. These types can be natively serialized and deserialized by Unity, and manipulated in the Unity Editor's Inspector window.
  2. A MonoBehaviour provides a wrapper that links to a MonoScript. A MonoScript is an internal data type that Unity uses to hold a reference to a specific scripting class within a specific assembly and namespace. The MonoScript does not contain any actual executable code.

1.2.Inter-Object references

All UnityEngine.Objects can have references to other UnityEngine.Obejcts.These other Objects may reside within the same Asset file, or may be imported from other Asset files. When serialized, these references consist of two separate pieces of data: a File GUID and a Local ID. The file GUID identifies the Assets file where the target resource is stored. A locally unique local ID identifies each Object within an Asset file because an Asset file may contain multiple Objects File GUIDs are stored in .meta files.These .meta files are generated when Unity first imports an Assets, and are stored in the same directory as the Asset.

1.3.Why File GUIDs and Local IDs?

文件 GUID 提供了文件位置的抽象。只要文件 GUID 和一个文件关联上,那文件在磁盘上的位置就变得无关紧要了。这个文件可以随意移动,而不必更新所有引用了该文件的对象。 一个资产文件可能包含多个 UnityEngine.Object,为了清楚的区分它们,需要本地 ID。 Unity 编辑器拥有已知文件 GUID 到文件路径的映射。这个映射实体会把资产的文件路径和文件 GUID 关联起来。如果 Unity 编辑器打开时,一个 .meta 文件丢失而资产的路径并没有改变的资产,编辑器会确保这个资产得到相同的文件 GUID。

1.4.Composite Assets and importers

non-native Asset types must be imported into Unity.This is done via an asset importer.The result of the import process is one or more UnityEngine.Objects. These are visible in the Unity Editor as multiple sub-assets within the parent Asset,such as multiple sprites nested beneath a texture Asset that has been imported as a sprite atlas. Each of these Objects will share a File GUID as their source data is stored within the same Asset file. 导入过程会将源资产文件转换成在 Unity 编辑器中选中的目标平台合适的格式。导入过程也可能会带有比较重的操作,比如纹理压缩。如果每次 Unity 编辑器打开的时候都要执行导入过程的话会是 Unity 编辑器变得特别没有效率。 作为解决方案,Unity 会讲资产导入后的结果缓存到 Libraray 文件夹。导入后的结果会缓存到以资产的文件 GUID 前两个字母命名的文件夹中。这个文件夹在 Library/metadat/ 文件夹内。每个独立的对象都会被序列化为单独的以它们资产文件 GUID 命名的二进制文件。

1.5.Serialization and instances

GUID的比较是比较慢的,这需要一个在运行时更高效的系统,Unity内部维持了一个能把文件GUID和本地ID换成在独立会话内唯一的,简单的数字的缓存。这个数字叫做实例ID。当新的对象注册到缓存时,会给它分配一个严格递增的值。 这个缓存维护了给定的实例ID、对象源文件中定义的文件GUID和本地ID和内存中对象的映射关系。它让UnityEngine.Objects稳定的维护的各个对象间的引用成为可能。通过一个示例ID的引用可以快速的返回这个ID对应的对象。如果这个对象没有加载,Unity可以根据FileID和本地ID来实时加载对象。

At startup, the Instance ID cache is initialized with data for all Objects immediately required by the project (i.e., referenced in built Scenes), as well as all Objects contained in the Resources folder. Additional entries are added to the cache when new assets are imported at runtime and when Objects are loaded from AssetBundles. Instance ID entries are only removed from the cache when an AssetBundle providing access to a specific File GUID and Local ID is unloaded.When this occurs, the mapping between the Instance ID, its File GUID and Local ID are deleted to conserve memory. If the AssetBundle is re-loaded, a new Instance ID will be created for each Object loaded from the re-loaded AssetBundle. On specific platforms, certain events can force Objects out of memory. For example, graphical Assets can be unloaded from graphics memory on iOS when an app is suspended. If these Objects originated in an AssetBundle that has been unloaded, Unity will be unable to reload the source data for the Objects. Any extant references to these Objects will also be invalid. In the preceding example, the scene may appear to have invisible meshes or magenta textures.

1.6.MonoScripts

MonoBehaviour拥有一个MonoScript,MonoScript仅包含简单的用来定位特定脚本的信息比较重要。构建项目的时候,Unity收集所有Assets文件下零散放置的脚本,然后将他们编译成Mono程序集。Unity会为Assets文件夹下的不同语言和Assets/Plugins文件夹下的脚本构建单独的程序集。在Plugins子文件夹外的C#脚本会编译到Assembly-CSharp.dll中,而Plugins子文件夹内的脚本会编译到Assembly-CSharp-firstpass.dll中。 这些程序集会被包含到Unity应用的最终构建里面。他们也是MonoScript引用的程序集。与其他资源不同,所有Unity程序内的程序集会在程序第一次启动时加载。 ?因为有 MonoScript 对象,AssetBundle(或者是场景文件,或者是预设)中 MonoBehaviour 组件可以不包含实际运行代码。这使得不同的 MonoBehaviour 可以指向特定的共享的类,即使这些不同的 MonoBehaviour 在不同的 AssetBundle 中。

1.7.Resource的生命周期

UnityEingine.Objects会在具体或者特定的时间从内存中加载/卸载。 有两种方式可以加载UnityEngine.Objects: 自动的和显式的。当一个实例ID映射到一个源数据存在,但是没加载到内存并被间接引用的对象时,对象会被自动创建。 对象可以在Script中显式加载。显示加载方式要可以使直接创建他们,也可以是通过资源加载的API,例如AssetBundle.LoadAsset。 当一个对象被加载,Unity会尝试将所有引用就从文件GUID和本地ID转换成实例ID.

Understand the resource lifecycle of UnityEngine.Objects.Objects are loaded into/unloaded from memory at specific and defined times. An Object is loaded automatically when: 1.The Instance ID mapped to that Object is dereferenced 2.The Object is currently not loaded into memory 3.The Object's source data can be located

当满足下面两个条件时,一个对象在它的实例ID第一次引用是按需加载: 1.实例ID引用了没有加载的对象 2.实例ID在缓存中有效的、对应文件GUID和本地ID

如果一个文件GUID和本地ID不包含实例ID,或者一个实例ID关联一个引用无效的文件GUID和本地ID的未加载的对象,实例ID引用将会保留但是世纪对象缺少不能加载。这个在Unity编译器里面显示为(Missing)。在程序运行时或者场景视图里,基于(Missing )对象的类型,会有下面几种显示:比如网格不可见,纹理显示成洋红色。

Objects are unloaded in three specific scenarios: 1.Objects are automatically unloaded when Asset cleanup occurs.This process is triggered automatically when scenes are changed destructively (i.e. when SceneManager.LoadScene is invoked non-additively), or when a script invokes the Resources.UnloadUnusedAssets API.This process only unloads unreferenced Objects; an Object will only be unloaded if no Mono variable holds a reference to the Object, and there are no other live Objects holding references to the Object. Furthermore, note that anything marked with HideFlags.DontUnloadUnusedAsset and HideFlags.HideAndDontSave will not be unloaded. 场景变化卸载 2.Objects sourced from the Resources folder can be explicitly unloaded by invoking the Resources.UnloadAsset API. The Instance ID for these Objects remains valid and will still contain a valid File GUID and LocalID entry. If any Mono variable or other Object holds a reference to an Object that is unloaded with Resources.UnloadAsset, then that Object will be reloaded as soon as any of the live references are dereferenced. Resource卸载 3.Objects sourced from AssetBundles are automatically and immediately unloaded when invoking the AssetBundle.Unload(true) API. This invalidates the File GUID and Local ID of the Object's Instance ID, and any live references to the unloaded Objects will become "(Missing)" references. From C# scripts, attempting to access methods or properties on an unloaded object will produce a NullReferenceException. AB对象卸载 If AssetBundle.Unload(false) is called, live Objects sourced from the unloaded AssetBundle will not be destroyed, but Unity will invalidate the File GUID and Local ID references of their Instance IDs. It will be impossible for Unity to reload these Objects if they are later unloaded from memory and live references to the unloaded Objects remain. (Note: The most common case where Objects are removed from memory at runtime without being unloaded occurs when Unity loses control of its graphics context. This may occur when a mobile app is suspended and the app is forced into the background. In this case, the mobile OS usually evicts all graphical resources from GPU memory. When the app returns to the foreground, Unity must reload all needed Textures, Shaders and Meshes to the GPU before scene rendering can resume.)

1.8.Loading large hierarchies

When creating any GameObject hierarchy,CPU time is spent in several different ways:

  • Reading the source data 读取源数据
  • Setting up the parent-child relationships between the new Transforms 构建新的Transform间父-子关系
  • Instantiating the new GameObjects and Components 实例化新的游戏对象和组件
  • Awakening the new GameObjects and Components on the main thread 在主线程中激活新游戏对象和组件时间 后三种时间花费一般时不变的,不论是从现成结构中或者从存储中加载。但是读取源数据的时间与层次结构中的组件和游戏对象成线性增加的关系,当然还要乘以读取源数据的速度。 在当前的全平台中,从内存中读取数据要更快比从硬盘中读取数据。在读取较慢的平台,从硬盘中读取prefab序列化的数据将超过实例化prefab的时间。

As mentioned before, when serializing a monolithic prefab, every GameObject and component's data is serialized separately, which may duplicate data. For example, a UI screen with 30 identical elements will have the identical element serialized 30 times, producing a large blob of binary data. At load time, the data for all of the GameObjects and Components on each one of those 30 duplicate elements must be read from disk before being transferred to the newly-instantiated Object. This file reading time is a significant contributor to the overall cost of instantiating large prefabs. Large hierarchies should be instantiated in modular chunks, and then be stitched together at runtime.

关于实例化的优化: Unity 5.4 note: Unity 5.4 altered the representation of transforms in memory. Each root transform's entire child hierarchy is stored in compact, contiguous regions of memory. When instantiating new GameObjects that will be instantly reparented into another hierarchy, consider using the new GameObject.Instantiate overloaded variants which accept a parent argument. Using this overload avoids the allocation of a root transform hierarchy for the new GameObject. In tests, this speeds up the time required for an instantiate operation by about 5-10%.

3.The Resources folder

2.1.Best Practices for the Resources System

Don't use it.

  • Resources folder 让内存管理变得更加困难。
  • 不恰当的使用Resources文件增加application的启动时间和包的大小。
  • Resources System将降低项目自定义平台的能力并且消除了增量更新的可能性。

2.2.Proper uses of the Resources system

  • 在项目原型阶段使用Resources 文件进行开发,当项目进入full production阶段,Resources文件应该被消除。
  • 在整个项目周期中经常被使用
  • 占用内存很小
  • 不需要升级更新
  • Used for minimal bootstrapping

2.3.Serialization of Resources

The Assets and Objects in all folders named "Resources" are combined into a single serialized file when a project is built. This file also contains metadata and indexing information, similar to an AssetBundle. On most platforms, the lookup data structure is a balanced search tree, which has a construction time that grows at an O(n log(n)) rate. This growth also causes the index's loading time to grow more-than-linearly as the number of Objects in Resources folders increases.

AssetBundle fundamentals

一个AssetBundle包括两个部分:a header and data segment The header contains information about the AssetBundle,such as its identifiers,compression type, and a manifest. The manifest is a lookup table keyed by an Object's name. Each entry provides a byte index that indicates where a given Object can be found within the AssetBundle's data segment. On most platforms, this lookup table is implemented as a balanced search tree.

3.3.Loading AssetBundles

AB can be loaded via four distinct APIs. The behavior of these four APIs is different depending on two criteria:

  • Whether the AB is LZMA compressed,LZ4 compressed or uncompressed
  • The platform on which the AssetBundle is being loaded.

These APIs are:

  • AssetBundle.LoadFromMemory(Async optional)
  • AssetBundle.LoadFromFile(Async optional)
  • UnityWebRequest's DownloadHandlerAssetBundle
  • WWW.LoadFromCacheOrDownload (on Unity 5.6 or older)
3.3.1 AssetBundle.LoadFromMemory(Async)

Unity's recommendation is not to use this API. AssetBundle.LoadFromMemoryAsync 从托管代码字节数组(C# 中的 Btye[])中加载 AssetBundle。它总是会从本地内存中开辟一段连续内存,然后从托管代码的字节数组中拷贝源数据到这段新分配的内存中。如果 AssetBundle 是 LZMA 压缩格式的,拷贝过程中 AssetBundle 会被解压。而 LZ4 压缩格式的 AssetBundle 会原原本本的拷贝过去。

AssetBundle.LoadFromMemoryAsync 从托管代码字节数组(C# 中的 Btye[])中加载 AssetBundle。它总是会从本地内存中开辟一段连续内存,然后从托管代码的字节数组中拷贝源数据到这段新分配的内存中。如果 AssetBundle 是 LZMA 压缩格式的,拷贝过程中 AssetBundle 会被解压。而 LZ4 压缩格式的 AssetBundle 会原原本本的拷贝过去。

3.3.2. AssetBundle.LoadFromFile

从本地存储中高效的加载未压缩的AssetBundle。如果AssetBundle未压缩或者使用LZ4压缩,这个API有如下表现。 移动设备:API只会加载AssetBundle的Header,其他数据保留在磁盘中。当调用加载的方法或者他们实例ID被间接引用时对象会被按需加载。在这种情况下没有额外的内存开销。

Unity编译器:这个API会将整个AssetBundle加载进内存,而不像从磁盘上读取所有字节,使用AssetBundle.LoadFromMemoryAsync。

3.3.3. AssetBundleDownloadHandler

The UnityWebRequest API allows developers to specify exactly how Unity should handle downloaded data and allows developers to eliminate unnecessary memory usage. The simplest way to download an AssetBundle using UnityWebRequest is call UnityWebRequest.GetAssetBundle.下载一个AssetBundle

LZMA压缩的AB包将解压然后LZ4再压缩一下, 下载完成后,assetBundle属性提供了对下载数据中AssetBundle的访问。

If caching information is provided to a UnityWebRequest object, and the requested AssetBundle already exists in Unity's cache, then the AssetBundle will become available immediately and this API will operate identically to AssetBundle.LoadFromFile.

3.3.4. WWW.LoadFromCacheOrDownload

WWW.LoadFromCacheOrDownload is an API that allows loading of Objects from both remote servers and local storage. Files can be loaded from local storage via a file:// URL. If the AssetBundle is present in the Unity cache, this API will behave exactly like AssetBundle.LoadFromFile.

3.3.5. Loading Assets From AssetBundles
3.4.1. Low-level loading details
3.4.4. Recommendations

In many cases, it is preferable to load as many needed Objects as possible before players enter performance-critical areas of an application, such as the main game level or world. This is particularly critical on mobile platforms, where access to local storage is slow and the memory churn of loading and unloading Objects at play-time can trigger the garbage collector. For projects that must load and unload Objects while the application is interactive, see the Managing loaded assets section of the AssetBundle usage patterns step for more information on unloading Objects and AssetBundles.

AssetBundle usage patterns

管理加载后的资产

大多数项目应该使用 AssetBundle.Unload(true) 并且使用方法确保对象不会有重复副本。有两种通用的方法: 1.在应用生命周期中,临时 AssetBundle 卸载有明确定义的点,比如两个关卡之间或者加载场景的时候。 2.维护单个物体的引用计数,并当组成 AssetBundle 的对象都未被使用时卸载 AssetBundle。

如果必须用AssetBundle.Unload(false), 则单个对象能通过下面两种方式卸载: 1.在场景和代码中删除不需要对象的所有引用。完成之后调用 Resources.UnloadUnusedAssets 2.非增量方式加载场景。这个行为会卸载当前场景中的所有对象,然后自动的调用 Resources.UnloadUnusedAssets()

参考资料: http://blog.shuiguzi.com/2017/04/18/AssetBundle_usage_pattern_1/

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.07.08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.1.Inside Assets and Objects
  • 1.2.Inter-Object references
  • 1.3.Why File GUIDs and Local IDs?
  • 1.4.Composite Assets and importers
  • 1.5.Serialization and instances
  • 1.6.MonoScripts
  • 1.7.Resource的生命周期
  • 1.8.Loading large hierarchies
  • 3.The Resources folder
    • 2.1.Best Practices for the Resources System
      • 2.2.Proper uses of the Resources system
        • 2.3.Serialization of Resources
        • AssetBundle fundamentals
          • 3.3.Loading AssetBundles
            • 3.3.1 AssetBundle.LoadFromMemory(Async)
            • 3.3.2. AssetBundle.LoadFromFile
            • 3.3.3. AssetBundleDownloadHandler
            • 3.3.4. WWW.LoadFromCacheOrDownload
            • 3.3.5. Loading Assets From AssetBundles
            • 3.4.1. Low-level loading details
            • 3.4.4. Recommendations
            • 管理加载后的资产
        • AssetBundle usage patterns
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档