数据注册表

使用数据注册表可以存储、合并、读取和管理不同来源的数据。

Choose your operating system:

Windows

macOS

Linux

选择实现方法:

Blueprints

C++

数据注册表(Data Registry) 是一种高效的全局储存空间,用于那些带有 USTRUCT 标签的数据结构。数据注册表支持同步和异步数据访问,以及用户定义的缓存行为。数据注册表主要用于常见的只读数据。

由于数据注册表包含在插件中,因此需要一些设置才能使用它。数据注册表快速入门详细介绍了设置流程,并介绍了一些基本概念。如果是基于特定会话的数据,例如情节进展或角色的当前状态,请使用引擎的"保存游戏(Save Game)"系统。

你可以将数据注册表设置成为从不同源头加载或生成数据,还可以通过资产扫描和手动注册来填充。数据注册表类似于 复合数据表(Composite Data Tables),但除了标准的表格数据行之外,还可以存储曲线数据。此外,它使用一个间接层,而不是手动将多个表格合并在一起。

数据源

数据注册表从两种数据源类型来采集数据项: 数据注册表源(Data Registry Sources)元数据注册表源(Meta Data Registry Sources)

这些数据源并非真正的数据项,数据注册表只是通过它们来查找或生成数据项。

数据源在数据注册表中的顺序很重要,如果在某个数据源中未找到特定的数据项,数据注册表会在列表后面的数据源中查找该数据项。这样可能会产生覆盖和回退行为,并使得特定于上下文的数据源覆盖通用的数据源。

数据注册表插件内置了数据注册表源和元数据注册表源,分别封装成 数据表(Data Tables)曲线表(Curve Tables)

数据注册表从两种数据源类型来采集数据项: 数据注册表源(Data Registry Sources)元数据注册表源(Meta Data Registry Sources)

这些数据源并非真正的数据项,数据注册表只是通过它们来查找或生成数据项。

数据源在数据注册表中的顺序很重要,如果在某个数据源中未找到特定的数据项,数据注册表会在列表后面的数据源中查找该数据项。这样可能会产生覆盖和回退行为,并使得特定于上下文的数据源覆盖通用的数据源。

数据注册表插件内置了数据注册表源和元数据注册表源,分别封装成 数据表(Data Tables)曲线表(Curve Tables)

数据注册表源

数据注册表直接拥有你在数据注册表资产内的数组中所创建并配置的数据注册表源对象。

这些对象表示的是与特定数据源有关的接口,数据注册表通过这些接口可以查找信息,例如单独的数据表或Web数据库。

C++开发人员可以创建数据注册表源子类,用于处理其他类型的数据或实现各类间接寻址规则,从而将

[标识符](GameplayFeatures/DataRegistries#Identifiers)
映射到数据项。如果你的项目具有一个或多个数据注册表源子类,那么在将新数据源添加到数据注册表资产时,这些子类将显示在列表中。

数据注册表直接拥有你在数据注册表资产内的数组中所创建并配置的数据注册表源对象。

这些对象表示的是与特定数据源有关的接口,数据注册表通过这些接口可以查找信息,例如单独的数据表或Web数据库。

如果你希望处理其他类型的数据,或实施不同的间接寻址规则以便将

[标识符](GameplayFeatures/DataRegistries#Identifiers)
映射到数据项,那么可以创建 UDataRegistrySource 的子类。

在将新数据源添加到数据注册表资产时,你创建的任何子类都将显示在下拉列表中。

元数据注册表源

元数据注册表源会在运行时创建并拥有其他数据源。

除了显式列出所有数据源,元数据注册表源还使用通用规则,例如扫描一组用户命名的路径,以找出包含数据项的资产。它们还可以监听特定资产的手动注册。

由于元数据注册表源是动态生成的,它们发现的数据源(和这些源中的数据项)将在运行时加载到数据注册表中。

与数据注册表源相似,C++开发人员可以创建元数据注册表源的子类,用于处理其他数据类型、创建不同的扫描规则,以及执行其他操作。

如果你的项目中包含一个或多个元数据注册表源的子类,当你将新的数据源添加到数据注册表资产时,这些子类将出现在下拉列表中。

元数据注册表源在运行时创建和拥有其他数据源。除了显式列出所有数据源,元数据注册表源还使用通用规则,例如扫描一组由用户命名的路径以找出包含数据项的资产。它们还可以侦听特定资产的手动注册。

由于元数据注册表源是动态生成的,它们发现的数据源(和这些源中的数据项)将在运行时加载到数据注册表中。与数据注册表源相似,你可以创建自己的子类来处理其他数据类型,创建不同的扫描规则,以及执行其他操作。要执行此操作,请重载 UMetaDataRegistrySource 类。在将新数据源添加到数据注册表资产时,你创建的任何子类都作为选项将显示在下拉列表中。

标识符

数据注册表插件使用独有的标识符来查找数据注册表以及其中包含的单个数据项。这些标识符本质上都是基于字符串的名称,不过 FDataRegistryType(用于数据注册表资产)和 FDataRegistryId(用于数据注册表中的单个数据项)结构却属于包装器(wrapper),能提供实用的编辑器内部功能。 FDataRegistryType 用于辨识数据注册表资产, FDataRegistryId 用于辨识数据注册表和其中的特定数据项。如果你需要查找数据注册表资产或从中检索单个数据项,可以使用这些标识符类型。

每个数据注册表资产都必须在 注册表类型(Registry Type) 字段中具有唯一名称. 如果两个数据注册表资产在此字段中具有相同的名称,系统将仅识别和填充其中一个。类似的,如果多个数据项共用同一个标识数值(名称或Gameplay标签),注册表会读取所有项,但检索操作将仅访问数据注册表资产加载的第一个数据项;关于数据项的加载顺序,请参见数据源部分。

使用C++的开发人员可以通过创建子数据注册表类和重载 ResolveDataRegistryId 函数来更改此行为。

数据注册表资产标识符

在设置数据注册表资产时,开发人员必须使用唯一命名来设置 注册表类型(Registry Type)。它是数据注册表的标识符。

在设置此值之后,编辑器中的所有"注册表类型"字段都会立即将新的数据注册表名称添加到其下拉列表中。

这样在引用其他资产中的数据注册表时可以防止拼写错误引发用户错误,而且可以让选择流程更快速简单。

在设置数据注册表资产时,开发人员必须将 注册表类型(Registry Type) 字段设置为唯一的名称数值。这是数据注册表的标识符。

在设置此数值之后,编辑器中的 FDataRegistryType 字段都会立即将新数据注册表名称添加到其下拉列表。

这样在引用其他资产中的数据注册表时可以防止拼写错误引发用户错误,而且可以让选择流程更快速简单。

DataRegistryAssetType.png

上图:设置数据注册表资产的标识符。

下图:在需要引用数据注册表资产的Actor中选择一个标识符,或者选择该数据注册表中的特定数据行。

DataRegistryAssetReferences.png

数据项标识符

识别单个数据项时(例如数据表中的数据行),需要指定数据注册表资产和数据项本身。

数据注册表ID包含这两个标识符。其 数据注册表类型(Data Registry Type) 字段将显示为下拉列表,你可以通过注册表的标识名称在其中选择所有已知的数据注册表。

从下拉列表中选择数据注册表的名称之后,数据注册表ID(Data Registry ID) 字段将变化以适应数据注册表。

如果数据注册表的ID格式使用Gameplay标签,用户界面将显示包含该Gameplay标签及其所有子项的筛选后列表。

如果数据注册表的ID格式未使用Gameplay标签,用户界面将显示一个下拉列表,其中包含数据注册表资产包含的所有已知数据行。

识别个体数据项时,例如数据表中的行,需要指定数据注册表资产和数据项本身。

FDataRegistryId 类型包含两个标识符。其 数据注册表类型(Data Registry Type) 字段将显示为下拉列表,你可以通过注册表的标识名称在其中选择所有已知的数据注册表。

在该下拉列表中选择数据注册表的名称之后,数据注册表ID(Data Registry ID) 字段将发生变化以适应数据注册表。

如果数据注册表的ID格式使用Gameplay标签,用户界面将显示包含该Gameplay标签及其所有子项的筛选后列表。

如果数据注册表的ID格式未使用Gameplay标签,用户界面将显示数据注册表资产包含的所有已知行的下拉列表。

如果编辑某个数据注册表资产,在通过数据项标识符引用该资产的其他资产中,你的更改可能不会立即生效。 如果发生这种情况,可能是因为数据项标识符在其下拉列表中包含废弃的数据行。 在引用数据注册表(而不是数据注册表资产自身)的资产中点击 编译(Compile) 按钮,可以使用最新的数据项信息更新界面。

DataItemIDS.png

左侧是数据注册表资产的数据项选项,且ID格式使用了Gameplay标签。

右侧是使用简单名称的数据注册表资产的数据项选项。

由于 FDataRegistryId 包含一个 FDataRegistryType 成员变量(名为 RegistryType),因此不需要单独的 FDataRegistryType 标识符就可以找到包含数据行的数据注册表资产。

标识符解析

系统通过搜索你提供的数据注册表ID的 项名称(Item Name) 来查找数据项。

你可以使用C++创建子数据注册表类,以此来改变此行为。如需更多详细信息,请切换到本页面的C++版本。

动态标识符解析

默认情况下,系统通过搜索你提供的 FDataRegistryIdItemName 字段的数值查找数据项。如果这不是你的项目的理想行为,你可以创建自己的 UDataRegistry 子类并重载 MapIdToResolvedName 函数,以在本地范围中包含额外的 FDataRegistryResolverScope 结构体。重载你的 FDataRegistryResolverScope 子类中的 ResolveIdToName 函数,可以重新映射传入的行名称,甚至可以使用动态或特定于玩家的信息。在解析ID之后,系统将产生一个(在系统中)保证唯一的 FDataRegistryLookup 并将其用作缓存唯一 ID。

快速函数参考

以下函数对于你快速掌握数据注册表很有帮助。此处并未列出完整信息,但列出了你在项目中设置数据注册表后,访问数据时需要用到的基本函数。

  • 获取数据注册表项(Acquire Data Registry Item) 将搜索给定项并在成功后将其加载到缓存。此函数对于来自

    [元数据注册表源](GameplayFeatures/DataRegistries#MetaDataRegistrySources)
    的数据项至关重要,因为其他数据注册表功能只能访问已缓存的数据项。如果你希望访问你认为存在(或可能存在)但当前不在缓存中的数据项,请调用此函数。在调用此函数时,将通过右侧的引脚立即恢复执行。返回数值将指明加载操作是否已经开始,而不是指明该操作的结果;失败意味着操作无法开始,将不会调用已连接的回调函数。一旦回调函数执行,搜索和加载操作已完成,你就可以使用此部分中介绍的其他函数来尝试从缓存中检索数据(假设数据已经成功加载)。如果你的数据此时未立即出现在缓存中,则表示未找到。

如果你成功检索到一次数据项,但稍后无法检索到,可能是因为它已经从缓存中删除。

重新调用 获取数据注册表项(Acquire Data Registry Item) 并在输入回调函数后立即检索。

根据项目的特定需求,你还可以考虑制作自己的数据副本,或调整数据项的缓存行为。

AcquireDRItem.png

  • 查找数据注册表项(Find Data Registry Item) 将会检查数据注册表缓存中的数据项,并根据是否找到数据项来判断下一步。如果找到,你可以通过右侧的 输出项(Out Item) 引脚访问它。此函数会立即返回,因此那些虽然存在但不在缓存中的数据项不会被找到。

FindDRItem.png

  • 获取数据注册表项(Get Data Registry Item) 类似于 查找数据注册表项(Find Data Registry Item),但会返回成功/失败数值而不是判断下一步,并在成功时自动填充连接左侧 输出项(Out Item) 引脚的变量。此函数将立即返回,因此那些虽然存在但不在缓存中的数据项不会被找到。

GetDRItem.png

  • 数据注册表曲线求值(Evaluate Data Registry Curve) 会查找已缓存的曲线。类似于 查找数据注册表项(Find Data Registry Item),该函数是同步的,因此会立即返回,但如果曲线在函数调用时不在缓存中,将无法找到曲线。此函数根据是否成功来执行下一步,并在失败时返回你提供的默认数值。假如无论曲线是否可用,你都需要输出数值,那么可以使用此功能。

EvaluateDRCurve.png

  • UDataRegistrySubsystem::Get - 返回指向 UDataRegistrySubsystem 实例的指针。列表中唯一的静态函数,此函数用作子系统的进入点。

  • UDataRegistrySubsystem::GetRegistryForType - 获取名称或 FDataRegistryType(独立或来自于 FDataRegistryId::RegistryType),并返回一个指向匹配的数据注册表(如果存在)的 UDataRegistry 指针。

  • UDataRegistrySubsystem::RegisterSpecificAsset - 按照 FDataRegistryType 搜索数据注册表,并向其添加特定的资产。如果未提供有效的 FDataRegistryType,子系统则会尝试将资产添加到子系统中的所有数据注册表。如果至少一个数据注册表接受了资产,则返回 true

  • UDataRegistry::GetCachedItem - 在数据注册表中搜索与你提供的 FDataRegistryId 对应的数据项。如果项在函数调用时不在缓存中,函数将返回 null。否则,函数将返回一个常量指针,指向数据注册表存储的任何一个结构体类型。

  • UDataRegistry::GetAllCachedItems - 对于数据注册表中的每个缓存项,使用引擎的 UScriptStruct 数据(作为 const uint8*)的指针来填充映射,使用项的 FDataRegistryId 作为键。此外,通过单独的输出参数提供 UScriptStruct 本身。这在针对作为调试工具的缓存进行迭代时非常有用,例如使用 UScriptStruct::ExportText 函数来记录每个缓存项的内容。

  • UDataRegistry::EvaluateCachedCurve - 查找与你提供的 FDataRegistryId 对应的曲线,并针对给定的输入数值对其进行求值。如果请求的曲线在调用时不在缓存中,函数将失败。此函数的返回数值类型为 FDataRegistryCacheGetResult,此类型的数值提供与你请求的曲线的缓存状态有关的信息,最重要的是确认是否找到了曲线。曲线的输出数值类型为 float,来自于输出参数。

  • UDataRegistry::AcquireItem:类似于 GetCachedItem,但即使数据项在调用时未缓存,仍然可以找到。此函数是异步的;它将在完成搜索时运行你的 FDataRegistryItemAcquiredCallback 类型的回调函数。函数的返回数值是 bool,用于指示在计划回调时是否成功;false 返回数值表示已经存在错误,你的回调函数不会运行。返回数值 true 表示你的回调函数将运行,但无法保证数据项存在。

缓存你从数据注册表中检索到的结构体指针可能会不安全。

虽然有些数据项始终可用,但有些数据项却是动态加载的,数据注册表可能在不发出警告的情况下卸载这些数据项。

如果你在处理有可能会被卸载的数据,建议你在检索到数据后立刻使用,或者缓存副本,而不要保留数据注册表中的指针。

与游戏功能集成

数据注册表插件可以添加来自游戏功能插件的数据注册表和单个数据注册表来源。如需相关流程的详细信息,请参见游戏功能和模块化Gameplay页面。

本文基于此前的虚幻引擎版本编写,未针对当前的虚幻引擎5.0版本更新过。