$npx -y skills add quodsoler/unreal-engine-skills --skill ue-data-assets-tablesUse 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 re
| 1 | # UE Data Assets and Tables |
| 2 | |
| 3 | You are an expert in Unreal Engine's data management and asset loading systems. |
| 4 | |
| 5 | --- |
| 6 | |
| 7 | ## Context |
| 8 | |
| 9 | Read `.agents/ue-project-context.md` for project-specific data patterns, module layout, plugin dependencies, and any custom AssetManager subclass or DataAsset conventions the project has established. |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## Information Gathering |
| 14 | |
| 15 | Before generating code or advice, ask: |
| 16 | |
| 17 | 1. What kind of data is being stored? (item stats, level config, ability definitions, NPC data, etc.) |
| 18 | 2. Is this data authored by designers in spreadsheets, or configured directly in the editor? |
| 19 | 3. What are the loading requirements — always in memory, loaded per-level, streamed on demand? |
| 20 | 4. Is memory budget a concern? How many instances are expected? |
| 21 | 5. Does the project already use a custom `UAssetManager` subclass? |
| 22 | |
| 23 | --- |
| 24 | |
| 25 | ## Core Framework |
| 26 | |
| 27 | ### DataAssets vs DataTables — Choosing the Right Tool |
| 28 | |
| 29 | | Concern | DataAsset | DataTable | |
| 30 | |---|---|---| |
| 31 | | Structure | C++ class with typed UPROPERTY fields | Row struct, all rows same shape | |
| 32 | | Designer workflow | Editor-authored instances, picker UI | Spreadsheet import (CSV/JSON) | |
| 33 | | Hierarchy / inheritance | Yes, via Blueprint subclasses | No | |
| 34 | | Asset Manager integration | Yes (`UPrimaryDataAsset`) | Not directly | |
| 35 | | Bulk lookup by row name | No | Yes (`FindRow`) | |
| 36 | | Best for | Per-item config objects | Large flat tables (loot, dialogue, XP curves) | |
| 37 | |
| 38 | --- |
| 39 | |
| 40 | ## DataAssets |
| 41 | |
| 42 | ### UDataAsset — Simple Configuration Objects |
| 43 | |
| 44 | `UDataAsset` (declared in `Engine/DataAsset.h`) is the base class. Assets are only loaded when directly referenced or explicitly loaded. Subclass it with typed UPROPERTY fields: |
| 45 | |
| 46 | ```cpp |
| 47 | UCLASS(BlueprintType) |
| 48 | class MYGAME_API UMyItemData : public UDataAsset |
| 49 | { |
| 50 | GENERATED_BODY() |
| 51 | public: |
| 52 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item") FText DisplayName; |
| 53 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item") float BaseDamage = 10.f; |
| 54 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item") TSoftObjectPtr<UStaticMesh> Mesh; |
| 55 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item") TSoftClassPtr<AActor> SpawnClass; |
| 56 | }; |
| 57 | ``` |
| 58 | |
| 59 | In the editor: right-click in Content Browser > Miscellaneous > Data Asset, select `UMyItemData`. |
| 60 | |
| 61 | ### UPrimaryDataAsset — Asset Manager Integration |
| 62 | |
| 63 | `UPrimaryDataAsset` overrides `GetPrimaryAssetId()` so the Asset Manager can track, scan, and load it. The Primary Asset Type is derived from the first native class in the hierarchy. |
| 64 | |
| 65 | ```cpp |
| 66 | // PrimaryAssetType == native class name; PrimaryAssetName == asset name. |
| 67 | UCLASS(BlueprintType) |
| 68 | class MYGAME_API UWeaponDefinition : public UPrimaryDataAsset |
| 69 | { |
| 70 | GENERATED_BODY() |
| 71 | public: |
| 72 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon") |
| 73 | FText WeaponName; |
| 74 | |
| 75 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon") |
| 76 | float FireRate = 1.f; |
| 77 | |
| 78 | // meta = (AssetBundles = "X") groups soft refs for selective AM loading. |
| 79 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon", |
| 80 | meta = (AssetBundles = "UI")) |
| 81 | TSoftObjectPtr<UTexture2D> Icon; |
| 82 | |
| 83 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon", |
| 84 | meta = (AssetBundles = "Game")) |
| 85 | TSoftObjectPtr<USkeletalMesh> WorldMesh; |
| 86 | }; |
| 87 | ``` |
| 88 | |
| 89 | --- |
| 90 | |
| 91 | ## DataTables |
| 92 | |
| 93 | ### Defining a Row Struct |
| 94 | |
| 95 | `FTableRowBase` is declared in `Engine/DataTable.h`. Every row struct must inherit it and use `USTRUCT(BlueprintType)`. |
| 96 | |
| 97 | ```cpp |
| 98 | // ItemTableRow.h |
| 99 | #pragma once |
| 100 | #include "Engine/DataTable.h" |
| 101 | #include "ItemTableRow.generated.h" |
| 102 | |
| 103 | USTRUCT(BlueprintType) |
| 104 | struct FItemTableRow : public FTableRowBase |
| 105 | { |
| 106 | GENERATED_BODY() |
| 107 | |
| 108 | UPROPERTY(EditAnywhere, BlueprintReadWrite) |
| 109 | FText DisplayName; |
| 110 | |
| 111 | UPROPERTY(EditAnywhere, BlueprintReadWrite) |
| 112 | int32 MaxStack = 1; |
| 113 | |
| 114 | UPROPERTY(EditAnywhere, BlueprintReadWrite) |
| 115 | float Weight = 0.5f; |
| 116 | |
| 117 | UPROPERTY(EditAnywhere, BlueprintReadWrite) |
| 118 | TSoftObjectPtr<UStaticMesh> PreviewMesh; |
| 119 | |
| 120 | // Called after CSV/JSON import. Override for custom fixups. |
| 121 | virtual void OnPostDataImport(const UDataTable* InDataTable, |
| 122 | const FName InRowName, |
| 123 | TArray<FString>& OutCollectedImportProblems) override; |
| 124 | }; |
| 125 | ``` |
| 126 | |
| 127 | In the editor: right-click > Miscellaneous > Data Table, assign `FItemTableRow` as the row struct. |
| 128 | |
| 129 | ### Querying DataTables at Runtime |
| 130 | |
| 131 | ```cpp |
| 132 | UPROPERTY(EditDefaultsOnly, Category = "Data") |
| 133 | TObjectPtr<UDataTable> ItemTable; |