天天看点

Unreal Engine 4 —— Asset Manager介绍

这篇博客介绍了UE4中的Asset Manager相关的内容,大部分参考的是Fortnite的公开资料。

背景(题外话)

如果不出意外的话,未来的一段时间将要回归UE4的全职开发了。用了将近9个月的unity,或多或少能感受到这两款商业引擎的设计理念的差异,也开阔了不少思路。在使用Unity 5的过程中发现它的Asset Management做的很不错,回想起来当初用UE4做Console的时候也没有遇到过这种需求 —— 不过这个功能在移动端应该是很常用的需求,因此也做个记录吧……

涉及到的类

  • Assest

    Asset

    指的是在

    Content Browser

    中看到的那些物件。贴图,BP,音频和地图等都属于Asset.
  • Asset Registry

    Asset Registry

    是资源注册表,其中存储了每个特定的asset的有用的信息。这些信息会在asset被储存的时候进行更新。
  • Streamable Managers

    Streamable Managers

    负责进行读取物件并将其放在内存中。
  • Primary Assets

    Primary Assets

    指的是在游戏中可以进行手动载入/释放的东西。包括地图文件以及一些游戏相关的物件,例如character classs或者inventory items.
  • Secondary Assets

    Secondary Assets

    指的是其他的那些Assets了,例如贴图和声音等。这一类型的assets是根据

    Primary Assets

    来自动进行载入的。
  • Asset Bundle

    Asset Bundle

    是一个Asset的列表,用于将一堆Asset在runtime的时候载入。
  • Asset Manager

    Asset Manager

    是一个可选的全局单例类,用于管理

    primary assets

    asset bundles

    ,这些东西在runtime的时候很有用。

Primary Assets

Primary Asset

指的是可以针对于

UObject::GetPrimaryAssetId()

返回一个有效的值的

UObject

对于默认的工程,所有在

/Game/Maps

路径下的Maps会被设为Primary Assets,而所有其他的assets如果需要设定为Primary Assets,都需要手动指定。

所有的

Primary Assets

是通过

FPrimaryAssetId

来进行引用的,

FPrimaryAssetId

拥有如下的属性:

  • PrimaryAssetType

    :这指的是一个用于描述物件的逻辑类型的名字,通常是基类的名字。例如,我们有两个继承自同一个本地类

    AWeapon

    的BP

    AWeaponRangedGrenade_C

    AWeaponMeleeHammer_C

    ,那么它们会有同样的

    PrimaryAssetType

    ——

    "Weapon"

  • PrimaryAssetName

    :这指的是用于描述这个asset的名字。通常来说,这就是这个object的名字(short name),但是对于例如说maps来说,这个值就是对应的asset path。
  • PrimaryAssetType:PrimaryAssetName

    可以组成整个游戏实例中的asset的唯一的描述。当客户端在和服务端通信的时候,就可以通过这个字符串来确认某个物件。例如,

    "Weapon:BattleAxe_Tier2"

    本质上和

    "/Game/Items/Weapons/Axes/BattleAxe_Tier2.BattleAxe_Tier2_C"

    是一样的。
  • FPrimaryAssetId

    中分别有两个

    FName

    的Tag,分别是

    PrimaryAssetTypeTag

    PrimaryAssetNameTag

    。因此,当一个

    Primary Asset

    被保存了之后,就可以直接在

    Asset Registry

    中通过这两个Tag来找到这个Asset。

Streamable Manager

FStreamableManager

可以用来处理assets的读取,并且将其在被需要的时候保存在内存中。针对不同的操作,可以有不同的

Streamable Manager

。FStreamableManager可以与

FStreamableHandle

一起工作,来更好的处理物件在内存中的生命周期。

  • FStreamableHandle

    是一个用于处理assets读取的结构体。通常来讲,在streaming operation会返回一个shared pointer,这个shared pointer就是用于追踪这个结构体的。当一个handle是active的时候,它就可以确保它引用的那些assets是在内存中的了。
  • FStreamableHandle

    从Loading开始就被激活了,当这个handle被显式cancel或者release的时候,将被disactive。
  • FStreamableHandle::ReleaseHandle()

    可以用来被显示调用。但是当所有的指向这个handle的只能指针被销毁的时候被隐式调用。
  • FStreamableHandle::CancelHandle()

    是用来中断Loading过程的接口。这个函数被调用后,Loading将会被中断,并且将取消Loading完成过后的所有回调函数。
  • FStreamableHandle::WaitUntilComplete()

    将阻断线程,知道所需的asset被成功载入为止。这个函数被调用时,所需的asset的载入优先级将被设为最高(通过将其移到优先级队列的top来实现),并且不会影响其他的异步载入操作,因此通常比

    LoadObject

    函数更快。
  • RequestAsyncLoad

    是基本的stream操作。可以传入一个

    FStringAssetReference

    或者一个

    FStringAssetReference

    的列表。调用了这个操作之后,引擎会试图去Load这些Assets,并且在Loading完毕之后调用回调函数。同时,这个函数会返回一个

    Streamable Handle

    shared pointer

    来供后期调用。
  • RequestSyncLoad

    RequestAsyncLoad

    的同步版本。这个函数要么会进行异步载入并且调用

    WaitUntilComplete

    函数,要么直接调用

    LoadObject

    函数 —— 哪个更快就调哪个。
  • LoadSynchronous

    是另一种Loading的方式,这个函数会返回一个asset,并且这个函数可以有模版安全的版本。
  • 这些函数都有

    bManageActiveHandle

    的参数,默认为false —— 如果设定为true的话,会导致

    streamable manager

    本身就带有一个这个handle的reference。这样一来的话就需要手动管理以及release这个handle了。

Asset Manager

AssetManager

是一个单例的UObject,它提供了在runtime的时候进行查询以及读取Primary Assets的操作。这个东西是原本用于取代

ObjectLibraries

当前提供的操作的,并且可以对

FStreamableManager

进行一层封装来处理Async Loading的操作。引擎内置的asset manager只能提供基本的管理操作,但是一些更加复杂的东西,例如caching需要自己实现。Asset Manager的基本操作如下:

  • Get()

    :单例的Get操作。
  • ScanPathsForPrimaryAssets(Type, Paths, BaseClass)

    :这个函数可以查询特定目录下的某类特定的

    primary asset

    ,如果在Editor下则直接读取磁盘上的信息,如果在cooked工程中会从asset registry cache中读取asset信息。
  • GetPrimaryAssetPath(PrimaryAssetId)

    : 获得PrimaryAssetId对应的asset的object path。
  • GetPrimaryAssetIdForPath(StringReference)

    : 获得object path对应的asset的primary asset id信息,以Type:Name的形式。
  • GetPrimaryAssetIdFromData(FAssetData)

    :通过

    FAssetData

    来获得对应的Type:Name形式的

    Primary Asset Id

  • GetPrimaryAssetData(PrimaryAssetId)

    :同理
  • GetPrimaryAssetDataList(Type)

    :返回一个所有对应类型的asset列表。
  • GetPrimaryAssetObject(PrimaryAssetId)

    :查询这个对应的UObject是否在内存中。如果这个UObject不再内存中,则返回nullptr。
  • LoadPrimaryAssets(AssetList, BundleState, Callback, Priority)

    :异步载入这些primary assets和BundleState所引用的所有其他assets。返回一个

    FStreamableHandle

    shared_pointer

    用于追踪,并且在Loading完成后调用回调函数。
  • UnloadPrimaryAssets(AssetList)

    :这个函数会调用这些primary assets的GC。
  • ChangeBundleStateForPrimaryAssets(AssetList, Add, Remove)

    :这个函数可以用一种更复杂的方式去处理bundle state。

Asset Bundles

Asset Bundle在Unity中很常见了——总体来说就是显式的primary assets相关的assets列表。

  • 从底层来看,一个Bundle本质上其实就是一个

    FName

    TArray<FStringAssetReferences>

    的map。每一个bundle都与一个

    Primary Asset Id

    相关。但是……这个东西也可以是一个动态的asset。
  • 如果需要对普通的Primary Asset生成对应的Asset Bundle,我们需要在自己的Object中加入一个

    FAssetBundleData

    UStruct

    ,并且在进行save操作的时候将这个

    UStruct

    进行填充。然后,这些数据就会被写在asset registry tag中,并且在这些asset数据被读取的时候,这个

    UStruct

    会被识别并处理。
  • AssetBundle meta tag可以被设定为确定的

    AssetPtr

    或者

    StringAssetReference

  • 可以通过调用

    AddDynamicAsset

    函数来在runtime的时候处理一些特定的asest bundle.
  • 在Asset Bundle中的任何东西,都会被认为是该primary asset的一部分。这在进行Chunking的时候会非常有用。

其他

整个的AssetManager是一个很大的系统,如果有个例子就更好了。

如果有时间就把Fortnite的例子翻译过来吧……

<全文完>

继续阅读