feat: Add Blueprint Limitations & Workarounds documentation and update GameTagRegistry spec

This commit is contained in:
Lefteris Notas
2026-05-19 18:09:10 +03:00
parent fef25a3363
commit bc8ab7c48f
3 changed files with 369 additions and 66 deletions

View File

@@ -54,16 +54,16 @@ None. The registry is a flat collection of tag declarations.
| Name | Inputs | Outputs | Category | Description |
|------|--------|---------|----------|-------------|
| `GetAllRegisteredTags` | — | `Array<FGameplayTag>` | Query | Returns all tags defined in this registry (reads from the project's tag table) |
| `GetTagDisplayName` | `Tag: FGameplayTag` | `FText` | Query | Returns the human-readable display name from the tag table |
| `ValidateTag` | `Tag: FGameplayTag` | `bool` | Validation | Returns `true` if the tag exists in the project's registered tag table |
| `GetAllRegisteredTags` | — | `Array<FGameplayTag>` | Query | Reads tags from `DT_ProjectTags` Data Table (see Section 14 for UE5 BP workaround) |
| `GetTagDisplayName` | `Tag: FGameplayTag` | `Text` | Query | Returns the human-readable display name via `Get Tag Display Name` node |
| `ValidateTag` | `Tag: FGameplayTag` | `Boolean` | Validation | Returns `true` if the tag is valid via `Is Gameplay Tag Valid` node |
### 6.2 Blueprint Callable Functions
| Name | Inputs | Outputs | Category | Description |
|------|--------|---------|----------|-------------|
| `LogAllTags` | — | — | Debug | Prints all registered tags to the output log (Editor-only) |
| `ExportTagNamespace` | `NamespacePrefix: FString` | `FString` | Tooling | Exports all tags matching a namespace prefix as a formatted string (for documentation generation) |
| `LogAllTags` | — | — | Debug | Prints all tags (from Data Table) to the output log (Editor-only) |
| `ExportTagNamespace` | `NamespacePrefix: String` | `String` | Tooling | Exports all tags matching a namespace prefix as a formatted string | |
---
@@ -75,17 +75,82 @@ None. This Data Asset is passive — it has no runtime events.
## 8. Overridden Events
| Event | Description |
|-------|-------------|
| `OnAssetLoaded` | Logs a validation warning if no Gameplay Tags are registered in the project |
| `PreSaveGameplayTags` | Ensures the tag table is synchronised with the asset's documented namespaces |
**Note:** Data Assets do NOT have `BeginPlay`, `Tick`, or `OnAssetLoaded` in Blueprint. All validation logic should be called externally from a GameInstance or Subsystem during initialization.
---
### Initialization Pattern (called externally):
```
[In GI_GameFramework.Init() or BPC_StateManager.BeginPlay:]
├─► Load DA_GameTagRegistry (hard reference or Get Data Asset)
├─► Call DA_GameTagRegistry.GetAllRegisteredTags()
├─► Array Length == 0?
│ ├─► Yes → Print Warning: "No tags in DT_ProjectTags! Framework tags are unregistered."
│ └─► No → Log: "N tags registered."
└─► Optional: call LogAllTags() for debug output
```
## 9. Blueprint Graph Logic
### 9.1 Initialisation Flow
### 9.1 GetAllRegisteredTags (Data Table Proxy)
**⚠️ UE5 Limitation:** `UGameplayTagsManager::RequestAllGameplayTags()` is C++ only — not exposed to Blueprints. This function uses a **Data Table proxy** instead. Tags must be registered in `DT_ProjectTags` which mirrors `DefaultGameplayTags.ini`.
```
[Function: GetAllRegisteredTags] → Array<GameplayTag>
Step 1: Get Data Table Row Names (DT_ProjectTags)
Step 2: Create empty Array<GameplayTag> → LocalTags
Step 3: ForEachLoop (RowNames):
├─► Get Data Table Row (DT_ProjectTags, RowName)
│ → Break the GameplayTagTableRow struct → get "Tag" field
├─► Add "Tag" to LocalTags array
Step 4: Return LocalTags
```
**Node Search:** `Get Data Table Row Names`, `ForEachLoop`, `Get Data Table Row`, `Break GameplayTagTableRow`, `Add`
### 9.2 ValidateTag
**⚠️ UE5 Limitation:** `UGameplayTagsManager::RequestGameplayTag()` is not a direct Blueprint node. Use `Is Gameplay Tag Valid` instead.
```
[Function: ValidateTag(Tag)] → Boolean
Step 1: Is Gameplay Tag Valid (Tag)
├─► True → Return true
└─► False → Print Warning: "Invalid Tag: {Get Tag Name(Tag)}" → Return false
```
**Node Search:** `Is Gameplay Tag Valid`, `Get Tag Name`
### 9.3 GetTagDisplayName
```
[Function: GetTagDisplayName(Tag)] → Text
Step 1: Get Tag Display Name (Tag) → Return
```
**Node Search:** `Get Tag Display Name` (Blueprint pure node, available)
### 9.4 LogAllTags
```
[Function: LogAllTags] (Blueprint Callable)
Step 1: GetAllRegisteredTags() → Store in LocalTags
Step 2: ForEachLoop (LocalTags):
├─► Get Tag Name → ToString → Print String
Step 3: Print String: "Total tags: {Array Length(LocalTags)}"
```
### 9.5 ExportTagNamespace(Prefix: String) → String
```
[Function: ExportTagNamespace]
Step 1: GetAllRegisteredTags() → LocalTags
Step 2: Create empty String → Output
Step 3: ForEachLoop (LocalTags):
├─► Get Tag Name → ToString → TagString
├─► Branch: Does TagString start with Prefix?
│ True → Append TagString + "\n" to Output
└─► Continue
Step 4: Return Output
```
[OnAssetLoaded]
└─► Call GetAllRegisteredTags()
@@ -179,104 +244,151 @@ This asset does not talk to other systems directly. All communication is passive
## 14. Manual Implementation Guide
> **For human implementer:** Follow these steps to build `DA_GameTagRegistry` in UE5.
> **For human implementer:** Follow these steps to build `DA_GameTagRegistry` in UE5 Blueprints.
> **⚠️ UE5 BP Limitation:** `UGameplayTagsManager::RequestAllGameplayTags()` is C++ only. This implementation uses a **Data Table proxy** (`DT_ProjectTags`) as the 100% Blueprint workaround. You must create and maintain `DT_ProjectTags` alongside your `DefaultGameplayTags.ini`.
### 14.0 Prerequisite: Create the Tag Data Table
Before implementing the Data Asset, create the proxy Data Table:
1. Right-click in Content Browser → **Miscellaneous → Data Table**
2. Row Structure: `GameplayTagTableRow`
3. Name: `DT_ProjectTags`
4. Save to: `Content/Framework/Core/`
5. Add rows: one per tag (row name = anything, fill the "Tag" field with your GameplayTag)
6. Go to **Project Settings → GameplayTags → Gameplay Tag Table List** → click `+` → select `DT_ProjectTags`
- This auto-registers these tags with the engine's tag manager.
### 14.1 Class Setup
1. Right-click in Content Browser → **Miscellaneous → Data Asset**
2. Select parent class: `PrimaryDataAsset`
2. Select parent class: `PrimaryDataAsset`
3. Name: `DA_GameTagRegistry`
4. Save to: `Content/Framework/Core/`
### 14.2 Variables
Add to Class Defaults:
| Variable | Type | Instance Editable | Default |
|----------|------|------------------|---------|
| `TagNamespace` | `Text` | ✓ | *Framework tag namespace description* |
| `bIsFrameworkTag` | `Boolean` | ✓ | `true` |
There are NO tag entries stored in this asset. Tags live in `Project Settings → GameplayTags` or `DefaultGameplayTags.ini`.
| Variable | Type | Instance Editable | Default | Category |
|----------|------|------------------|---------|----------|
| `TagNamespace` | `Text` | ✓ | *"Framework tag namespace documentation"* | Documentation |
| `bIsFrameworkTag` | `Boolean` | ✓ | `true` | Documentation |
| `TagDataTable` | `Data Table` (Object Reference) | ✓ | `DT_ProjectTags` | Config |
### 14.3 Function Implementations
#### `GetAllRegisteredTags` → `Array of GameplayTag`
#### `GetAllRegisteredTags()` → `Array<GameplayTag>` *(Blueprint Pure)*
**Purpose:** Returns every tag registered in the project.
**Purpose:** Returns all tags from the Data Table proxy. This replaces the C++-only `UGameplayTagsManager::RequestAllGameplayTags()`.
**Nodes to Use (Blueprint Implementable Function):**
**Node-by-Node Logic:**
```
[Function: GetAllRegisteredTags]
└─ Get All Gameplay Tags (from GameplayTags subsystem)
└─ Return the array
[Function: GetAllRegisteredTags] (Pure, no execution pins)
Step 1: Get Data Table Row Names (TagDataTable) → returns Array<Name>
Step 2: Create empty Array<GameplayTag> → LocalTags
Step 3: ForEachLoop over RowNames:
├─► Get Data Table Row (TagDataTable, Array Element)
│ → Struct Pin: Break GameplayTagTableRow
│ → Get "Tag" field (type: GameplayTag)
├─► Add "Tag" to LocalTags array
Step 4: Return LocalTags
```
**Node Search:** Right-click → "Get All Gameplay Tags"
**Nodes to Search:** `Get Data Table Row Names`, `ForEachLoop`, `Get Data Table Row`, `Break GameplayTagTableRow`, `Add`, `Array`
#### `GetTagDisplayName(Tag)` → `Text`
**⚠️ Note:** Since this is a **Pure** function, the ForEachLoop must be inside a **Pure function graph** (which supports loops in UE5). If your UE version doesn't support loops in Pure functions, make this a **BlueprintCallable** (impure) function instead.
**Nodes to Use (Blueprint Pure):**
#### `GetTagDisplayName(Tag: GameplayTag)` → `Text` *(Blueprint Pure)*
**Node-by-Node Logic:**
```
[Function: GetTagDisplayName]
Input Tag → Get Tag Display Name (from GameplayTags)
└─ Return the text
Input Tag → Get Tag Display Name (Tag) → Return
```
**Node Search:** Right-click → "Get Tag Display Name"
**Node Search:** `Get Tag Display Name` — this IS available in Blueprint (part of GameplayTags plugin).
#### `ValidateTag(Tag)` → `Boolean`
#### `ValidateTag(Tag: GameplayTag)` → `Boolean` *(Blueprint Pure)*
**Nodes to Use (Blueprint Pure / Callable):**
**⚠️ UE5 Limitation:** `UGameplayTagsManager::RequestGameplayTag()` is not directly available in BP. Use `Is Gameplay Tag Valid` instead.
**Node-by-Node Logic:**
```
[Function: ValidateTag]
Input Tag → Is Valid Tag (from GameplayTags)
Branch on result:
True → Return true
False → Print Warning: "Invalid Tag: {Tag}" → Return false
Step 1: Is Gameplay Tag Valid (Tag)
├─► True → Return true
└─► False → Print Warning: "Invalid Tag: " + Get Tag Name(Tag) → Return false
```
**Node Search:** Right-click → "Is Gameplay Tag Valid"
**Node Search:** `Is Gameplay Tag Valid`, `Get Tag Name`
#### `LogAllTags` → *(void)*
**⚠️ Note:** `Is Gameplay Tag Valid` returns true only if the tag is registered in the engine's tag table (which `DT_ProjectTags` populates via Project Settings). Tags NOT in the table will return false.
**Blueprint Callable — Editor Only:**
#### `LogAllTags()` → *(void)* *(Blueprint Callable)*
**Editor Only.** Prints all tags from the Data Table to the output log.
**Node-by-Node Logic:**
```
[Function: LogAllTags]
├─ Get All Gameplay Tags → ForEachLoop
│ └─ Print String: Tag.ToString()
└─ Print String: "Total tags: {Array Length}"
Step 1: Call GetAllRegisteredTags()LocalTags
Step 2: ForEachLoop (LocalTags):
├─► Get Tag Name (Array Element) → ToString → Print String
Step 3: Print String: "Total tags: " + Array Length(LocalTags)
```
**Node Search:** Right-click → "ForEachLoop", "Print String"
**Node Search:** `ForEachLoop`, `Get Tag Name`, `ToString (String)`, `Print String`, `Array Length`
#### `ExportTagNamespace(NamespacePrefix)` → `String`
#### `ExportTagNamespace(NamespacePrefix: String)` → `String` *(Blueprint Callable)*
**Blueprint Callable:**
**Node-by-Node Logic:**
```
[Function: ExportTagNamespace]
├─ Get All Gameplay Tags
├─ ForEachLoop:
│ └─ Get Tag Name → ToString
└─ Does string start with NamespacePrefix?
True → Append to output string with newline
└─ Return output string
Step 1: Call GetAllRegisteredTags() → LocalTags
Step 2: Create String variable → Output = ""
Step 3: ForEachLoop (LocalTags):
├─► Get Tag Name (Array Element) → ToString → TagString
├─► Branch: Does TagString start with NamespacePrefix? (use "Starts With" string node)
│ True → Append TagString + "\n" to Output (use "Append" or "Build String")
└─► Continue
Step 4: Return Output
```
### 14.4 Networking
**Node Search:** `Starts With (String)`, `Append`, `Build String`, `ForEachLoop`
No replication needed. This is a read-only Data Asset. All clients load identical copies from disk.
### 14.4 External Initialization
### 14.5 Blueprint Build Checklist
Since Data Assets have no `BeginPlay`, call validation from your GameInstance or StateManager:
- [ ] Create Data Asset: `DA_GameTagRegistry` (Parent: `PrimaryDataAsset`)
- [ ] Add `TagNamespace` (Text) and `bIsFrameworkTag` (Bool) variables
- [ ] Implement `GetAllRegisteredTags` function (Pure)
- [ ] Implement `GetTagDisplayName` function (Pure)
- [ ] Implement `ValidateTag` function (Pure)
- [ ] Implement `LogAllTags` (Editor-only)
- [ ] Implement `ExportTagNamespace` (Tooling)
- [ ] Document all tag namespaces in the asset's Description field (use Section 10 as reference)
- [ ] Verify: all tag namespaces from Section 10 exist in `DefaultGameplayTags.ini`
**In `GI_GameFramework.Init()` or `BPC_StateManager.BeginPlay()`:**
```
Step 1: Get DA_GameTagRegistry (hard reference or Load Asset)
Step 2: Call DA_GameTagRegistry.GetAllRegisteredTags() → LocalTags
Step 3: Branch: Array Length(LocalTags) == 0
├─► True → Print Warning: "No tags in DT_ProjectTags!"
└─► False → Print String: "N tags registered: " + Array Length
Step 4: [Debug builds only] Call DA_GameTagRegistry.LogAllTags()
```
### 14.5 Networking
No replication needed. This is a read-only Data Asset with a read-only Data Table reference. All clients load identical copies from disk.
### 14.6 Blueprint Build Checklist
- [ ] Create Data Table `DT_ProjectTags` (Row Structure: `GameplayTagTableRow`)
- [ ] Populate `DT_ProjectTags` with all framework tags from Section 10
- [ ] Add `DT_ProjectTags` to `Project Settings → GameplayTags → Gameplay Tag Table List`
- [ ] Create Data Asset `DA_GameTagRegistry` (Parent: `PrimaryDataAsset`)
- [ ] Add variables: `TagNamespace` (Text), `bIsFrameworkTag` (Boolean), `TagDataTable` (Data Table Ref → DT_ProjectTags)
- [ ] Implement `GetAllRegisteredTags` using Data Table Row iteration
- [ ] Implement `GetTagDisplayName` using `Get Tag Display Name` node
- [ ] Implement `ValidateTag` using `Is Gameplay Tag Valid` node
- [ ] Implement `LogAllTags` (editor-only, prints all tags)
- [ ] Implement `ExportTagNamespace` (string prefix filtering)
- [ ] Add external initialization call from `GI_GameFramework.Init()` or `BPC_StateManager.BeginPlay()`
- [ ] Verify: all tags from Section 10 exist in `DT_ProjectTags`
- [ ] Verify: `ValidateTag` returns true for registered tags, false for unregistered
---