docs: Update item pickup setup instructions and add game examples for Blueprint walkthroughs
This commit is contained in:
@@ -46,23 +46,180 @@ Based on `Item Type`, fill the relevant sub-struct (other sub-panels auto-hide v
|
|||||||
| `Ammo` | `AmmoData` (legacy) | AmmoTypeTag, PerPickupCount |
|
| `Ammo` | `AmmoData` (legacy) | AmmoTypeTag, PerPickupCount |
|
||||||
| `KeyItem`, `Document`, `Collectible`, `Resource`, `Misc` | None — core properties only | |
|
| `KeyItem`, `Document`, `Collectible`, `Resource`, `Misc` | None — core properties only | |
|
||||||
|
|
||||||
### Step 4 — Place a Pickup in the World
|
### Step 4 — Build the BP_ItemPickup Blueprint (one-time)
|
||||||
|
|
||||||
The Data Asset does **not** appear in the world. To spawn it:
|
The Data Asset does **not** appear in the world. You need a Blueprint Actor that references it. Here is the exact setup:
|
||||||
|
|
||||||
1. Create a `BP_ItemPickup` actor (see spec #25: [`../../04-inventory/25_BP_ItemPickup.md`](../04-inventory/25_BP_ItemPickup.md))
|
#### 4.1 — Create the Blueprint
|
||||||
2. In the `BP_ItemPickup` actor instance, set `Config → Item Data` → your `DA_Item_MedKit`
|
|
||||||
3. Drag `BP_ItemPickup` into the level
|
|
||||||
4. The pickup reads the mesh from the Data Asset, handles overlap, and calls `BPC_InventorySystem.AddItem(ItemData)` on interact
|
|
||||||
|
|
||||||
```
|
```
|
||||||
DA_Item_MedKit (Content Browser) ← defines WHAT the item is
|
Content Browser → Framework/Inventory/
|
||||||
|
Right-click → Blueprint Class → Actor
|
||||||
|
Name: BP_ItemPickup
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.2 — Add Components
|
||||||
|
|
||||||
|
Open `BP_ItemPickup` → **Viewport** tab → **Add Component** (top-left button):
|
||||||
|
|
||||||
|
| # | Component Class | Name | Purpose |
|
||||||
|
|---|----------------|------|---------|
|
||||||
|
| 1 | `StaticMeshComponent` | `Mesh` | Renders the item's 3D model in the world |
|
||||||
|
| 2 | `SphereComponent` | `InteractionCollision` | Detects when the player is near enough to pick up |
|
||||||
|
| 3 | *(optional)* `AudioComponent` | `PickupSound` | Plays a sound when collected |
|
||||||
|
|
||||||
|
**Select `InteractionCollision` → Details panel:**
|
||||||
|
- `Sphere Radius` → `150.0` (how close the player must be)
|
||||||
|
- Under **Collision Presets** → set to `OverlapOnlyPawn` (only the player triggers it)
|
||||||
|
|
||||||
|
#### 4.3 — Create the Config Variable
|
||||||
|
|
||||||
|
1. In the **My Blueprint** panel (left side), click **+ Variable**
|
||||||
|
2. Name: `Config`
|
||||||
|
3. Variable Type: **Struct → `S_PickupConfig`** (you may need to create this struct first — see spec #25 for its fields)
|
||||||
|
|
||||||
|
If `S_PickupConfig` doesn't exist yet, create it manually:
|
||||||
|
```
|
||||||
|
Content Browser → Framework/Inventory/
|
||||||
|
Right-click → Blueprints → Structure
|
||||||
|
Name: S_PickupConfig
|
||||||
|
Add fields:
|
||||||
|
ItemData → Type: DA_ItemData (Object Reference)
|
||||||
|
Quantity → Type: Integer, default: 1
|
||||||
|
bAutoRotate → Type: Boolean, default: true
|
||||||
|
bBobUpDown → Type: Boolean, default: true
|
||||||
|
BobAmplitude → Type: Float, default: 10.0
|
||||||
|
BobFrequency → Type: Float, default: 1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
4. After creating the variable, select it → Details panel:
|
||||||
|
- **Instance Editable** → ✓ checked (so you can set it per-instance in the level)
|
||||||
|
- **Category** → `Pickup Config`
|
||||||
|
- **Tooltip** → "The item data asset this pickup represents"
|
||||||
|
|
||||||
|
#### 4.4 — Wire the Construction Script
|
||||||
|
|
||||||
|
Go to the **Construction Script** tab (or open the Construction Script graph). This runs every time you change the Config in the editor, so the mesh updates immediately.
|
||||||
|
|
||||||
|
```
|
||||||
|
Construction Script
|
||||||
|
│
|
||||||
|
├─ Get Config → Break S_PickupConfig → ItemData
|
||||||
|
│ │
|
||||||
|
│ ├─ Is Valid (ItemData)?
|
||||||
|
│ │ ├─ True → continue
|
||||||
|
│ │ └─ False → Print String "No ItemData assigned" (Editor-only) → Return
|
||||||
|
│ │
|
||||||
|
│ └─ ItemData → Get World Mesh (this is a soft reference — may not be loaded)
|
||||||
|
│ │
|
||||||
|
│ ├─ Is Valid (WorldMesh)?
|
||||||
|
│ │ ├─ True:
|
||||||
|
│ │ │ → Set Static Mesh (Mesh component, New Mesh = WorldMesh)
|
||||||
|
│ │ │ → ItemData → Get Display Name → Set Actor Label
|
||||||
|
│ │ └─ False:
|
||||||
|
│ │ → ItemData → World Mesh → Async Load Asset
|
||||||
|
│ │ → On Load Complete → Set Static Mesh
|
||||||
|
│ │
|
||||||
|
│ └─ ItemData → Get Icon → (cache for UI prompt)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key node-by-node:**
|
||||||
|
1. Drag the `Config` variable from My Blueprint → **Get Config**
|
||||||
|
2. **Break S_PickupConfig** (right-click the pin → Split Struct Pin, or drag off → "Break")
|
||||||
|
3. From the `ItemData` pin, drag off → Create a `IsValid` node
|
||||||
|
4. From the valid `ItemData` output, drag off → type `Get World Mesh` (this appears because `WorldMesh` is a `UPROPERTY` on `DA_ItemData`; if using soft reference, you'll get a `TSoftObjectPtr<UStaticMesh>` — connect to **Async Load Asset** or **Load Synchronous** for Construction Script)
|
||||||
|
5. From the resolved mesh, connect to `Set Static Mesh` node → target is the `Mesh` component (drag Mesh from the Components panel into the graph → Get)
|
||||||
|
|
||||||
|
#### 4.5 — Wire Interaction (I_Interactable)
|
||||||
|
|
||||||
|
**Step A — Add the Interface:**
|
||||||
|
1. **Class Settings** (toolbar button) → **Interfaces** → **Add** → select `UInteractable` (the C++ interface from `I_InterfaceLibrary.h`)
|
||||||
|
2. Compile. Now `Interact`, `Can Interact`, `Get Interaction Prompt` events appear in the right-click menu.
|
||||||
|
|
||||||
|
**Step B — Wire the Events:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Event Interact (from I_Interactable)
|
||||||
|
│ Interactor: AActor*
|
||||||
|
│
|
||||||
|
├─ Get Config → Break → ItemData
|
||||||
|
│
|
||||||
|
├─ Interactor → Get Component by Class (BPC_InventorySystem)
|
||||||
|
│ │
|
||||||
|
│ └─ Is Valid?
|
||||||
|
│ ├─ False → Return (no inventory system on interactor)
|
||||||
|
│ └─ True → InventorySystem
|
||||||
|
│ │
|
||||||
|
│ ├─ Call Can Add Item(ItemData, Quantity from Config)
|
||||||
|
│ │ │
|
||||||
|
│ │ ├─ False → Print "Inventory Full" → Return
|
||||||
|
│ │ └─ True:
|
||||||
|
│ │ ├─ Call Add Item(ItemData, Quantity)
|
||||||
|
│ │ ├─ Play Sound at Location (PickupSound, GetActorLocation)
|
||||||
|
│ │ ├─ Destroy Actor
|
||||||
|
│ │ └─ Return
|
||||||
|
│ │
|
||||||
|
│ └─ (If ItemData has bIsInfinite or respawn logic — skip Destroy)
|
||||||
|
|
||||||
|
Event Can Interact (from I_Interactable)
|
||||||
|
│ Interactor, OutBlockReason (by ref)
|
||||||
|
│
|
||||||
|
├─ Is Config.ItemData Valid?
|
||||||
|
│ ├─ False → OutBlockReason = "No item data" → Return False
|
||||||
|
│ └─ True → Return True
|
||||||
|
|
||||||
|
Event Get Interaction Prompt (from I_Interactable)
|
||||||
|
│
|
||||||
|
├─ Get Config → ItemData → Get Display Name
|
||||||
|
└─ Return "Pick up [ItemName]"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.6 — Add Bobbing Rotation (optional polish)
|
||||||
|
|
||||||
|
In **Event Graph → Event BeginPlay**:
|
||||||
|
|
||||||
|
```
|
||||||
|
Event BeginPlay
|
||||||
|
│
|
||||||
|
├─ Get Config → Break → bAutoRotate
|
||||||
|
│ └─ True → Add Timeline → name: "BobAndRotate"
|
||||||
|
│ ├─ Update track:
|
||||||
|
│ │ ├─ Mesh → Add World Rotation (DeltaRotation = 0,0,Timeline.Rotation * 90)
|
||||||
|
│ │ └─ Mesh → Add World Offset (Delta = 0,0,Timeline.Bob * BobAmplitude * sin(time), ...)
|
||||||
|
│ │
|
||||||
|
│ └─ Timeline: float track 0→1 looping over 2 seconds
|
||||||
|
├─ Enable collision on InteractionCollision
|
||||||
|
└─ Bind OnComponentBeginOverlap(InteractionCollision) → Highlight mesh, show prompt
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.7 — Compile and Use
|
||||||
|
|
||||||
|
1. **Compile → Save**
|
||||||
|
2. **Drag `BP_ItemPickup` into your level**
|
||||||
|
3. Select the instance → Details panel → **Pickup Config → Item Data** → select `DA_Item_MedKit`
|
||||||
|
4. Set `Quantity` → `1` (or more for stacked items)
|
||||||
|
5. The **Construction Script** runs immediately — the mesh should appear in the viewport
|
||||||
|
6. Position the actor where you want it
|
||||||
|
|
||||||
|
**Verification in PIE (Play In Editor):**
|
||||||
|
- The pickup should show its mesh, slowly rotating
|
||||||
|
- Walk up to it — the overlap event fires
|
||||||
|
- Press Interact — item goes into inventory, pickup disappears
|
||||||
|
- Open inventory → item should be in a slot with the correct name and icon
|
||||||
|
|
||||||
|
```
|
||||||
|
DA_Item_MedKit (Content Browser) ← defines IDENTITY
|
||||||
↑ referenced by
|
↑ referenced by
|
||||||
BP_ItemPickup (Actor in level) ← physical body in the world
|
BP_ItemPickup (Actor in level) ← physical BODY in world
|
||||||
|
├── StaticMeshComponent "Mesh" ← renders WorldMesh from Data Asset
|
||||||
|
├── SphereComponent "Collision" ← detects player proximity
|
||||||
|
└── Config struct ← holds pointer to DA_ItemData + quantity
|
||||||
↑ interacts via
|
↑ interacts via
|
||||||
BPC_InteractionDetector → BPC_InventorySystem.AddItem()
|
BPC_InteractionDetector → I_Interactable → BPC_InventorySystem.AddItem()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **For concrete, step-by-step examples** of building specific item types (flashlight, pistol, medkit, keycard) with complete Blueprint graphs, component lists, and wiring diagrams, see the [`docs/game/`](../../game/README.md) directory.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Single Identity Tag — Why One Tag Is Enough
|
## Single Identity Tag — Why One Tag Is Enough
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Remaining Blueprint Build Order — All 135 Systems
|
# Remaining Blueprint Build Order — All 135 Systems
|
||||||
|
|
||||||
**Version:** 1.0 | **Generated:** 2026-05-21 | **C++ Status:** 22 header files (12 full, 10 stubs)
|
**Version:** 1.1 | **Generated:** 2026-05-21 | **C++ Status:** 22 header files (12 full, 10 stubs)
|
||||||
|
|
||||||
This document is the authoritative "what's left to build" reference. It cross-references the 22 C++ classes in `Source/PG_Framework/` against all 135 Blueprint spec files to show exactly what Blueprint assets remain to be created, in dependency-safe build order.
|
This document is the authoritative "what's left to build" reference. It cross-references the 22 C++ classes in `Source/PG_Framework/` against all 135 Blueprint spec files to show exactly what Blueprint assets remain to be created, in dependency-safe build order.
|
||||||
|
|
||||||
@@ -67,8 +67,19 @@ These 8 systems are the bedrock. Without them, nothing else runs.
|
|||||||
|--------|--------|
|
|--------|--------|
|
||||||
| **C++ Status** | ✅ Full — `Source/PG_Framework/Public/Core/DA_GameTagRegistry.h` + `.cpp` |
|
| **C++ Status** | ✅ Full — `Source/PG_Framework/Public/Core/DA_GameTagRegistry.h` + `.cpp` |
|
||||||
| **BP Spec** | ✅ `docs/blueprints/01-core/01_DA_GameTagRegistry.md` |
|
| **BP Spec** | ✅ `docs/blueprints/01-core/01_DA_GameTagRegistry.md` |
|
||||||
| **What to Build** | ⬜ **Data Asset instance:** `Content/Framework/Core/DA_GameTagRegistry` → assign `TagDataTables` (11 entries) |
|
| **What to Build** | ⬜ **Data Asset instance** (not a Blueprint child — a Data Asset instance created from the C++ class) |
|
||||||
| **Manual Steps** | 11 Data Tables must be created from CSV and registered in Project Settings first. |
|
|
||||||
|
**Setup Steps:**
|
||||||
|
1. **Prerequisite:** All 11 Data Tables must already exist (imported from CSV) and registered in `Project Settings → Gameplay Tags → Gameplay Tag Table List`. If not done, go back to the Data Tables section below.
|
||||||
|
2. In Content Browser, navigate to `Content/Framework/Core/` (create folder if needed)
|
||||||
|
3. **Right-click → Miscellaneous → Data Asset**
|
||||||
|
4. In the "Pick Data Asset Class" dialog, type `DA_GameTagRegistry` and select it
|
||||||
|
5. Name it `DA_GameTagRegistry`
|
||||||
|
6. **Open the Data Asset** → find the `Tag Data Tables` array property
|
||||||
|
7. Click `+` eleven times → assign each of the 11 `DT_Tags_*` Data Tables in order
|
||||||
|
8. Save
|
||||||
|
|
||||||
|
**Verification:** Open `DA_GameTagRegistry` → call `GetAllRegisteredTags()` (right-click the asset → "Test Data Asset" or via Blueprint). Should return 300+ tags. If empty, the Data Tables are not registered in Project Settings.
|
||||||
|
|
||||||
### 02 — FL_GameUtilities
|
### 02 — FL_GameUtilities
|
||||||
| Aspect | Detail |
|
| Aspect | Detail |
|
||||||
@@ -90,30 +101,88 @@ These 8 systems are the bedrock. Without them, nothing else runs.
|
|||||||
|--------|--------|
|
|--------|--------|
|
||||||
| **C++ Status** | ✅ Full — `Source/PG_Framework/Public/Core/GI_GameFramework.h` + `.cpp` |
|
| **C++ Status** | ✅ Full — `Source/PG_Framework/Public/Core/GI_GameFramework.h` + `.cpp` |
|
||||||
| **BP Spec** | ✅ `docs/blueprints/01-core/04_GI_GameFramework.md` |
|
| **BP Spec** | ✅ `docs/blueprints/01-core/04_GI_GameFramework.md` |
|
||||||
| **What to Build** | 🔵 **BP child:** `Content/Framework/Core/BP_GameFramework` (parent: `GI_GameFramework`). In Class Defaults: set `TagRegistry` → `DA_GameTagRegistry`, `bValidateTagsOnInit` → `true`. |
|
| **What to Build** | 🔵 **BP child:** `Content/Framework/Core/BP_GameFramework` (parent: `GI_GameFramework`) |
|
||||||
| **Project Settings** | Set as **Default Game Instance** in Maps & Modes. |
|
| **Project Settings** | Set as **Default Game Instance** in Maps & Modes |
|
||||||
|
|
||||||
|
**Setup Steps:**
|
||||||
|
1. In Content Browser, navigate to `Content/Framework/Core/` (create folder if needed)
|
||||||
|
2. **Right-click → Blueprint Class**
|
||||||
|
3. In the "Pick Parent Class" dialog, scroll to the bottom and click **"All Classes"**
|
||||||
|
4. In the search box that appears, type `GI_GameFramework` and select it
|
||||||
|
5. Name it `BP_GameFramework`
|
||||||
|
6. **Double-click to open** the Blueprint → click **Class Defaults** (toolbar button at top)
|
||||||
|
7. In the Details panel, find the **Framework\|Config** category:
|
||||||
|
- `Tag Registry` → click the dropdown → select `DA_GameTagRegistry`
|
||||||
|
- `b Validate Tags On Init` → check the box (set to `true`)
|
||||||
|
- `b Log Tags On Init` → optional — check to see all tags in Output Log on startup
|
||||||
|
8. **Compile → Save**
|
||||||
|
9. **Edit → Project Settings → Maps & Modes → Game Instance Class** → select `BP_GameFramework`
|
||||||
|
|
||||||
|
**Why a BP child?** `GI_GameFramework` is `UCLASS(Blueprintable)` — you can't use the C++ class directly as Game Instance. UE5 requires a concrete class. The BP child inherits all C++ logic (game phases, session flags, service resolution, tag validation) and lets you override Blueprint events like `Init` or `Shutdown` for project-specific startup logic.
|
||||||
|
|
||||||
### 05 — GM_CoreGameMode
|
### 05 — GM_CoreGameMode
|
||||||
| Aspect | Detail |
|
| Aspect | Detail |
|
||||||
|--------|--------|
|
|--------|--------|
|
||||||
| **C++ Status** | ✅ Full — `Source/PG_Framework/Public/Core/GM_CoreGameMode.h` + `.cpp` |
|
| **C++ Status** | ✅ Full — `Source/PG_Framework/Public/Core/GM_CoreGameMode.h` + `.cpp` |
|
||||||
| **BP Spec** | ✅ `docs/blueprints/01-core/05_GM_CoreGameMode.md` |
|
| **BP Spec** | ✅ `docs/blueprints/01-core/05_GM_CoreGameMode.md` |
|
||||||
| **What to Build** | 🔵 **BP child:** `Content/Framework/Core/BP_CoreGameMode` (parent: `GM_CoreGameMode`). In Class Defaults: set `PlayerControllerClass` → `PC_CoreController`, `PlayerStateClass` → `PS_CorePlayerState`, `GameStateClass` → `BP_CoreGameState`, `DefaultPawnClass` → your player pawn. |
|
| **What to Build** | 🔵 **BP child:** `Content/Framework/Core/BP_CoreGameMode` (parent: `GM_CoreGameMode`) |
|
||||||
| **Project Settings** | Set as **Default GameMode** in Maps & Modes. |
|
| **Project Settings** | Set as **Default GameMode** in Maps & Modes |
|
||||||
|
|
||||||
|
**Setup Steps:**
|
||||||
|
1. In Content Browser, navigate to `Content/Framework/Core/`
|
||||||
|
2. **Right-click → Blueprint Class → All Classes** → search `GM_CoreGameMode`
|
||||||
|
3. Name it `BP_CoreGameMode`
|
||||||
|
4. **Double-click to open** → click **Class Defaults** (toolbar button at top)
|
||||||
|
5. The `PlayerControllerClass`, `PlayerStateClass`, `GameStateClass`, and `DefaultPawnClass` are **inherited from `AGameModeBase`** — they appear in the Details panel under the **"Classes"** section (not under Framework). Fill them in:
|
||||||
|
|
||||||
|
| Property | Value | Notes |
|
||||||
|
|----------|-------|-------|
|
||||||
|
| `Player Controller Class` | `PC_CoreController` | Your BP child of `APC_CoreController` (C++ stub — create it first) |
|
||||||
|
| `Player State Class` | `PS_CorePlayerState` | Your BP child of `APS_CorePlayerState` (C++ stub — create it first) |
|
||||||
|
| `Game State Class` | `BP_CoreGameState` | Your BP child of `AGS_CoreGameState` |
|
||||||
|
| `Default Pawn Class` | Your player pawn BP | The Character BP with all BPC_ components attached |
|
||||||
|
| `HUD Class` | `WBP_HUDController` | Your root HUD widget (create later in Phase 6) |
|
||||||
|
|
||||||
|
6. **Compile → Save**
|
||||||
|
7. **Edit → Project Settings → Maps & Modes → Default GameMode** → select `BP_CoreGameMode`
|
||||||
|
|
||||||
|
> **Important:** The C++ header explicitly states: *"PlayerControllerClass, PlayerStateClass, and GameStateClass are inherited from AGameModeBase — do not redeclare (UHT forbids shadowing). Set them in your BP child's Class Defaults."* These properties are in the inherited "Classes" category, not in custom Framework categories. If you can't find them, make sure you're looking at Class Defaults (not the Event Graph) and scroll through all categories.
|
||||||
|
|
||||||
### 06 — GS_CoreGameState
|
### 06 — GS_CoreGameState
|
||||||
| Aspect | Detail |
|
| Aspect | Detail |
|
||||||
|--------|--------|
|
|--------|--------|
|
||||||
| **C++ Status** | ✅ Full — `Source/PG_Framework/Public/Core/GS_CoreGameState.h` + `.cpp` |
|
| **C++ Status** | ✅ Full — `Source/PG_Framework/Public/Core/GS_CoreGameState.h` + `.cpp` |
|
||||||
| **BP Spec** | ✅ `docs/blueprints/01-core/06_GS_CoreGameState.md` |
|
| **BP Spec** | ✅ `docs/blueprints/01-core/06_GS_CoreGameState.md` |
|
||||||
| **What to Build** | 🔵 **BP child:** `Content/Framework/Core/BP_CoreGameState` (parent: `GS_CoreGameState`). No extra config needed — replicated state works out of the box. |
|
| **What to Build** | 🔵 **BP child:** `Content/Framework/Core/BP_CoreGameState` (parent: `GS_CoreGameState`) |
|
||||||
|
|
||||||
|
**Setup Steps:**
|
||||||
|
1. In Content Browser, navigate to `Content/Framework/Core/`
|
||||||
|
2. **Right-click → Blueprint Class → All Classes** → search `GS_CoreGameState`
|
||||||
|
3. Name it `BP_CoreGameState`
|
||||||
|
4. **Compile → Save** (no extra config needed — all 5 replicated state variables with `OnRep_` handlers work out of the box)
|
||||||
|
5. Assign it in `BP_CoreGameMode` → Class Defaults → Classes → `Game State Class` → `BP_CoreGameState`
|
||||||
|
|
||||||
|
**What you get for free:** Chapter tracking, narrative phase, encounter state, objective tags, play time accumulation — all replicated with dispatchers that fire identically for local and remote clients.
|
||||||
|
|
||||||
### 07 — DA_ItemData
|
### 07 — DA_ItemData
|
||||||
| Aspect | Detail |
|
| Aspect | Detail |
|
||||||
|--------|--------|
|
|--------|--------|
|
||||||
| **C++ Status** | ✅ Full — `Source/PG_Framework/Public/Inventory/DA_ItemData.h` + `.cpp` |
|
| **C++ Status** | ✅ Full — `Source/PG_Framework/Public/Inventory/DA_ItemData.h` + `.cpp` |
|
||||||
| **BP Spec** | ✅ `docs/blueprints/01-core/07_DA_ItemData.md` |
|
| **BP Spec** | ✅ `docs/blueprints/01-core/07_DA_ItemData.md` |
|
||||||
| **What to Build** | ⬜ **Data Asset instances:** `DA_Item_*` per game item (weapons, consumables, key items, documents, collectibles, tools, resources, misc). |
|
| **What to Build** | ⬜ **Data Asset instances** (NOT world actors — see note below) |
|
||||||
|
|
||||||
|
**How to create each item:**
|
||||||
|
1. `Content/Framework/DataAssets/Items/` → **Right-click → Miscellaneous → Data Asset**
|
||||||
|
2. Class: `DA_ItemData` → Name: `DA_Item_[Name]` (e.g., `DA_Item_MedKit`)
|
||||||
|
3. Open → fill core properties: ItemTag, DisplayName, Description, Icon, WorldMesh, Weight, StackLimit, ItemType
|
||||||
|
4. Based on ItemType, fill the relevant sub-struct:
|
||||||
|
- Weapon/Tool → fill `Equipment Data` (slot, damage, fire rate, magazine size)
|
||||||
|
- Consumable → fill `Consumable Data` (health restore, use duration)
|
||||||
|
- KeyItem → set `b Is Key Item` = true (auto-protects from drop/clear)
|
||||||
|
5. Save
|
||||||
|
6. **To place in world:** Create a `BP_ItemPickup` actor (spec #25), set its `Config.ItemData` to this asset, drag into level.
|
||||||
|
|
||||||
|
> **Data Assets are NOT actors.** `DA_ItemData` defines *what* the item is (identity card). `BP_ItemPickup` is the *physical body* in the world. See `docs/blueprints/01-core/07_DA_ItemData.md` for the full explanation with diagrams.
|
||||||
|
|
||||||
### 128 — SS_EnhancedInputManager
|
### 128 — SS_EnhancedInputManager
|
||||||
| Aspect | Detail |
|
| Aspect | Detail |
|
||||||
@@ -128,14 +197,40 @@ These 8 systems are the bedrock. Without them, nothing else runs.
|
|||||||
|--------|--------|
|
|--------|--------|
|
||||||
| **C++ Status** | 🟡 **Stub** — `Source/PG_Framework/Public/Player/PC_CoreController.h` + `.cpp` |
|
| **C++ Status** | 🟡 **Stub** — `Source/PG_Framework/Public/Player/PC_CoreController.h` + `.cpp` |
|
||||||
| **BP Spec** | Covered in 02-player integration docs |
|
| **BP Spec** | Covered in 02-player integration docs |
|
||||||
| **What to Build** | 🔵 **BP child:** `Content/Framework/Player/PC_CoreController` (parent: `APC_CoreController`). Add logic: input routing, BeginPlay (push IMC_Default), phase handling. |
|
| **What to Build** | 🔵 **BP child:** `Content/Framework/Player/PC_CoreController` (parent: `APC_CoreController`) |
|
||||||
|
|
||||||
|
**Setup Steps:**
|
||||||
|
1. In Content Browser, navigate to `Content/Framework/Player/`
|
||||||
|
2. **Right-click → Blueprint Class → All Classes** → search `PC_CoreController` (or `APC_CoreController`)
|
||||||
|
3. Name it `PC_CoreController`
|
||||||
|
4. **Open → Event Graph → Event BeginPlay:**
|
||||||
|
- Get Game Instance → Cast To `BP_GameFramework`
|
||||||
|
- Get Subsystem (Class: `SS_EnhancedInputManager`) — auto-created, no need to spawn
|
||||||
|
- Call `Push Context` on the subsystem: Context=`IMC_Default`, Priority=0, ContextTag=`Framework.Input.Context.Default`
|
||||||
|
5. Add input routing logic for game phase transitions (bind to `GI_GameFramework.OnGamePhaseChanged` → show/hide cursor, switch input mode)
|
||||||
|
6. **Compile → Save**
|
||||||
|
7. Assign in `BP_CoreGameMode` → Class Defaults → Classes → `Player Controller Class` → `PC_CoreController`
|
||||||
|
|
||||||
|
> **C++ stub means:** The C++ class exists solely so UHT can compile forward-declarations from `GM_CoreGameMode`. It has a constructor but no logic. All gameplay behavior (input routing, phase handling, BeginPlay) goes in your BP child.
|
||||||
|
|
||||||
### — PS_CorePlayerState
|
### — PS_CorePlayerState
|
||||||
| Aspect | Detail |
|
| Aspect | Detail |
|
||||||
|--------|--------|
|
|--------|--------|
|
||||||
| **C++ Status** | 🟡 **Stub** — `Source/PG_Framework/Public/Player/PS_CorePlayerState.h` + `.cpp` |
|
| **C++ Status** | 🟡 **Stub** — `Source/PG_Framework/Public/Player/PS_CorePlayerState.h` + `.cpp` |
|
||||||
| **BP Spec** | Covered in 02-player integration docs |
|
| **BP Spec** | Covered in 02-player integration docs |
|
||||||
| **What to Build** | 🔵 **BP child:** `Content/Framework/Player/PS_CorePlayerState` (parent: `APS_CorePlayerState`). Add player-specific replicated state: inventory slots, equipped items, narrative flags. |
|
| **What to Build** | 🔵 **BP child:** `Content/Framework/Player/PS_CorePlayerState` (parent: `APS_CorePlayerState`) |
|
||||||
|
|
||||||
|
**Setup Steps:**
|
||||||
|
1. In Content Browser, navigate to `Content/Framework/Player/`
|
||||||
|
2. **Right-click → Blueprint Class → All Classes** → search `PS_CorePlayerState` (or `APS_CorePlayerState`)
|
||||||
|
3. Name it `PS_CorePlayerState`
|
||||||
|
4. **Open → Class Defaults** (or add replicated variables in Event Graph):
|
||||||
|
- Add player-specific replicated state: inventory slot data, equipped items array, narrative flags map
|
||||||
|
- Use `Replicated` or `Replicated Using` with `OnRep_` handlers to fire dispatchers
|
||||||
|
5. **Compile → Save**
|
||||||
|
6. Assign in `BP_CoreGameMode` → Class Defaults → Classes → `Player State Class` → `PS_CorePlayerState`
|
||||||
|
|
||||||
|
> **Why empty?** The C++ stub inherits from `APlayerState` which already has `PlayerName`, `Score`, `Ping`. Add your project's player data (loadout, stats, progression) to this BP child. For multiplayer, mark variables `Replicated` and add `GetLifetimeReplicatedProps` override.
|
||||||
|
|
||||||
### Data Tables (Phase 0 Root)
|
### Data Tables (Phase 0 Root)
|
||||||
| Asset | Status |
|
| Asset | Status |
|
||||||
@@ -152,6 +247,20 @@ These 8 systems are the bedrock. Without them, nothing else runs.
|
|||||||
| `DT_Tags_Audio.csv` | ⬜ Import, register |
|
| `DT_Tags_Audio.csv` | ⬜ Import, register |
|
||||||
| `DT_Tags_Achievement.csv` | ⬜ Import, register |
|
| `DT_Tags_Achievement.csv` | ⬜ Import, register |
|
||||||
|
|
||||||
|
**How to create each Data Table:**
|
||||||
|
1. In Content Browser, navigate to `Content/Framework/DataTables/` (create folder if needed)
|
||||||
|
2. **Right-click → Miscellaneous → Data Table**
|
||||||
|
3. In the "Pick Row Structure" dialog, search and select `GameplayTagTableRow`
|
||||||
|
4. Name it exactly: `DT_Tags_[Category]` (e.g., `DT_Tags_Player`)
|
||||||
|
5. **Double-click** the Data Table to open it → click **Import** (toolbar button)
|
||||||
|
6. Select the corresponding CSV file from the framework source
|
||||||
|
7. After importing all 11, go to **Edit → Project Settings → Gameplay Tags → Gameplay Tag Table List**
|
||||||
|
8. Click `+` eleven times → assign each `DT_Tags_*` Data Table
|
||||||
|
9. **Click the refresh icon** next to "Import Tags from Config" to force tag load
|
||||||
|
10. Restart the editor to ensure all tags are cached
|
||||||
|
|
||||||
|
> **⚠️ Critical:** Without Step 8-9, NO framework gameplay tags will be recognized. `Is Gameplay Tag Valid` returns false for all tags. `DA_GameTagRegistry.ValidateTag()` fails. The CSV files alone are not enough — UE must be told which Data Tables to scan for tags.
|
||||||
|
|
||||||
### Starter GameInstance (00-project-setup)
|
### Starter GameInstance (00-project-setup)
|
||||||
| Aspect | Detail |
|
| Aspect | Detail |
|
||||||
|--------|--------|
|
|--------|--------|
|
||||||
@@ -321,7 +430,29 @@ These 2 systems are the central nervous system. `BPC_StateManager` must be on th
|
|||||||
| Aspect | Detail |
|
| Aspect | Detail |
|
||||||
|--------|--------|
|
|--------|--------|
|
||||||
| **C++ Status** | 🔵 **BP-Only** |
|
| **C++ Status** | 🔵 **BP-Only** |
|
||||||
| **What to Build** | 🔵 **BP actor child** — physical item in world: bob/rotate animation, `I_Interactable`, auto-pickup or manual pickup. Set `DA_ItemData` per instance. |
|
| **What to Build** | 🔵 **BP actor child** — physical item in world: bob/rotate animation, `I_Interactable`, auto-pickup or manual pickup |
|
||||||
|
|
||||||
|
**Setup Steps (3-part workflow):**
|
||||||
|
|
||||||
|
**Part A — Create the Data Asset (one-time per item type):**
|
||||||
|
1. `Content/Framework/DataAssets/Items/` → Right-click → Miscellaneous → Data Asset → Class: `DA_ItemData`
|
||||||
|
2. Name: `DA_Item_MedKit` → Open → fill: ItemTag, DisplayName, Icon, WorldMesh, Weight, StackLimit, ItemType
|
||||||
|
3. If ItemType=Consumable → fill ConsumableData (HealthRestore=25, UseDuration=2.0)
|
||||||
|
|
||||||
|
**Part B — Create the BP_ItemPickup Blueprint (one-time):**
|
||||||
|
1. `Content/Framework/Inventory/` → Right-click → Blueprint Class → Actor → name: `BP_ItemPickup`
|
||||||
|
2. Implement `I_Interactable` interface (Class Settings → Interfaces → Add `UInteractable`)
|
||||||
|
3. Add Components: `StaticMeshComponent` (named "Mesh"), `SphereComponent` (named "InteractionCollision")
|
||||||
|
4. Add Variable: `Config` of type `S_PickupConfig`
|
||||||
|
5. In Construction Script: read `Config.ItemData` → set Mesh to `ItemData.WorldMesh`
|
||||||
|
|
||||||
|
**Part C — Place Items in Level (repeat per item instance):**
|
||||||
|
1. Drag `BP_ItemPickup` into the level
|
||||||
|
2. Select it → Details panel → Config → Item Data → `DA_Item_MedKit`
|
||||||
|
3. Set Quantity → 1 (or more for stacked items)
|
||||||
|
4. Position the actor where you want it
|
||||||
|
|
||||||
|
> `DA_ItemData` is NOT an actor — you cannot drag it into the level. It lives in the Content Browser as a Data Asset. `BP_ItemPickup` is the physical body that references the Data Asset. See `docs/blueprints/01-core/07_DA_ItemData.md` for the full explanation.
|
||||||
|
|
||||||
### 26 — BPC_ActiveItemSystem
|
### 26 — BPC_ActiveItemSystem
|
||||||
| Aspect | Detail |
|
| Aspect | Detail |
|
||||||
@@ -357,8 +488,26 @@ These 2 systems are the central nervous system. `BPC_StateManager` must be on th
|
|||||||
| Aspect | Detail |
|
| Aspect | Detail |
|
||||||
|--------|--------|
|
|--------|--------|
|
||||||
| **C++ Status** | ✅ Full — `Source/PG_Framework/Public/Inventory/BPC_InventorySystem.h` + `.cpp` |
|
| **C++ Status** | ✅ Full — `Source/PG_Framework/Public/Inventory/BPC_InventorySystem.h` + `.cpp` |
|
||||||
| **What to Build** | ➖ **None.** Attach C++ component directly to player pawn. Set `GridWidth`, `GridHeight`, `MaxWeight` in Details panel. |
|
| **What to Build** | ➖ **None.** Attach C++ component directly to player pawn. |
|
||||||
| **Attach To** | Player pawn |
|
|
||||||
|
**Setup Steps:**
|
||||||
|
1. Open your `BP_PlayerCharacter` Blueprint
|
||||||
|
2. In the Components panel, click **Add Component** → search `BPC_InventorySystem`
|
||||||
|
3. Select the component → in Details panel:
|
||||||
|
- `Grid Width` → `8` (columns)
|
||||||
|
- `Grid Height` → `5` (rows — gives 40 total slots)
|
||||||
|
- `Max Weight` → `50.0` (set to `0` to disable weight limit)
|
||||||
|
4. **Compile → Save**
|
||||||
|
|
||||||
|
**Usage from Blueprint:**
|
||||||
|
- Get component: `Get Component by Class (BPC_InventorySystem)`
|
||||||
|
- `Add Item(ItemData, Quantity)` → returns `int32` (actual quantity added — may be less if full)
|
||||||
|
- `Can Add Item(ItemData, Quantity)` → returns `bool` (check before showing pickup prompt)
|
||||||
|
- `Get Item Count(ItemData)` → returns total quantity across all stacks
|
||||||
|
- `Sort Inventory` — native-speed C++ TArray sort with lambda
|
||||||
|
- Bind to `On Inventory Changed` dispatcher → UI widgets refresh
|
||||||
|
|
||||||
|
**Attach To** | Player pawn (alongside HealthSystem, StaminaSystem, etc. — order doesn't matter for inventory)
|
||||||
|
|
||||||
### 32 — BPC_ItemCombineSystem
|
### 32 — BPC_ItemCombineSystem
|
||||||
| Aspect | Detail |
|
| Aspect | Detail |
|
||||||
@@ -670,4 +819,4 @@ Every system has a complete spec in `docs/blueprints/` with node-by-node Manual
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Remaining Blueprint Build Order v1.0 — Companion to `cpp-blueprint-status.md` and `cpp-integration-guide.md`.*
|
*Remaining Blueprint Build Order v1.1 — Companion to `cpp-blueprint-status.md` and `cpp-integration-guide.md`.*
|
||||||
|
|||||||
72
docs/game/README.md
Normal file
72
docs/game/README.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# Game Examples — Concrete Blueprint Walkthroughs
|
||||||
|
|
||||||
|
**Directory:** `docs/game/`
|
||||||
|
**Purpose:** Step-by-step examples of building specific game items using the PG_Framework. Each document shows exactly what to create, what components to add, what Blueprint nodes to wire, and how to verify it works.
|
||||||
|
|
||||||
|
These documents are **separate from the framework** — they belong in a `Content/Game/` folder in your project, not in `Content/Framework/`. The framework provides the rules and systems; these examples show how to use them.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How to Use These Examples
|
||||||
|
|
||||||
|
Each example follows the same structure:
|
||||||
|
|
||||||
|
1. **DA_ItemData** — Create the Data Asset that defines the item's identity
|
||||||
|
2. **BP_ItemPickup** — Create the world actor that represents the item
|
||||||
|
3. **Optional: BP_UsableItem** — If the item has active-use behavior (flashlight toggle, weapon fire), create a specialized actor with `I_UsableItem`
|
||||||
|
4. **Blueprints & Wiring** — Exact node-by-node graphs with screenshots descriptions
|
||||||
|
5. **Verification** — How to test it works in PIE
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example Index
|
||||||
|
|
||||||
|
| Example | Item Type | Complexity | What You Learn |
|
||||||
|
|---------|-----------|-----------|----------------|
|
||||||
|
| [Flashlight Tool](item-flashlight.md) | `Tool` | Medium | Data Asset setup, `I_UsableItem`, `BP_ItemPickup` Construction Script, soft reference resolution, toggleable function via `I_Toggleable` |
|
||||||
|
| [Pistol Weapon](item-pistol.md) | `Weapon` | High | `Equipment Data`, `BPC_AmmoComponent`, `I_Equippable`, frame-driven fire, ammo consumption |
|
||||||
|
| [MedKit Consumable](item-medkit.md) | `Consumable` | Low | `Consumable Data`, quick-slot use, `BPC_HealthSystem` integration |
|
||||||
|
| [Keycard Key Item](item-keycard.md) | `KeyItem` | Low | `bIsKeyItem`, `I_Lockable` interaction, door unlocking |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Folder Structure in Your UE5 Project
|
||||||
|
|
||||||
|
```
|
||||||
|
Content/
|
||||||
|
├── Framework/ ← Framework systems (read-only, don't modify)
|
||||||
|
│ ├── Core/ GI_GameFramework, DA_GameTagRegistry, etc.
|
||||||
|
│ ├── Player/ BPC_HealthSystem, BPC_StateManager, etc.
|
||||||
|
│ ├── Inventory/ BP_ItemPickup base, BPC_InventorySystem
|
||||||
|
│ ├── DataAssets/ DA_ItemData, DA_EquipmentConfig, etc.
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
└── Game/ ← YOUR project content (this is what you create)
|
||||||
|
├── Items/ DA_ItemData instances (DA_Item_MedKit, DA_Item_Flashlight)
|
||||||
|
├── Weapons/ DA_ItemData weapon instances (DA_Item_Pistol)
|
||||||
|
├── Pickups/ BP_Pickup_* children (BP_Pickup_MedKit, BP_Pickup_Flashlight)
|
||||||
|
├── Actors/ BP_Door_*, BP_Puzzle_* instances
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Rule:** Never modify files in `Content/Framework/`. Create your assets in `Content/Game/` and reference framework systems via interfaces and dispatchers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reference — Framework Systems Used by These Examples
|
||||||
|
|
||||||
|
| Framework System | Asset Path | Used For |
|
||||||
|
|-----------------|-----------|----------|
|
||||||
|
| `DA_ItemData` | `Content/Framework/DataAssets/Items/` (C++ class) | Item identity definition |
|
||||||
|
| `BP_ItemPickup` | `Content/Framework/Inventory/` (base BP) | World pickup actor |
|
||||||
|
| `BPC_InventorySystem` | C++ component (auto-attach to pawn) | Add/remove/query items |
|
||||||
|
| `BPC_HealthSystem` | C++ stub → BP child on pawn | Taking/healing damage |
|
||||||
|
| `BPC_DamageReceptionSystem` | C++ component (auto-attach to pawn) | Damage pipeline |
|
||||||
|
| `BPC_ConsumableSystem` | BP child on pawn | Using consumable items |
|
||||||
|
| `BPC_EquipmentSlotSystem` | BP child on pawn | Equipping weapons/tools |
|
||||||
|
| `BPC_KeyItemSystem` | BP child on pawn | Key item unlock logic |
|
||||||
|
| `I_Interactable` | C++ interface in `I_InterfaceLibrary.h` | Interaction detection |
|
||||||
|
| `I_UsableItem` | C++ interface in `I_InterfaceLibrary.h` | Active-use items (flashlight, weapon) |
|
||||||
|
| `I_Toggleable` | C++ interface in `I_InterfaceLibrary.h` | On/off state (flashlight toggle) |
|
||||||
|
| `I_Lockable` | C++ interface in `I_InterfaceLibrary.h` | Locked doors/containers |
|
||||||
|
| `I_Damageable` | C++ interface in `I_InterfaceLibrary.h` | Receiving damage |
|
||||||
411
docs/game/item-flashlight.md
Normal file
411
docs/game/item-flashlight.md
Normal file
@@ -0,0 +1,411 @@
|
|||||||
|
# Item Example — Flashlight (Tool)
|
||||||
|
|
||||||
|
**Item Type:** `Tool`
|
||||||
|
**Complexity:** Medium
|
||||||
|
**Systems Used:** `DA_ItemData`, `BP_ItemPickup`, `I_UsableItem`, `I_Toggleable`, `BPC_InventorySystem`, `BPC_EquipmentSlotSystem`
|
||||||
|
**What You Learn:** Data Asset setup, pickup actor with soft reference mesh, Construction Script wiring, toggleable active-use item, inventory integration
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Create the DA_ItemData
|
||||||
|
|
||||||
|
### 1.1 — Create the Data Asset
|
||||||
|
|
||||||
|
```
|
||||||
|
Content Browser → Game/Items/
|
||||||
|
(create folder "Items" if needed)
|
||||||
|
Right-click → Miscellaneous → Data Asset
|
||||||
|
Class: DA_ItemData
|
||||||
|
Name: DA_Item_Flashlight
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 — Fill Core Properties
|
||||||
|
|
||||||
|
Open `DA_Item_Flashlight`:
|
||||||
|
|
||||||
|
| Property | Value | Notes |
|
||||||
|
|----------|-------|-------|
|
||||||
|
| `Item Tag` | `Framework.Item.Tool.Flashlight` | Must be registered in `DA_GameTagRegistry`. Tag hierarchy: Framework → Item → Tool → Flashlight |
|
||||||
|
| `Display Name` | "Flashlight" | Player sees this in inventory UI |
|
||||||
|
| `Description` | "A heavy-duty tactical flashlight. Click to toggle on/off." | Shown in inventory |
|
||||||
|
| `Icon` | `T_Flashlight_Icon` | Assign any texture; for prototype, use a white square |
|
||||||
|
| `World Mesh` | `SM_Flashlight` | **Soft reference** — doesn't need to be loaded at startup; Construction Script resolves it |
|
||||||
|
| `Weight` | `1.5` | Carried weight in your carry limit |
|
||||||
|
| `Stack Limit` | `1` | Cannot stack multiple flashlights |
|
||||||
|
| `Item Type` | `Tool` | Determines that `Equipment Data` panel shows |
|
||||||
|
|
||||||
|
### 1.3 — Fill Equipment Data
|
||||||
|
|
||||||
|
Scroll down — the `Equipment Data` panel appears because `ItemType == Tool`:
|
||||||
|
|
||||||
|
| Field | Value | Notes |
|
||||||
|
|-------|-------|-------|
|
||||||
|
| `Slot` | `Framework.Equipment.Slot.Tool` | Which equipment slot it goes into |
|
||||||
|
| `Damage` | `0` | Not a weapon |
|
||||||
|
| `Fire Rate` | `0` | Not a weapon |
|
||||||
|
| `Range` | `0` | N/A |
|
||||||
|
| `Magazine Size` | `0` | N/A |
|
||||||
|
| `Reload Time` | `0` | N/A |
|
||||||
|
|
||||||
|
### 1.4 — Save
|
||||||
|
|
||||||
|
File → Save (or Ctrl+S). The Data Asset is ready.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Create the BP_Pickup_Flashlight (World Pickup Actor)
|
||||||
|
|
||||||
|
This is the actor the player sees in the level. It reads the Data Asset and represents it in the world.
|
||||||
|
|
||||||
|
### 2.1 — Create Blueprint
|
||||||
|
|
||||||
|
```
|
||||||
|
Content Browser → Game/Pickups/
|
||||||
|
(create folder "Pickups" if needed)
|
||||||
|
Right-click → Blueprint Class → Actor
|
||||||
|
Name: BP_Pickup_Flashlight
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 — Add Components
|
||||||
|
|
||||||
|
Open `BP_Pickup_Flashlight` → **Viewport** tab.
|
||||||
|
|
||||||
|
**Add Component** button (top-left of the Components panel):
|
||||||
|
|
||||||
|
| Order | Component Class | Name | Purpose |
|
||||||
|
|-------|----------------|------|---------|
|
||||||
|
| 1 | `StaticMeshComponent` | `Mesh` | Renders the flashlight 3D model in the world |
|
||||||
|
| 2 | `SphereComponent` | `PickupTrigger` | Detects when player is close enough to interact |
|
||||||
|
| 3 | *(Optional)* `PointLightComponent` | `BatteryIndicator` | Small glow showing the pickup has charge |
|
||||||
|
|
||||||
|
**Select `PickupTrigger` → Details:**
|
||||||
|
- `Sphere Radius` → `200.0`
|
||||||
|
- **Collision → Collision Presets** → `OverlapOnlyPawn`
|
||||||
|
|
||||||
|
**Select `Mesh` → Details:**
|
||||||
|
- **Collision → Collision Enabled** → `No Collision` (the sphere handles interaction; mesh collision is unnecessary)
|
||||||
|
|
||||||
|
Your Components panel should look like:
|
||||||
|
|
||||||
|
```
|
||||||
|
BP_Pickup_Flashlight (self)
|
||||||
|
├── DefaultSceneRoot
|
||||||
|
├── Mesh ← StaticMeshComponent
|
||||||
|
├── PickupTrigger ← SphereComponent (radius 200)
|
||||||
|
└── BatteryIndicator ← PointLightComponent (optional)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 — Create the ItemData Variable
|
||||||
|
|
||||||
|
In the **My Blueprint** panel (left side):
|
||||||
|
|
||||||
|
1. Click **+ Variable** → name: `ItemData` → type: **DA_ItemData → Object Reference**
|
||||||
|
2. Select the variable → Details:
|
||||||
|
- **Instance Editable** → ✓
|
||||||
|
- **Category** → `Pickup`
|
||||||
|
- **Tooltip** → "The item Data Asset this pickup represents"
|
||||||
|
|
||||||
|
This is simpler than using a struct for a single item — you can expand to a struct later if you need more fields.
|
||||||
|
|
||||||
|
### 2.4 — Wire the Construction Script
|
||||||
|
|
||||||
|
Switch to the **Construction Script** tab (or open the graph). This runs when the blueprint is compiled or when you change `ItemData` in the editor.
|
||||||
|
|
||||||
|
```
|
||||||
|
[Construction Script]
|
||||||
|
│
|
||||||
|
├─ ItemData (drag from My Blueprint → Get ItemData)
|
||||||
|
│
|
||||||
|
├─ Is Valid? (ItemData)
|
||||||
|
│ │
|
||||||
|
│ ├─ Branch (Condition = IsValid result)
|
||||||
|
│ │
|
||||||
|
│ ├─ True:
|
||||||
|
│ │ │
|
||||||
|
│ │ ├─ ItemData → Get World Mesh
|
||||||
|
│ │ │ This returns a TSoftObjectPtr<UStaticMesh>
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ └─ To Soft Object Reference (conversion node, or just drag from pin)
|
||||||
|
│ │ │ └─ Resolve Soft Reference (or "Async Load Asset")
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ ├─ On Complete (if async) → Set Static Mesh (Mesh component, New Mesh = loaded asset)
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ └─ For Construction Script, use "Load Synchronous" to see it in editor immediately:
|
||||||
|
│ │ │ └─ Sync Load Asset (from soft ref) → Set Static Mesh (Mesh)
|
||||||
|
│ │ │
|
||||||
|
│ │ ├─ ItemData → Get Display Name → Set Actor Label
|
||||||
|
│ │ │ (This renames the actor in the world to "Flashlight" so it's easy to find in the World Outliner)
|
||||||
|
│ │ │
|
||||||
|
│ │ └─ ItemData → Get Weight → (optional: store for physics if dropped)
|
||||||
|
│ │
|
||||||
|
│ └─ False:
|
||||||
|
│ └─ Print String ("No ItemData assigned to BP_Pickup_Flashlight")
|
||||||
|
│ └─ Only if Editor (not PIE/standalone)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Exact node sequence:**
|
||||||
|
|
||||||
|
| Step | Right-click / Drag | Search | Connect |
|
||||||
|
|------|-------------------|--------|---------|
|
||||||
|
| 1 | Drag `ItemData` variable → Get | — | — |
|
||||||
|
| 2 | Drag from ItemData pin | `Is Valid` | Branch Condition |
|
||||||
|
| 3 | True branch → drag from ItemData | `Get World Mesh` | — |
|
||||||
|
| 4 | Drag from World Mesh pin | `Load Synchronous` | — |
|
||||||
|
| 5 | Drag `Mesh` component from Components → Get | — | Target of Set Static Mesh |
|
||||||
|
| 6 | Drag from Load Synchronous Return Value | `Set Static Mesh` (New Mesh) | — |
|
||||||
|
| 7 | Drag from ItemData → `Get Display Name` | — | To String (Auto) |
|
||||||
|
| 8 | Drag from string result | `Set Actor Label` → New Actor Label | — |
|
||||||
|
|
||||||
|
### 2.5 — Add I_Interactable Interface
|
||||||
|
|
||||||
|
1. **Class Settings** (toolbar button)
|
||||||
|
2. **Implemented Interfaces** → **Add** → search and select `UInteractable`
|
||||||
|
3. **Compile** (blue checkmark). Now these events appear:
|
||||||
|
|
||||||
|
#### Event Interact
|
||||||
|
|
||||||
|
```
|
||||||
|
Event Interact (Interface -> UInteractable)
|
||||||
|
│ Interactor: AActor*
|
||||||
|
│ Return Node expects a Boolean
|
||||||
|
│
|
||||||
|
├─ Interactor → Get Component by Class → Class: UBPC_InventorySystem
|
||||||
|
│ │
|
||||||
|
│ └─ Is Valid?
|
||||||
|
│ ├─ False → Return False (no inventory on this actor)
|
||||||
|
│ │
|
||||||
|
│ └─ True → [InventorySystem ref]
|
||||||
|
│ │
|
||||||
|
│ ├─ ItemData → Is Valid?
|
||||||
|
│ │ └─ False → Return False
|
||||||
|
│ │
|
||||||
|
│ ├─ [InventorySystem] → Can Add Item
|
||||||
|
│ │ ├─ Item: ItemData
|
||||||
|
│ │ ├─ Quantity: 1
|
||||||
|
│ │ └─ Result → Branch
|
||||||
|
│ │ ├─ True:
|
||||||
|
│ │ │ ├─ [InventorySystem] → Add Item (Item=ItemData, Quantity=1)
|
||||||
|
│ │ │ │ → Result (int32, ignore)
|
||||||
|
│ │ │ ├─ Destroy Actor
|
||||||
|
│ │ │ └─ Return True
|
||||||
|
│ │ └─ False:
|
||||||
|
│ │ ├─ Print String ("Inventory full or cannot carry")
|
||||||
|
│ │ └─ Return False
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Event Can Interact
|
||||||
|
|
||||||
|
```
|
||||||
|
Event Can Interact (Interface -> UInteractable)
|
||||||
|
│ Interactor: AActor*
|
||||||
|
│ OutBlockReason: FText& (by reference)
|
||||||
|
│ Return Node expects Boolean
|
||||||
|
│
|
||||||
|
├─ ItemData → Is Valid?
|
||||||
|
│ ├─ False → OutBlockReason = "No item data configured" → Return False
|
||||||
|
│ └─ True → Return True
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Event Get Interaction Prompt
|
||||||
|
|
||||||
|
```
|
||||||
|
Event Get Interaction Prompt (Interface -> UInteractable)
|
||||||
|
│ Return Node expects FText
|
||||||
|
│
|
||||||
|
├─ ItemData → Get Display Name
|
||||||
|
├─ Format Text: "Pick up {0}" (format with DisplayName)
|
||||||
|
└─ Return the formatted text
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.6 — Optional: Bobbing & Rotation
|
||||||
|
|
||||||
|
In **Event Graph → Event BeginPlay**:
|
||||||
|
|
||||||
|
```
|
||||||
|
Event BeginPlay
|
||||||
|
│
|
||||||
|
├─ Mesh → Set Collision Enabled → No Collision
|
||||||
|
├─ PickupTrigger → Set Collision Enabled → Query Only
|
||||||
|
│
|
||||||
|
├─ [Create a Timeline named "FloatAnim"]
|
||||||
|
│ │
|
||||||
|
│ ├─ Float Track: 0.0 to 1.0, length 2.0 seconds, Looping ✓
|
||||||
|
│ │
|
||||||
|
│ └─ Timeline → Update pin:
|
||||||
|
│ │
|
||||||
|
│ ├─ Mesh → Add World Rotation
|
||||||
|
│ │ ├─ Delta Rotation: (0, 0, TimelineOutput * 90.0)
|
||||||
|
│ │ └─ Sweep: false, Teleport: false
|
||||||
|
│ │
|
||||||
|
│ └─ Mesh → Set World Location (interpolation)
|
||||||
|
│ └─ Z = ActorLocation.Z + sin(TimelineOutput * 2π) * 5.0
|
||||||
|
│ (Use "Make Vector" → Break Actor Location → modify Z → "Lerp" or direct set)
|
||||||
|
│
|
||||||
|
└─ Play Timeline from Start
|
||||||
|
```
|
||||||
|
|
||||||
|
> For the bobbing math: `sin(TimelineOutput * 6.28318) * 5.0` gives a smooth ±5 unit bounce.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Create the BP_Flashlight (Active-Use Actor — Toggleable)
|
||||||
|
|
||||||
|
This is a separate Blueprint for the *held* flashlight — what the player actually uses after picking it up. It implements `I_UsableItem` and `I_Toggleable`.
|
||||||
|
|
||||||
|
### 3.1 — Create Blueprint
|
||||||
|
|
||||||
|
```
|
||||||
|
Content Browser → Game/Actors/
|
||||||
|
Right-click → Blueprint Class → Actor
|
||||||
|
Name: BP_Flashlight
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 — Add Components
|
||||||
|
|
||||||
|
| # | Component | Name | Purpose |
|
||||||
|
|---|-----------|------|---------|
|
||||||
|
| 1 | `StaticMeshComponent` | `BodyMesh` | Flashlight body |
|
||||||
|
| 2 | `SpotLightComponent` | `LightBeam` | The actual light cone |
|
||||||
|
| 3 | `AudioComponent` | `ClickSound` | Plays the toggle click sound |
|
||||||
|
|
||||||
|
Select `LightBeam` → Details:
|
||||||
|
- `Intensity` → `5000.0` (bright)
|
||||||
|
- `Inner Cone Angle` → `15.0`
|
||||||
|
- `Outer Cone Angle` → `30.0`
|
||||||
|
- **Visible** → false (starts off — player toggles it on)
|
||||||
|
|
||||||
|
### 3.3 — Add Toggle State Variable
|
||||||
|
|
||||||
|
**+ Variable** → Name: `bIsOn` → Type: `Boolean` → Default: `false`
|
||||||
|
- **Replication** → `Replicated` (if multiplayer)
|
||||||
|
|
||||||
|
### 3.4 — Add Interfaces
|
||||||
|
|
||||||
|
Class Settings → Interfaces → Add:
|
||||||
|
- `UUsableItem` — so the player can select and use it from inventory
|
||||||
|
- `UToggleable` — so the player can toggle it on/off
|
||||||
|
|
||||||
|
### 3.5 — Wire I_Toggleable
|
||||||
|
|
||||||
|
```
|
||||||
|
Event Toggle (Interface -> UToggleable)
|
||||||
|
│ Toggler: AActor*
|
||||||
|
│
|
||||||
|
├─ bIsOn = NOT bIsOn (FlipFlop or Boolean NOT)
|
||||||
|
│
|
||||||
|
├─ Branch (bIsOn)
|
||||||
|
│ ├─ True:
|
||||||
|
│ │ ├─ LightBeam → Set Visibility (true)
|
||||||
|
│ │ ├─ ClickSound → Play
|
||||||
|
│ │ └─ (Optional) Play toggle-on animation montage
|
||||||
|
│ │
|
||||||
|
│ └─ False:
|
||||||
|
│ ├─ LightBeam → Set Visibility (false)
|
||||||
|
│ └─ ClickSound → Play
|
||||||
|
|
||||||
|
Event Set State (Interface -> UToggleable)
|
||||||
|
│ bNewState: Boolean, Setter: AActor*
|
||||||
|
│
|
||||||
|
├─ Set bIsOn = bNewState
|
||||||
|
├─ LightBeam → Set Visibility (bNewState)
|
||||||
|
└─ (Don't play click sound — this is programmatic, not player toggle)
|
||||||
|
|
||||||
|
Event Get Current State
|
||||||
|
│ Return Boolean
|
||||||
|
└─ Return bIsOn
|
||||||
|
|
||||||
|
Event Get State Label
|
||||||
|
│ Return FText
|
||||||
|
└─ Format Text: "Flashlight is {0}" → format with (bIsOn ? "ON" : "OFF")
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.6 — Wire I_UsableItem
|
||||||
|
|
||||||
|
```
|
||||||
|
Event Use Item (Interface -> UUsableItem)
|
||||||
|
│ User: AActor*, Target: AActor*
|
||||||
|
│ Return Boolean
|
||||||
|
│
|
||||||
|
├─ Call Toggle (self, User) ← reuses the I_Toggleable logic
|
||||||
|
└─ Return True
|
||||||
|
|
||||||
|
Event Can Use Item
|
||||||
|
│ User, Target
|
||||||
|
│ Return Boolean
|
||||||
|
└─ Return True (can always toggle flashlight)
|
||||||
|
|
||||||
|
Event Get Use Duration
|
||||||
|
│ Return Float
|
||||||
|
└─ Return 0.0 (instant toggle)
|
||||||
|
|
||||||
|
Event On Item Used
|
||||||
|
│ User: AActor*
|
||||||
|
│
|
||||||
|
└─ (Optional) Play a small haptic pulse on controller
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.7 — Compile & Save
|
||||||
|
|
||||||
|
The held flashlight is ready. When a player picks up `BP_Pickup_Flashlight` and the inventory's `BPC_ActiveItemSystem` routes the item to use, it calls `I_UsableItem.UseItem()` on this actor, which toggles the light.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Wire Inventory Integration
|
||||||
|
|
||||||
|
### 4.1 — In Your Player Pawn BP
|
||||||
|
|
||||||
|
When `BPC_ActiveItemSystem` detects that an item of type `Tool` is used:
|
||||||
|
1. Get the equipped item from `BPC_EquipmentSlotSystem`
|
||||||
|
2. Spawn `BP_Flashlight` actor (if not already spawned)
|
||||||
|
3. Call `I_UsableItem.UseItem()`
|
||||||
|
4. The flashlight actor handles its own toggle state
|
||||||
|
|
||||||
|
### 4.2 — Quick Test Without Full Integration
|
||||||
|
|
||||||
|
For a rapid prototype, add this to your **PlayerCharacter's Event Graph**:
|
||||||
|
|
||||||
|
```
|
||||||
|
Event InputAction (IA_UseItem, Pressed)
|
||||||
|
│
|
||||||
|
├─ Branch (bFlashlightEquipped) ← set this bool when flashlight is picked up
|
||||||
|
│ ├─ True:
|
||||||
|
│ │ ├─ Get All Actors of Class (BP_Flashlight)
|
||||||
|
│ │ ├─ For Each → Call I_Toggleable.Toggle
|
||||||
|
│ │ └─ (In production, use BPC_ActiveItemSystem instead of this crude search)
|
||||||
|
│ └─ False: do nothing
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Verification Checklist
|
||||||
|
|
||||||
|
**Step 1 — Data Asset:** Open `DA_Item_Flashlight` → all fields filled, no blank properties.
|
||||||
|
|
||||||
|
**Step 2 — Pickup in Editor:** Drag `BP_Pickup_Flashlight` into level → set `ItemData` → the mesh appears in viewport immediately (Construction Script).
|
||||||
|
|
||||||
|
**Step 3 — Pickup Interaction (PIE):**
|
||||||
|
- [ ] Walk up to flashlight pickup → interaction prompt appears ("Pick up Flashlight")
|
||||||
|
- [ ] Press Interact → flashlight disappears from world
|
||||||
|
- [ ] Check log/print: `BPC_InventorySystem.AddItem` was called
|
||||||
|
- [ ] In debug: `Get All Items` on inventory → flashlight is in a slot
|
||||||
|
|
||||||
|
**Step 4 — Held Use:**
|
||||||
|
- [ ] Toggle via IA_UseItem → light turns on (visible in world)
|
||||||
|
- [ ] Toggle again → light turns off
|
||||||
|
- [ ] Click sound plays on each toggle
|
||||||
|
|
||||||
|
**Step 5 — Equipment Slot:**
|
||||||
|
- [ ] Flashlight occupies `Framework.Equipment.Slot.Tool` slot
|
||||||
|
- [ ] Cannot equip another tool while flashlight is in slot (slot type validation)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Common Issues
|
||||||
|
|
||||||
|
| Issue | Cause | Fix |
|
||||||
|
|-------|-------|-----|
|
||||||
|
| Mesh doesn't appear in Construction Script | Soft reference not loaded | Use `Load Synchronous` instead of `Async Load Asset` in Construction Script |
|
||||||
|
| Pickup doesn't respond to interaction | `UInteractable` interface not added | Class Settings → Interfaces → Add `UInteractable` → Compile |
|
||||||
|
| "Can Add Item" always returns false | `BPC_InventorySystem.MaxWeight` too low or `GridWidth/Height = 0` | Set MaxWeight to 0 (unlimited) for testing, or increase it |
|
||||||
|
| Light doesn't toggle | `bIsOn` variable not replicated or `I_Toggleable` not wired | Verify `Toggle` event is called; check `Set Visibility` target is the correct light component |
|
||||||
|
| "No ItemData configured" error | Forgot to set `ItemData` on the pickup instance in the level | Select the pickup → Details → set ItemData → recompile |
|
||||||
Reference in New Issue
Block a user