天天看點

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的例子翻譯過來吧……

<全文完>

繼續閱讀