资源管理

资源加载与卸载

Windows
MacOS
Linux

虚幻引擎 4 自动处理 资源 加载与卸载,开发者无需编写系统代码告知引擎具体所需的资源。然而,某些情况下开发者可能需要更精确地掌控资源发现、加载与审核的时机与方法。在这些情况下,资源管理器(Asset Manager) 便能大显身手。资源管理器是存在于编辑器和打包游戏中的独特全局对象,可根据不同项目进行覆盖和自定义。它提供了一个管理资源的框架,可将内容划分为数据块,对应项目的上下文,而同时保证虚幻引擎 4 松散打包架构 的优势。它同时提供了一套工具,协助审核硬盘和内存使用,提供所需信息,以优化资源组织,在部署游戏时进行 烘焙和数据块划分

主资源、次资源和主资源标签

从概念上来说,虚幻引擎 4 的资源管理系统将所有资源分为两类:主资源次资源。资源管理器通过主资源的 主资源 ID 即可直接对其进行操作,调用 GetPrimaryAssetId 即可获得此 ID。为将特定 UObject 类构成的资源指定为主资源,覆盖 GetPrimaryAssetId 即可返回一个有效的 FPrimaryAssetId 结构。次资源不由资源管理器直接处理,但其被主资源引用或使用后引擎便会自动进行加载。默认只有 UWorld 资源(关卡)为主资源;所有其他资源均为次资源。为将次资源设为主资源,必须覆盖其类的 GetPrimaryAssetId 函数,返回一个有效的 FPrimaryAssetId 结构。

资源管理器和可流送管理器

资源管理器 对象是一个单例,负责管理主资源的发现与加载。引擎中所包含的基础资源管理器类拥有基础的管理功能,但也可进行延展,满足项目特定的需求。可流送管理器 结构涵盖在资源管理器中,执行实际的异步对象加载,并使用 可流送句柄 将对象保存在内存中,直到不再需要后进行卸载。与单件的资源管理器不同,引擎不同区域中有多个可流送管理器,用途皆有所不同。

资源束

资源束(Asset Bundle) 是与主资源相关特定资源的命名列表。用“AssetBundles”元标签对 UObjectTAssetPtrFStringAssetReference 成员的 UPROPERTY 代码段进行标记即可创建资源束。标签的数值将显示保存次资源的束的命名。举例而言,以下保存在 MeshPtr 成员变量中的静态网格体资源在 UObject 被保存时将被添加到名为“TestBundle”的资源束。

/** 模型 */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = Display, AssetRegistrySearchable, meta = (AssetBundles = "TestBundle"))
TAssetPtr<UStaticMesh> MeshPtr;

使用资源束的第二种方式是用项目的资源管理器类在运行时将其注册。在此情况下,程序员需要编写代码填入 FAssetBudleData 结构,然后将结构传至资源管理器的 AddDynamicAsset 函数,并使主资源 ID 与束中的次资源关联起来。

从硬盘注册并加载主资源

多数主资源均在 Content Browser 中,并作为资源文件保存在硬盘上,以便美术师或设计师进行编辑。程序员创建可以此法使用的类的最简单方式是从 UPrimaryDataAsset 继承,它是 UDataAsset 的一个版本,拥有加载和保存内置资源束数据的功能。如需要不同的基类,如 APawnUPrimaryDataAsset 可用作一个最小范例,代表使资源束能够用于类的必备元素。以下类在 Fortnite 中保存区域主题数据,在游戏的地图选择模式中构建区域的演示时,这些数据能够告知游戏使用何种美术资源:

/** 用户能够从地图画面中进行选择的区域 */
UCLASS(Blueprintable)
class FORTNITEGAME_API UFortZoneTheme : public UPrimaryDataAsset
{
    GENERATED_UCLASS_BODY()

    /** 区域名称 */
    UPROPERTY(EditDefaultsOnly, Category=Zone)
    FText ZoneName;

    /** 进入此区域时将进行加载的地图 */
    UPROPERTY(EditDefaultsOnly, Category=Zone)
    TAssetPtr<UWorld> ZoneToUse;

    /** 用于在地图上展示此其余的蓝图类 */
    UPROPERTY(EditDefaultsOnly, Category=Visual, meta=(AssetBundles = "Menu"))
    TAssetSubclassOf<class AFortTheaterMapTile> TheaterMapTileClass;
};

此类继承自 UPrimaryDataAsset,因此其拥有 GetPrimaryAssetId 的可用版本,其使用资源的短命名和原生类。举例而言,以命名“Forest”保存的 UFortZoneTheme 的主材质 ID 名称为“FortZoneTheme:Forest”。UFortZoneTheme 资源保存在编辑器中时,PrimaryDataAssetAssetBundleData 成员将被更新,将其作为次资源包含。

注册并加载主资源需要以下操作:

  1. 如果项目拥有自定义资源管理器类,则需要让引擎知悉。完成此操作的方法是修改项目的 DefaultEngine.ini 文件,并设置 [/Script/Engine.Engine] 代码段下的 AssetManagerClassName 变量。最终的数值应为以下格式: [/Script/Engine.Engine] AssetManagerClassName=/Script/Module.UClassName 其中“Module”代表项目的模块名,“UClassName”则代表希望使用的 UClass 名。在 Fortnite 中,项目的模块名为“FortniteGame”,希望使用的类则名为 UFortAssetManager(意味着其 UClass 命名为 FortAssetManager),因此第二行应为: AssetManagerClassName=/Script/FortniteGame.FortAssetManager

    除非需要特殊功能,默认资源管理器类不需要被覆盖。可使用默认引擎类 UAssetManager,然后即可略过此步骤。

  2. 用资源管理器注册主资源。有三种方法可执行此操作:用 Project Settings 菜单进行配置、在 DefaultGame.ini 中手动添加资源路径的阵列进行扫描,或通过编程使资源管理器类在启动过程中执行。

    通过 Project Settings(在 Game / Asset Manager 部分下)进行配置如下图所示: ProjectSettingsAssetManager.png

    可对扫描主资源的路径进行配置。

    设置

    效果

    Primary Asset Types to Scan

    列出要寻找和注册的主资源类型,以及在何处进行寻找,并对其执行何种操作。

    Directories to Exclude

    不进行主资源显式扫描的目录。这可用于排除测试资源。

    Primary Asset Rules

    列出特定的规则覆盖(Rules Overrides),其将说明资源的处理方式。查看 烘焙和数据块划分 中的详细内容。

    Only Cook Production Assets

    如勾选此项,被指定为 DevelopmentCook 的资源在烘焙过程中将出现错误。可用于确保最终发布的版本中不含测试资源。

    Primary Asset ID Redirects

    资源管理器查找 ID 出现在列表中的主资源的数据时,其 ID 将被提供的其他 ID 所替换。

    Primary Asset Type Redirects

    资源管理器查找主资源的数据时,将使用列表中提供的类型名称,而非其原生类型。

    Primary Asset Name Redirects

    资源管理器查找主资源的数据时将使用列表中提供的资源名称,而非其原生名称。

    • 如需编辑 DefaultGame.ini,要找到(或创建)一个名为 /Script/Engine.AssetManagerSettings 的代码段并手动添加主资源类。格式如下:

      [/Script/Engine.AssetManagerSettings]
      !PrimaryAssetTypesToScan=ClearArray
      +PrimaryAssetTypesToScan=(PrimaryAssetType="Map",AssetBaseClass=/Script/Engine.World,bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game/Maps")),SpecificAssets=,Rules=(Priority=-1,bApplyRecursively=True,ChunkId=-1,CookRule=Unknown))
      +PrimaryAssetTypesToScan=(PrimaryAssetType="PrimaryAssetLabel",AssetBaseClass=/Script/Engine.PrimaryAssetLabel,bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game")),SpecificAssets=,Rules=(Priority=-1,bApplyRecursively=True,ChunkId=-1,CookRule=Unknown))
    • 如希望直接在代码中注册主资源,则覆盖资源管理器类中的 StartInitialLoading 函数并从该处调用 ScanPathsForPrimaryAssets。因此,推荐您将所有同类型的主资源放入相同的子文件夹中。这将使资源查找和注册更为迅速。

  3. 加载资源。资源管理器函数 LoadPrimaryAssetsLoadPrimaryAssetLoadPrimaryAssetsWithType 可用于在适当的时间开始加载主资源。之后资源可通过 UnloadPrimaryAssetsUnloadPrimaryAssetUnloadPrimaryAssetsWithType 进行卸载。使用这些加载函数时,可指定一个资源束列表。以此法进行加载将使资源管理器按以上描述的方式加载这些资源束应用的次资源。

注册并加载动态创建的主资源

主资源束也可在运行时动态注册和加载。有两个资源管理器函数可用于理解此操作:

  • ExtractStringAssetReferences 检查给定的 UScriptStruct 的全部 UPROPERTY 成员,并识别资源引用(然后这些引用将被保存在一个资源名阵列中)。此阵列可在创建资源束时使用。 ExtractStringAssetReferences 参数:

    参数

    目的

    Struct

    搜索资源引用的 UStruct。

    StructValue

    结构体的 void pointer

    FoundAssetReferences

    用于返回结构体中找到的资源引用的阵列。

    PropertiesToSkip

    返回阵列中所排除的属性名阵列。

  • RecursivelyExpandBundleData 将找到对主资源的全部引用,并递归扩展找到其全部资源束依赖性。因此,这意味着上方 ZoneTheme 所引用的 TheaterMapTileClass 将被添加到 AssetBundleData。 然后它将注册命名的动态资源并开始加载。 RecursivelyExpandBundleData 参数:

    参数

    目的

    BundleData

    包含资源引用的束数据。它们将被递归扩展,可用于加载一套相关资源。

举例而言,Fortnite 在其自定义资源管理器类中使用以下代码,基于游戏中下载的“剧院”数据构造和加载资源:

// 从剧院 ID 构建命名
UFortAssetManager& AssetManager = UFortAssetManager::Get();
FPrimaryAssetId TheaterAssetId = FPrimaryAssetId(UFortAssetManager::FortTheaterInfoType, FName(*TheaterData.UniqueId));

TArray<FStringAssetReference> AssetReferences;
AssetManager.ExtractStringAssetReferences(FFortTheaterMapData::StaticStruct(), &TheaterData, AssetReferences);

FAssetBundleData GameDataBundles;
GameDataBundles.AddBundleAssets(UFortAssetManager::LoadStateMenu, AssetReferences);

// 递归延展引用,获得区域中的图块蓝图
AssetManager.RecursivelyExpandBundleData(GameDataBundles);

// 注册动态资源 
AssetManager.AddDynamicAsset(TheaterAssetId, FStringAssetReference(), GameDataBundles);

// 开始预加载
AssetManager.LoadPrimaryAsset(TheaterAssetId, AssetManager.GetDefaultBundleState());
Select Skin
Light
Dark

欢迎来到全新虚幻引擎4文档站!

我们正在努力开发新功能,包括反馈系统,以便您能对我们的工作作出评价。但它目前还未正式上线。如果您对此页面有任何意见与在使用中遭遇任何问题,请前往文档反馈论坛告知我们。

新系统上线运行后,我们会及时通知您的。

发表反馈意见