ue-data-assets-tables
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseUE Data Assets and Tables
UE Data Assets 与 DataTables
You are an expert in Unreal Engine's data management and asset loading systems.
您是Unreal Engine数据管理与资产加载系统方面的专家。
Context
背景信息
Read for project-specific data patterns, module layout, plugin dependencies, and any custom AssetManager subclass or DataAsset conventions the project has established.
.agents/ue-project-context.md请阅读了解项目特定的数据模式、模块布局、插件依赖,以及项目已确立的自定义AssetManager子类或DataAsset约定。
.agents/ue-project-context.mdInformation Gathering
信息收集
Before generating code or advice, ask:
- What kind of data is being stored? (item stats, level config, ability definitions, NPC data, etc.)
- Is this data authored by designers in spreadsheets, or configured directly in the editor?
- What are the loading requirements — always in memory, loaded per-level, streamed on demand?
- Is memory budget a concern? How many instances are expected?
- Does the project already use a custom subclass?
UAssetManager
在生成代码或提供建议前,请询问以下问题:
- 要存储的数据类型是什么?(物品属性、关卡配置、技能定义、NPC数据等)
- 这些数据是由设计师在电子表格中制作,还是直接在编辑器中配置?
- 加载需求是什么——始终驻留内存、按关卡加载、按需流加载?
- 内存预算是否需要关注?预计会有多少实例?
- 项目是否已使用自定义子类?
UAssetManager
Core Framework
核心框架
DataAssets vs DataTables — Choosing the Right Tool
DataAssets vs DataTables — 选择合适的工具
| Concern | DataAsset | DataTable |
|---|---|---|
| Structure | C++ class with typed UPROPERTY fields | Row struct, all rows same shape |
| Designer workflow | Editor-authored instances, picker UI | Spreadsheet import (CSV/JSON) |
| Hierarchy / inheritance | Yes, via Blueprint subclasses | No |
| Asset Manager integration | Yes ( | Not directly |
| Bulk lookup by row name | No | Yes ( |
| Best for | Per-item config objects | Large flat tables (loot, dialogue, XP curves) |
| 考量因素 | DataAsset | DataTable |
|---|---|---|
| 结构 | 带有类型化UPROPERTY字段的C++类 | 行结构体,所有行结构一致 |
| 设计师工作流 | 编辑器创建实例,带有选择器UI | 电子表格导入(CSV/JSON) |
| 层级/继承 | 支持,通过Blueprint子类实现 | 不支持 |
| Asset Manager集成 | 支持( | 不直接支持 |
| 按行名称批量查找 | 不支持 | 支持( |
| 最佳适用场景 | 单个物品的配置对象 | 大型扁平表(战利品、对话、经验曲线) |
DataAssets
DataAssets
UDataAsset — Simple Configuration Objects
UDataAsset — 简单配置对象
UDataAssetEngine/DataAsset.hcpp
UCLASS(BlueprintType)
class MYGAME_API UMyItemData : public UDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item") FText DisplayName;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item") float BaseDamage = 10.f;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item") TSoftObjectPtr<UStaticMesh> Mesh;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item") TSoftClassPtr<AActor> SpawnClass;
};In the editor: right-click in Content Browser > Miscellaneous > Data Asset, select .
UMyItemDataUDataAssetEngine/DataAsset.hcpp
UCLASS(BlueprintType)
class MYGAME_API UMyItemData : public UDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item") FText DisplayName;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item") float BaseDamage = 10.f;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item") TSoftObjectPtr<UStaticMesh> Mesh;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item") TSoftClassPtr<AActor> SpawnClass;
};在编辑器中操作:在内容浏览器中右键 > 杂项 > 数据资产,选择。
UMyItemDataUPrimaryDataAsset — Asset Manager Integration
UPrimaryDataAsset — Asset Manager集成
UPrimaryDataAssetGetPrimaryAssetId()cpp
// PrimaryAssetType == native class name; PrimaryAssetName == asset name.
UCLASS(BlueprintType)
class MYGAME_API UWeaponDefinition : public UPrimaryDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
FText WeaponName;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
float FireRate = 1.f;
// meta = (AssetBundles = "X") groups soft refs for selective AM loading.
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon",
meta = (AssetBundles = "UI"))
TSoftObjectPtr<UTexture2D> Icon;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon",
meta = (AssetBundles = "Game"))
TSoftObjectPtr<USkeletalMesh> WorldMesh;
};UPrimaryDataAssetGetPrimaryAssetId()cpp
// PrimaryAssetType == 原生类名称; PrimaryAssetName == 资产名称.
UCLASS(BlueprintType)
class MYGAME_API UWeaponDefinition : public UPrimaryDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
FText WeaponName;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
float FireRate = 1.f;
// meta = (AssetBundles = "X") 将软引用分组,以便AM选择性加载.
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon",
meta = (AssetBundles = "UI"))
TSoftObjectPtr<UTexture2D> Icon;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon",
meta = (AssetBundles = "Game"))
TSoftObjectPtr<USkeletalMesh> WorldMesh;
};DataTables
DataTables
Defining a Row Struct
定义行结构体
FTableRowBaseEngine/DataTable.hUSTRUCT(BlueprintType)cpp
// ItemTableRow.h
#pragma once
#include "Engine/DataTable.h"
#include "ItemTableRow.generated.h"
USTRUCT(BlueprintType)
struct FItemTableRow : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FText DisplayName;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MaxStack = 1;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Weight = 0.5f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSoftObjectPtr<UStaticMesh> PreviewMesh;
// Called after CSV/JSON import. Override for custom fixups.
virtual void OnPostDataImport(const UDataTable* InDataTable,
const FName InRowName,
TArray<FString>& OutCollectedImportProblems) override;
};In the editor: right-click > Miscellaneous > Data Table, assign as the row struct.
FItemTableRowFTableRowBaseEngine/DataTable.hUSTRUCT(BlueprintType)cpp
// ItemTableRow.h
#pragma once
#include "Engine/DataTable.h"
#include "ItemTableRow.generated.h"
USTRUCT(BlueprintType)
struct FItemTableRow : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FText DisplayName;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MaxStack = 1;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Weight = 0.5f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSoftObjectPtr<UStaticMesh> PreviewMesh;
// CSV/JSON导入后调用。重写以实现自定义修正。
virtual void OnPostDataImport(const UDataTable* InDataTable,
const FName InRowName,
TArray<FString>& OutCollectedImportProblems) override;
};在编辑器中操作:右键 > 杂项 > 数据表,将指定为行结构体。
FItemTableRowQuerying DataTables at Runtime
运行时查询DataTables
cpp
UPROPERTY(EditDefaultsOnly, Category = "Data")
TObjectPtr<UDataTable> ItemTable;
// FindRow<T>: returns nullptr if row not found or type mismatch.
const FItemTableRow* Row = ItemTable->FindRow<FItemTableRow>(
RowName, TEXT("LookupItem"));
// GetAllRows<T>: fills array with pointers to all rows.
TArray<FItemTableRow*> AllRows;
ItemTable->GetAllRows<FItemTableRow>(TEXT("GetAllItems"), AllRows);
// ForeachRow: iterate with row name keys.
ItemTable->ForeachRow<FItemTableRow>(
TEXT("ForeachRow"),
[](const FName& Key, const FItemTableRow& Value)
{
UE_LOG(LogTemp, Log, TEXT("Row %s: weight=%.2f"), *Key.ToString(), Value.Weight);
});cpp
UPROPERTY(EditDefaultsOnly, Category = "Data")
TObjectPtr<UDataTable> ItemTable;
// FindRow<T>: 如果未找到行或类型不匹配,返回nullptr.
const FItemTableRow* Row = ItemTable->FindRow<FItemTableRow>(
RowName, TEXT("LookupItem"));
// GetAllRows<T>: 将所有行的指针填充到数组中.
TArray<FItemTableRow*> AllRows;
ItemTable->GetAllRows<FItemTableRow>(TEXT("GetAllItems"), AllRows);
// ForeachRow: 按行名称键迭代.
ItemTable->ForeachRow<FItemTableRow>(
TEXT("ForeachRow"),
[](const FName& Key, const FItemTableRow& Value)
{
UE_LOG(LogTemp, Log, TEXT("Row %s: weight=%.2f"), *Key.ToString(), Value.Weight);
});Runtime Modification and Row Handles
运行时修改与行句柄
cpp
// AddRow/RemoveRow do not persist to disk.
FItemTableRow NewRow;
NewRow.DisplayName = FText::FromString(TEXT("Runtime Sword"));
ItemTable->AddRow(FName(TEXT("RuntimeSword")), NewRow);
ItemTable->RemoveRow(FName(TEXT("ObsoleteItem")));
// Import from CSV at runtime (RowStruct must be set beforehand).
TArray<FString> Problems = ItemTable->CreateTableFromCSVString(CsvContent);
// Import from JSON at runtime. JSON format uses "RowName" as key with struct fields as properties.
TArray<FString> JsonProblems = ItemTable->CreateTableFromJSONString(JsonContent);
// Export to CSV/JSON strings (WITH_EDITOR only — unavailable in cooked/shipping builds):
FString CsvOut = ItemTable->GetTableAsCSV();
FString JsonOut = ItemTable->GetTableAsJSON();
// FDataTableRowHandle: a UPROPERTY-friendly typed row reference.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config")
FDataTableRowHandle StartingWeaponHandle;
const FItemTableRow* Row = StartingWeaponHandle.GetRow<FItemTableRow>(
TEXT("StartingWeapon lookup"));cpp
// AddRow/RemoveRow不会持久化到磁盘.
FItemTableRow NewRow;
NewRow.DisplayName = FText::FromString(TEXT("Runtime Sword"));
ItemTable->AddRow(FName(TEXT("RuntimeSword")), NewRow);
ItemTable->RemoveRow(FName(TEXT("ObsoleteItem")));
// 运行时从CSV导入(必须预先设置RowStruct).
TArray<FString> Problems = ItemTable->CreateTableFromCSVString(CsvContent);
// 运行时从JSON导入。JSON格式使用"RowName"作为键,结构体字段作为属性.
TArray<FString> JsonProblems = ItemTable->CreateTableFromJSONString(JsonContent);
// 导出为CSV/JSON字符串(仅在WITH_EDITOR模式下可用——在打包/发布版本中不可用):
FString CsvOut = ItemTable->GetTableAsCSV();
FString JsonOut = ItemTable->GetTableAsJSON();
// FDataTableRowHandle: 支持UPROPERTY的类型化行引用.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config")
FDataTableRowHandle StartingWeaponHandle;
const FItemTableRow* Row = StartingWeaponHandle.GetRow<FItemTableRow>(
TEXT("StartingWeapon lookup"));Asset References
资产引用
Hard References
硬引用
cpp
// Hard ref: loaded when the referencing asset loads. Causes the mesh/material
// to be in memory as long as this object is alive.
UPROPERTY(EditDefaultsOnly, Category = "Art")
TObjectPtr<UStaticMesh> Mesh; // UE5 TObjectPtr preferred over raw ptrUse hard references only for assets that are always needed while this object exists (e.g., a character's skeleton).
cpp
// 硬引用:当引用资产加载时,该资产也会被加载。只要此对象存在,网格/材质就会驻留在内存中.
UPROPERTY(EditDefaultsOnly, Category = "Art")
TObjectPtr<UStaticMesh> Mesh; // UE5中优先使用TObjectPtr而非原始指针仅当对象存在期间始终需要该资产时才使用硬引用(例如角色的骨骼)。
Soft References
软引用
Soft references store a path string. The asset is NOT loaded until explicitly resolved.
cpp
// TSoftObjectPtr<T>: soft ref to an asset instance.
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Art")
TSoftObjectPtr<UStaticMesh> MeshSoft;
// TSoftClassPtr<T>: soft ref to a class (blueprint subclasses especially).
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Spawning")
TSoftClassPtr<AActor> SpawnableSoft;
// FSoftObjectPath: untyped path, useful for generic systems.
FSoftObjectPath MeshPath = MeshSoft.ToSoftObjectPath();软引用存储路径字符串。资产不会被加载,直到显式解析。
cpp
// TSoftObjectPtr<T>: 指向资产实例的软引用.
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Art")
TSoftObjectPtr<UStaticMesh> MeshSoft;
// TSoftClassPtr<T>: 指向类的软引用(尤其适用于Blueprint子类).
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Spawning")
TSoftClassPtr<AActor> SpawnableSoft;
// FSoftObjectPath: 无类型路径,适用于通用系统.
FSoftObjectPath MeshPath = MeshSoft.ToSoftObjectPath();Synchronous Resolution (avoid on game thread for large assets)
同步解析(大型资产避免在游戏线程使用)
cpp
UStaticMesh* Mesh = MeshSoft.LoadSynchronous(); // Blocks until loaded.
// FSoftObjectPath::TryLoad — returns nullptr if not on disk, does not assert.
UObject* Loaded = MeshPath.TryLoad();cpp
UStaticMesh* Mesh = MeshSoft.LoadSynchronous(); // 阻塞直到加载完成.
// FSoftObjectPath::TryLoad — 如果资产不在磁盘上返回nullptr,不会触发断言.
UObject* Loaded = MeshPath.TryLoad();Checking State Without Loading
不加载资产的状态检查
cpp
if (MeshSoft.IsNull()) { /* no path set */ }
if (MeshSoft.IsValid()) { /* path set AND asset is loaded in memory */ }
if (MeshSoft.IsPending()) { /* path set, async load started, not complete */ }
UStaticMesh* MeshPtr = MeshSoft.Get(); // Returns nullptr if not loaded.cpp
if (MeshSoft.IsNull()) { /* 未设置路径 */ }
if (MeshSoft.IsValid()) { /* 已设置路径且资产已加载到内存 */ }
if (MeshSoft.IsPending()) { /* 已设置路径,异步加载已启动但未完成 */ }
UStaticMesh* MeshPtr = MeshSoft.Get(); // 如果未加载返回nullptr.Async Loading
异步加载
FStreamableManager
FStreamableManager
FStreamableManagerEngine/StreamableManager.hUAssetManager::GetStreamableManager()cpp
FStreamableManager& SM = UAssetManager::GetStreamableManager();FStreamableManagerEngine/StreamableManager.hUAssetManager::GetStreamableManager()cpp
FStreamableManager& SM = UAssetManager::GetStreamableManager();RequestAsyncLoad — Single Asset
RequestAsyncLoad — 单个资产
cpp
void AMyActor::LoadWeaponMeshAsync()
{
FStreamableManager& SM = UAssetManager::GetStreamableManager();
StreamableHandle = SM.RequestAsyncLoad(
WeaponMeshSoft.ToSoftObjectPath(),
FStreamableDelegate::CreateUObject(this, &AMyActor::OnWeaponMeshLoaded));
}
void AMyActor::OnWeaponMeshLoaded()
{
if (StreamableHandle.IsValid() && StreamableHandle->HasLoadCompleted())
{
UStaticMesh* Mesh = WeaponMeshSoft.Get();
if (Mesh)
{
MeshComponent->SetStaticMesh(Mesh);
}
}
}
// Member:
TSharedPtr<FStreamableHandle> StreamableHandle;cpp
void AMyActor::LoadWeaponMeshAsync()
{
FStreamableManager& SM = UAssetManager::GetStreamableManager();
StreamableHandle = SM.RequestAsyncLoad(
WeaponMeshSoft.ToSoftObjectPath(),
FStreamableDelegate::CreateUObject(this, &AMyActor::OnWeaponMeshLoaded));
}
void AMyActor::OnWeaponMeshLoaded()
{
if (StreamableHandle.IsValid() && StreamableHandle->HasLoadCompleted())
{
UStaticMesh* Mesh = WeaponMeshSoft.Get();
if (Mesh)
{
MeshComponent->SetStaticMesh(Mesh);
}
}
}
// 成员变量:
TSharedPtr<FStreamableHandle> StreamableHandle;RequestAsyncLoad — Multiple Assets
RequestAsyncLoad — 多个资产
cpp
// All paths fire one callback when every asset is loaded.
TArray<FSoftObjectPath> PathsToLoad = {
IconSoft.ToSoftObjectPath(), MeshSoft.ToSoftObjectPath() };
StreamableHandle = SM.RequestAsyncLoad(
PathsToLoad,
FStreamableDelegate::CreateLambda([this]()
{
// Both guaranteed loaded; IconSoft.Get() and MeshSoft.Get() are valid.
}));cpp
// 所有路径加载完成后触发一次回调.
TArray<FSoftObjectPath> PathsToLoad = {
IconSoft.ToSoftObjectPath(), MeshSoft.ToSoftObjectPath() };
StreamableHandle = SM.RequestAsyncLoad(
PathsToLoad,
FStreamableDelegate::CreateLambda([this]()
{
// 两个资产均已加载完成; IconSoft.Get()和MeshSoft.Get()有效.
}));FStreamableHandle State and Control
FStreamableHandle状态与控制
cpp
Handle->HasLoadCompleted(); // true when all assets finished.
Handle->IsLoadingInProgress();// true while still loading.
Handle->WasCanceled(); // true if CancelHandle() was called.
Handle->GetLoadProgress(); // 0.0 to 1.0.
Handle->WaitUntilComplete(); // blocks game thread — use only on loading screens.
Handle->ReleaseHandle(); // allow GC of loaded assets.Priority: pass higher values to for urgent loads (default is 0).
Use (100) from for gameplay-critical assets.
RequestAsyncLoadAsyncLoadHighPriorityStreamableManager.hFStreamableManagerRequestSyncLoad()cpp
Handle->HasLoadCompleted(); // 所有资产加载完成时返回true.
Handle->IsLoadingInProgress();// 加载进行中时返回true.
Handle->WasCanceled(); // 如果调用了CancelHandle()返回true.
Handle->GetLoadProgress(); // 返回0.0到1.0的加载进度.
Handle->WaitUntilComplete(); // 阻塞游戏线程——仅在加载界面使用.
Handle->ReleaseHandle(); // 允许已加载资产被GC回收.优先级:为紧急加载向传递更高的值(默认值为0)。对于游戏玩法关键资产,使用中的(值为100)。
RequestAsyncLoadStreamableManager.hAsyncLoadHighPriorityFStreamableManagerRequestSyncLoad()Asset Manager
Asset Manager
Setup — DefaultGame.ini
设置 — DefaultGame.ini
ini
[/Script/Engine.AssetManagerSettings]
+PrimaryAssetTypesToScan=(PrimaryAssetType="WeaponDefinition",
AssetBaseClass=/Script/MyGame.WeaponDefinition,
bHasBlueprintClasses=False,
bIsEditorOnly=False,
Directories=((Path="/Game/Data/Weapons")),
Rules=(Priority=1,bApplyRecursively=True))ini
[/Script/Engine.AssetManagerSettings]
+PrimaryAssetTypesToScan=(PrimaryAssetType="WeaponDefinition",
AssetBaseClass=/Script/MyGame.WeaponDefinition,
bHasBlueprintClasses=False,
bIsEditorOnly=False,
Directories=((Path="/Game/Data/Weapons")),
Rules=(Priority=1,bApplyRecursively=True))Custom AssetManager Subclass
自定义AssetManager子类
Subclass , override for startup logic, register in :
UAssetManagerStartInitialLoading()DefaultEngine.iniini
[/Script/Engine.Engine]
AssetManagerClassName=/Script/MyGame.UMyAssetManager继承,重写以实现启动逻辑,并在中注册:
UAssetManagerStartInitialLoading()DefaultEngine.iniini
[/Script/Engine.Engine]
AssetManagerClassName=/Script/MyGame.UMyAssetManagerLoading Primary Assets
加载主资产
cpp
UAssetManager& AM = UAssetManager::Get();
// List all registered IDs of a type.
TArray<FPrimaryAssetId> WeaponIds;
AM.GetPrimaryAssetIdList(FPrimaryAssetType(TEXT("WeaponDefinition")), WeaponIds);
// Load a single primary asset asynchronously.
FPrimaryAssetId WeaponId(TEXT("WeaponDefinition"), TEXT("DA_Sword"));
TSharedPtr<FStreamableHandle> Handle = AM.LoadPrimaryAsset(
WeaponId,
TArray<FName>{ TEXT("Game") }, // load "Game" bundle (world mesh, etc.)
FStreamableDelegate::CreateLambda([WeaponId]()
{
UWeaponDefinition* Def = UAssetManager::Get()
.GetPrimaryAssetObject<UWeaponDefinition>(WeaponId);
// Use Def...
}));
// Load all assets of a type.
TSharedPtr<FStreamableHandle> AllHandle = AM.LoadPrimaryAssetsWithType(
FPrimaryAssetType(TEXT("WeaponDefinition")),
TArray<FName>{ TEXT("UI") }); // e.g., load only icons.
// Unload when no longer needed.
AM.UnloadPrimaryAsset(WeaponId);cpp
UAssetManager& AM = UAssetManager::Get();
// 获取某一类型的所有已注册ID.
TArray<FPrimaryAssetId> WeaponIds;
AM.GetPrimaryAssetIdList(FPrimaryAssetType(TEXT("WeaponDefinition")), WeaponIds);
// 异步加载单个主资产.
FPrimaryAssetId WeaponId(TEXT("WeaponDefinition"), TEXT("DA_Sword"));
TSharedPtr<FStreamableHandle> Handle = AM.LoadPrimaryAsset(
WeaponId,
TArray<FName>{ TEXT("Game") }, // 加载"Game"包(世界网格等)
FStreamableDelegate::CreateLambda([WeaponId]()
{
UWeaponDefinition* Def = UAssetManager::Get()
.GetPrimaryAssetObject<UWeaponDefinition>(WeaponId);
// 使用Def...
}));
// 加载某一类型的所有资产.
TSharedPtr<FStreamableHandle> AllHandle = AM.LoadPrimaryAssetsWithType(
FPrimaryAssetType(TEXT("WeaponDefinition")),
TArray<FName>{ TEXT("UI") }); // 例如,仅加载图标.
// 不再需要时卸载.
AM.UnloadPrimaryAsset(WeaponId);Asset Bundles
资产包
Bundles group soft references for selective loading. Decorate UPROPERTY fields with . The Asset Manager will load only the requested bundle's assets.
meta = (AssetBundles = "BundleName")cpp
// "UI" bundle: loaded in menus for icon display.
UPROPERTY(EditDefaultsOnly, meta = (AssetBundles = "UI"))
TSoftObjectPtr<UTexture2D> Icon;
// "Game" bundle: loaded when entering gameplay.
UPROPERTY(EditDefaultsOnly, meta = (AssetBundles = "Game"))
TSoftObjectPtr<USkeletalMesh> WorldMesh;
// Transition from UI to Game bundle:
AM.ChangeBundleStateForPrimaryAssets(
{ WeaponId },
{ TEXT("Game") }, // AddBundles
{ TEXT("UI") }); // RemoveBundles资产包将软引用分组以实现选择性加载。使用修饰UPROPERTY字段。Asset Manager将仅加载请求包中的资产。
meta = (AssetBundles = "BundleName")cpp
// "UI"包:在菜单中加载以显示图标.
UPROPERTY(EditDefaultsOnly, meta = (AssetBundles = "UI"))
TSoftObjectPtr<UTexture2D> Icon;
// "Game"包:进入游戏玩法时加载.
UPROPERTY(EditDefaultsOnly, meta = (AssetBundles = "Game"))
TSoftObjectPtr<USkeletalMesh> WorldMesh;
// 从UI包切换到Game包:
AM.ChangeBundleStateForPrimaryAssets(
{ WeaponId },
{ TEXT("Game") }, // 添加包
{ TEXT("UI") }); // 移除包Asset Registry
资产注册表
IAssetRegistryIAssetRegistry::GetChecked()FAssetRegistryModule::GetRegistry()cpp
#include "AssetRegistry/AssetRegistryModule.h"
#include "AssetRegistry/IAssetRegistry.h"
IAssetRegistry& AR = IAssetRegistry::GetChecked();
// Get all assets of a class in a path.
TArray<FAssetData> AssetDataList;
AR.GetAssetsByPath(FName(TEXT("/Game/Data/Weapons")), AssetDataList, /*bRecursive=*/true);
// Get all assets of a specific class.
AR.GetAssetsByClass(
FTopLevelAssetPath(TEXT("/Script/MyGame"), TEXT("WeaponDefinition")),
AssetDataList,
/*bSearchSubClasses=*/true);
// FAssetData is lightweight — no asset load occurs.
for (const FAssetData& Data : AssetDataList)
{
FString AssetName = Data.AssetName.ToString();
FSoftObjectPath Path = Data.GetSoftObjectPath();
// Read asset registry tags without loading.
FString TagValue;
Data.GetTagValue(FName(TEXT("WeaponType")), TagValue);
}
// Query by tag values.
TMultiMap<FName, FString> TagFilter;
TagFilter.Add(TEXT("WeaponType"), TEXT("Melee"));
AR.GetAssetsByTagValues(TagFilter, AssetDataList);IAssetRegistryIAssetRegistry::GetChecked()FAssetRegistryModule::GetRegistry()cpp
#include "AssetRegistry/AssetRegistryModule.h"
#include "AssetRegistry/IAssetRegistry.h"
IAssetRegistry& AR = IAssetRegistry::GetChecked();
// 获取某一路径下某一类别的所有资产.
TArray<FAssetData> AssetDataList;
AR.GetAssetsByPath(FName(TEXT("/Game/Data/Weapons")), AssetDataList, /*bRecursive=*/true);
// 获取某一特定类别的所有资产.
AR.GetAssetsByClass(
FTopLevelAssetPath(TEXT("/Script/MyGame"), TEXT("WeaponDefinition")),
AssetDataList,
/*bSearchSubClasses=*/true);
// FAssetData是轻量级的——不会加载资产.
for (const FAssetData& Data : AssetDataList)
{
FString AssetName = Data.AssetName.ToString();
FSoftObjectPath Path = Data.GetSoftObjectPath();
// 无需加载即可读取资产注册表标签.
FString TagValue;
Data.GetTagValue(FName(TEXT("WeaponType")), TagValue);
}
// 按标签值查询.
TMultiMap<FName, FString> TagFilter;
TagFilter.Add(TEXT("WeaponType"), TEXT("Melee"));
AR.GetAssetsByTagValues(TagFilter, AssetDataList);Making Properties Searchable
使属性可搜索
cpp
UPROPERTY(EditDefaultsOnly, AssetRegistrySearchable)
FName WeaponType;cpp
UPROPERTY(EditDefaultsOnly, AssetRegistrySearchable)
FName WeaponType;Common Mistakes and Anti-Patterns
常见错误与反模式
Hard Referencing Everything
所有内容都使用硬引用
cpp
// BAD: This UPROPERTY loads ALL 50 particle effects when this data asset loads.
UPROPERTY(EditDefaultsOnly)
TObjectPtr<UParticleSystem> HitEffect;
// GOOD: Soft reference — only load when the gameplay effect actually triggers.
UPROPERTY(EditDefaultsOnly)
TSoftObjectPtr<UParticleSystem> HitEffect;cpp
// 错误:此UPROPERTY会在加载该数据资产时加载所有50个粒子特效.
UPROPERTY(EditDefaultsOnly)
TObjectPtr<UParticleSystem> HitEffect;
// 正确:软引用——仅在游戏玩法特效实际触发时加载.
UPROPERTY(EditDefaultsOnly)
TSoftObjectPtr<UParticleSystem> HitEffect;Loading on the Game Thread
在游戏线程加载资产
cpp
// BAD: LoadSynchronous on a large skeletal mesh stalls the render thread.
USkeletalMesh* Mesh = MeshSoft.LoadSynchronous();
// GOOD: Async load, apply result in callback.
UAssetManager::GetStreamableManager().RequestAsyncLoad(
MeshSoft.ToSoftObjectPath(),
FStreamableDelegate::CreateUObject(this, &AMyActor::OnMeshLoaded));cpp
// 错误:在游戏线程使用LoadSynchronous加载大型骨骼网格会阻塞渲染线程.
USkeletalMesh* Mesh = MeshSoft.LoadSynchronous();
// 正确:异步加载,在回调中应用结果.
UAssetManager::GetStreamableManager().RequestAsyncLoad(
MeshSoft.ToSoftObjectPath(),
FStreamableDelegate::CreateUObject(this, &AMyActor::OnMeshLoaded));Forgetting to Keep the Handle Alive
忘记保持句柄存活
cpp
// BAD: Handle is a local — destroyed when function returns, assets may be unloaded.
void LoadStuff()
{
TSharedPtr<FStreamableHandle> Handle = SM.RequestAsyncLoad(...);
} // Handle destroyed here!
// GOOD: Store handle as a member until assets are no longer needed.
TSharedPtr<FStreamableHandle> LoadHandle; // member variablecpp
// 错误:句柄是局部变量——函数返回时被销毁,资产可能被卸载.
void LoadStuff()
{
TSharedPtr<FStreamableHandle> Handle = SM.RequestAsyncLoad(...);
} // 此处句柄被销毁!
// 正确:将句柄存储为成员变量,直到资产不再需要.
TSharedPtr<FStreamableHandle> LoadHandle; // 成员变量Not Registering Primary Asset Types
未注册主资产类型
If a subclass is not listed under in , returns nothing and silently fails.
UPrimaryDataAssetPrimaryAssetTypesToScanDefaultGame.iniGetPrimaryAssetIdListLoadPrimaryAsset如果子类未在的中列出,将返回空,会静默失败。
UPrimaryDataAssetDefaultGame.iniPrimaryAssetTypesToScanGetPrimaryAssetIdListLoadPrimaryAssetCircular Soft Reference Resolution
循环软引用解析
Resolving a soft reference that itself holds soft references that point back creates load cycles. Use Asset Bundles to break cycles — load only the bundle that is needed for the current state.
解析一个自身包含指向回环的软引用的资产会导致加载循环。使用资产包打破循环——仅加载当前状态所需的包。
DataTable Column Changes with Existing Data
修改DataTable列时保留现有数据
Adding a column to an existing DataTable struct invalidates serialized row data unless is set on the table or you re-import. Removing a column causes all existing rows to lose that data. Always back up DataTable assets before struct changes in production.
bPreserveExistingValues向现有DataTable结构体添加列会使序列化的行数据失效,除非在表上设置或重新导入。删除列会导致所有现有行丢失该列的数据。生产环境中修改结构体前请务必备份DataTable资产。
bPreserveExistingValuesEdge Cases
边缘情况
- Cooked vs uncooked paths: In uncooked builds, paths use . Cooked paths differ — do not hard-code paths; use
/Game/from UPROPERTY references.FSoftObjectPath - Asset Manager and cook: Assets not reachable through Primary Asset scanning rules or hard references will be excluded from the cook. Use or explicit asset labels to ensure inclusion.
PrimaryAssetRules - Hot reload: changes during PIE (Play In Editor) may not reflect until the asset is fully reloaded. Use
UDataAssetorPostLoadfor editor-time updates.PostEditChangeProperty - Memory budgets: Track loaded assets by type via . Use
UAssetManager::GetPrimaryAssetObjectListto swap between UI and Game bundles as scenes change.ChangeBundleStateForPrimaryAssets - : Set this on
bStripFromClientBuildsinstances that contain server-only data (e.g., loot tables with drop rates) to prevent distribution to clients.UDataTable
- 打包与未打包路径:在未打包构建中,路径使用。打包后的路径不同——不要硬编码路径;使用来自UPROPERTY引用的
/Game/。FSoftObjectPath - Asset Manager与打包:无法通过主资产扫描规则或硬引用访问的资产将被排除在打包之外。使用或显式资产标签确保资产被包含。
PrimaryAssetRules - 热重载:PIE(编辑器内运行)期间修改可能不会立即生效,直到资产完全重新加载。使用
UDataAsset或PostLoad实现编辑器时更新。PostEditChangeProperty - 内存预算:通过按类型跟踪已加载资产。使用
UAssetManager::GetPrimaryAssetObjectList在场景切换时在UI和Game包之间切换。ChangeBundleStateForPrimaryAssets - :在包含服务器专属数据(例如带有掉落率的战利品表)的
bStripFromClientBuilds实例上设置此属性,以避免分发给客户端。UDataTable
Related Skills
相关技能
- — UPROPERTY specifiers, USTRUCT, UObject lifecycle
ue-cpp-foundations - — saving and loading soft object references across sessions
ue-serialization-savegames - — adding
ue-module-build-system,AssetRegistrymodule dependencies to Build.csEngine
- — UPROPERTY说明符、USTRUCT、UObject生命周期
ue-cpp-foundations - — 跨会话保存和加载软对象引用
ue-serialization-savegames - — 在Build.cs中添加
ue-module-build-system、AssetRegistry模块依赖Engine