ue-data-assets-tables
Use when working with DataAsset, DataTable, soft reference, hard reference, TSoftObjectPtr, async loading, Asset Manager, StreamableManager, or game data structures in Unreal Engine. See references/asset-loading-patterns.md for async loading and StreamableManager patterns. See references/data-driven
By quodsoler · 845 installs
npx skills add quodsoler/unreal-engine-skills --skill ue-data-assets-tables
Source repository · Upstream listing
UE Data Assets and Tables
Target engine: UE 5.8 . APIs below are verified against the 5.8 headers; older forms are listed under "Deprecated — do not use".
This skill covers designer authored game data and the loading paths that bring it into memory: UDataAsset / UPrimaryDataAsset , UDataTable / UCurveTable , soft references, FStreamableManager , UAssetManager primary assets and cook rules, IAssetRegistry queries, the DataRegistry plugin and UDeveloperSettings . Build.cs modules: Engine and CoreUObject for data assets, tables and soft pointers; AssetRegistry for registry queries; DeveloperSettings for settings classes; DataRegistry for data registries. Deep dives live in [asset loading patterns](references/asset loading patterns.md) and [data driven design patterns](references/data driven design.md).
Context
Read .agents/ue project context.md if it exists (module names, conventions, enabled plugins, custom UAssetManager subclass, registered primary asset types). Do not stop if it is missing.
Identify the area from the request and the codebase. Ask only when two plausible readings would produce different code.
Request is about… Go to
Picking a container for game data [Choosing a Data Container]( choosing a data container)
UDataAsset , UPrimaryDataAsset , asset bundles [Data Assets]( data assets)
Row structs, CSV/JSON import, FindRow [Data Tables]( data tables)
Curves, table composition [Curve Tables and Composite Tables]( curve tables and composite tables)
TSoftObjectPtr , FSoftObjectPath , memory budget [References: Hard vs Soft]( references hard vs soft)
Async loading, handles, priorities [Async Loading]( async loading)
FPrimaryAssetId , scanning, bundle states [Asset Manager]( asset manager)
Cooking, chunking, PrimaryAssetTypesToScan [Cook Rules]( cook rules)
Finding assets without loading them [Asset Registry Queries]( asset registry queries)
Sharing rows across sources at runtime [Data Registry]( data registry)
Project wide tunables in Project Settings [Project Settings]( project settings)
Choosing a Data Container
Need Use Header
Per object config, editor authored, one asset per entry UDataAsset Engine/DataAsset.h
Same, plus load/unload by id and cook rules UPrimaryDataAsset Engine/DataAsset.h
Flat rows of identical shape, spreadsheet authored UDataTable + FTableRowBase Engine/DataTable.h
Rows merged from several tables (DLC, overrides) UCompositeDataTable Engine/CompositeDataTable.h
Float curves keyed by row name UCurveTable , FCurveTableRowHandle Engine/CurveTable.h
One row id resolved across many sources at runtime UDataRegistrySubsystem DataRegistrySubsystem.h
Project wide tunables in Project Settings UDeveloperSettings Engine/DeveloperSettings.h
UDataAsset supports Blueprint subclasses and inheritance; UDataTable rows do not. UDataTable gives lookup by row name out of the box; data assets need an Asset Manager scan or an Asset Registry query to be discoverable.
Data Assets
UDataAsset is a plain UObject you can instantiate from the Content Browser. UPrimaryDataAsset adds GetPrimaryAssetId() so UAssetManager can scan, load and unload it by id.
GetPrimaryAssetId() rules ( Engine/DataAsset.h:36 53 ):
The default returns type = name of the first native class going up the hierarchy (or the highest level Blueprint class), name = the asset FName.
Blueprint subclasses should be authored as Data Only Blueprints, not Data Asset instances, so parent class edits propagate.
Override only when you need a different type/name scheme; inside the class body the declaration is virtual FPrimaryAssetId GetPrimaryAssetId() const override; .
UpdateAssetBundleData() scans meta=(AssetBundles="Name") on the class and fills the AssetBundleData UPROPERTY during PreSave . Bundle names are free form; meta=(AssetBundles="Client,Server") puts one property in two bundles.
Plain UDataAsset has no primary asset id and loads only through whoever references it.
Data Tables
Every row struct derives FTableRowBase ( Engine/DataTable.h:33 ) and uses GENERATED BODY() .
FTableRowBase::IsDataValid(FDataValidationContext& Context) const is the third hook and is wrapped in if WITH EDITOR ( Engine/DataTable.h:60 68 ); guard any override the same way and include Misc/DataValidation.h .
Lookup
FDataTableRowHandle is the UPROPERTY friendly reference (table plus row name) designers pick in the editor:
Runtime mutation and import
GetTableAsCSV() , GetTableAsJSON() and GetTableAsString() sit inside if WITH EDITOR ( Engine/DataTable.h:318 345 ) — they do not exist in a cooked build. bStripFromClientBuilds on the table asset makes NeedsLoadForClient() return false, so server only tables never ship to clients ( Engine/DataTable.h:117,149 ).
Blueprint access
UDataTableFunctionLibrary ( Kismet/DataTableFunctionLibrary.h ) exposes tables to Blueprint: GetDataTableRowFromName , DoesDataTableRowExist , GetDataTableRowNames , GetDataTableColumnNames , GetDataTableColumnAsString , GetDataTableRowStruct , EvaluateCurveTableRow , GetCurveTableRowNames , plus the editor scripting FillDataTableFromCSVString / FillDataTableFromCSVFile .
Curve Tables and Composite Tables
FCurveTableRowHandle also offers GetRichCurve() , GetSimpleCurve() , IsNull() and Eval(float XValue, float YValue, const FString& ContextString) , which reports whether the lookup succeeded.
UCompositeDataTable merges its ParentTables array into one row map; later tables override earlier ones by row name. Use it for base game plus DLC or per platform overrides. AppendParentTables() , RemoveParentTables() and GetParentTables() manage the stack; AddRow and RemoveRow are overridden as no ops ( Engine/CompositeDataTable.h:43 47 , CompositeDataTable.cpp:191 204 ), so edit the parent tables instead.
References: Hard vs Soft
Form Loads target when owner loads Use for
TObjectPtr<UStaticMesh Yes Always needed, small, or already resident assets
TSubclassOf<AActor Yes (the class) Classes spawned constantly
TSoftObjectPtr<UStaticMesh No Art, audio, VFX referenced by data
TSoftClassPtr<AActor No Blueprint classes chosen by data
FSoftObjectPath No Untyped paths in generic systems
A hard reference inside a UPrimaryDataAsset pulls its whole dependency chain into memory as soon as the definition loads — the single biggest cause of runaway memory in data driven projects. Default to soft references for content, hard references for values and small structs.
Async Loading
FStreamableManager ( Engine/StreamableManager.h ) is owned by the Asset Manager; reach it with UAssetManager::GetStreamableManager() and never construct your own in game code.
RequestAsyncLoad has two forms. The classic form takes the paths, a delegate, then TAsyncLoadPriority Priority , bool bManageActiveHandle , bool bStartStalled , FString DebugName . The params form takes a single FStreamableAsyncLoadParams&& .
Params form, when you need the cancel/update delegates or JIT trickling:
RequestSyncLoad(TargetsToStream, bManageActiveHandle = false, DebugName = FString()) blocks until done — loading screens and one time init only.
FStreamableHandle member Meaning
IsActive() Request exists and has not been released or cancelled
HasLoadCompleted() Every target finished (the delegate may not have run yet)
IsLoadingInProgress() Still streaming
WasCanceled() / HasError() Cancelled by you / one or more targets failed
GetLoadProgress() 0.0 to 1.0
GetLoadedAsset() / GetLoadedAsset<T () First target; GetLoadedAssets(TArray<T &) for all
WaitUntilComplete(float Timeout = 0.f, bool bStartStalledHandles = true) Blocks the caller, returns EAsyncPackageState::Type
BindCompleteDelegate / BindCancelDelegate / BindUpdateDelegate Attach callbacks after the request was made
CancelHandle() Stops the load; the complete delegate never fires
ReleaseHandle() Drops the streaming reference so the assets can be collected
The handle is the reference. Assets loaded through RequestAsyncLoad stay alive only while a TSharedPtr<FStreamableHandle to that request survives (or bManageActiveHandle was true). Dropping every pointer before completion does not cancel: the manager holds the handle until the completion delegate has run, then releases it ( Engine/StreamableManager.h:316 320 ), so the assets are collectable at the next GC unless something else hard references them.
Delegate factories: FStreamableDelegate::CreateUObject(this, &UMyLoader::Fn) (safe, checks the object), CreateWeakLambda(this, Lambda) (safe, drops if the owner died), CreateLambda(Lambda) (unsafe with a captured this ).
UE ENABLE STREAMABLE JIT ASYNC LOADING ( Engine/StreamableManager.h:20 ) defaults to 0 and gates the just in time async loader at compile time. s.StreamableEnableJITAsyncLoading enables it for requests that opt in through bUseJustInTimeAsyncLoader ; s.StreamableEnableJITAsyncLoadingGlobally forces it for every request. JIT trickles requests to the async loader instead of queueing them all at once, which keeps prioritisation and cancellation responsive under load. FStreamableHandle::IsUsingJustInTimeAsyncLoader() reports the resolved state.
More patterns — batching, combined handles, progress, cancellation — in [asset loading patterns](references/asset loading patterns.md).
Asset Manager
UAssetManager is the global singleton registered through [/Script/Engine.Engine] AssetManagerClassName . Subclass it to override StartInitialLoading() and PostInitialAssetScan() .
Signatures ( Engine/AssetManager.h:333,340 ):
LoadPrimaryAssets (plural), LoadPrimaryAssetsWithType , ChangeBundleStateForPrimaryAssets and ChangeBundleStateForMatchingPrimaryAssets all come in the same pair of shapes. Location is filled in by the compiler — never pass it. Prefer the FAssetManagerLoadParams overload for new code; it carries OnComplete , OnCancel (both FStreamableDelegateWithHandle ), OnUpdate and Priority .
Unlike raw streamable requests, primary assets stay loaded until you unload them : you do not have to keep the returned handle alive, only to poll or wait on it.
ScanPathsForPrimaryAssets(FPrimaryAssetType, const TArray<FString & Paths, UClass BaseClass, bool bHasBlueprintClasses, bool bIsEditorOnly = false, bool bForceSynchronousScan = true) registers types from code when config driven scanning is not enough. AddDynamicAsset(const FPrimaryAssetId&, const FSoftObjectPath&, const FAssetBundleData&) registers a runtime generated primary asset.
Cook Rules
FPrimaryAssetTypeInfo fields ( Engine/AssetManagerTypes.h:131 ): PrimaryAssetType , AssetBaseClass , bHasBlueprintClasses , bIsEditorOnly , Directories , SpecificAssets , Rules . FPrimaryAssetRules ( Engine/AssetManagerTypes.h:66 ): Priority , ChunkId , bApplyRecursively , CookRule .
EPrimaryAssetCookRule ( Engine/AssetManagerTypes.h:28 ):
Value Meaning
Unknown Cooks in development and production only if something references it
NeverCook Never cooked; a dependency on it is an error
ProductionNeverCook Cooks in development if referenced, never in production
DevelopmentAlwaysProductionNeverCook Always cooked in development, never in production
DevelopmentAlwaysProductionUnknownCook Always in development; production only if referenced
AlwaysCook Always cooked in both
UAs