Files
UE5-Modular-Game-Framework/docs/game/item-keycard.md

263 lines
8.7 KiB
Markdown

# Item Example — Keycard (Key Item)
**Item Type:** `KeyItem`
**Complexity:** Low
**Systems Used:** `DA_ItemData`, `BP_ItemPickup`, `BPC_InventorySystem`, `BPC_KeyItemSystem`, `I_Lockable`, `BP_DoorActor`
**What You Learn:** Key item Data Asset, auto-protection from drop/clear, used-once pattern, door unlocking via `I_Lockable`
---
## 1. Create the DA_ItemData
### 1.1 — Create the Data Asset
```
Content Browser → Game/Items/
Right-click → Miscellaneous → Data Asset
Class: DA_ItemData
Name: DA_Item_Keycard_Omega
```
### 1.2 — Fill Properties
| Property | Value | Notes |
|----------|-------|-------|
| `Item Tag` | `Framework.Item.KeyItem.KeycardOmega` | Unique — registered in `DA_GameTagRegistry` |
| `Display Name` | "Omega Keycard" | Player sees this |
| `Description` | "A high-security keycard labeled 'Omega Wing.'" | Shown in journal/inventory |
| `Icon` | `T_Keycard_Icon` | Card-shaped texture |
| `World Mesh` | `SM_Keycard` | Flat card mesh |
| `Weight` | `0.1` | Very light |
| `Stack Limit` | `1` | One per slot — cannot stack keycards |
| `Item Type` | `KeyItem` | Key items are auto-protected |
| `b Is Key Item` | ✓ (checked) | This property also appears — it's a boolean flag |
| `b Can Be Dropped` | ✗ (unchecked) | Key items should NOT be droppable (auto-forced by `bIsKeyItem`) |
### 1.3 — Save
No sub-data needed. Key items don't have Equipment or Consumable data.
---
## 2. Create the BP_Pickup_KeycardOmega
Same pattern as other pickups:
### 2.1 — Create Blueprint
```
Content Browser → Game/Pickups/
Right-click → Blueprint Class → Actor
Name: BP_Pickup_KeycardOmega
```
### 2.2 — Components
| # | Component | Name | Purpose |
|---|-----------|------|---------|
| 1 | `StaticMeshComponent` | `Mesh` | Card model |
| 2 | `SphereComponent` | `PickupTrigger` | Radius 150, `OverlapOnlyPawn` |
### 2.3 — Variable
`ItemData` → Type: `DA_ItemData → Object Reference` → Instance Editable ✓
### 2.4 — Construction Script (same pattern)
```
ItemData → Get World Mesh → Load Synchronous → Set Static Mesh (Mesh)
ItemData → Get Display Name → Set Actor Label
```
### 2.5 — I_Interactable Wiring (same pattern)
```
Interactor → Get BPC_InventorySystem → CanAddItem(ItemData, 1)
True → AddItem → Destroy Actor → Return True
False → Print "Inventory Full" → Return False
```
---
## 3. Create the Lockable Door
### 3.1 — Create Door Blueprint
```
Content Browser → Game/Actors/
Right-click → Blueprint Class → Actor (or BP_DoorActor from framework)
Name: BP_Door_OmegaWing
```
### 3.2 — Add Components
| # | Component | Name | Purpose |
|---|-----------|------|---------|
| 1 | `StaticMeshComponent` | `DoorFrame` | The door frame (static) |
| 2 | `StaticMeshComponent` | `DoorPanel` | The moving door part |
| 3 | `BoxComponent` | `InteractionTrigger` | Door interaction range |
### 3.3 — Add Interfaces
Class Settings → Interfaces → Add:
- `UInteractable`
- `ULockable`
### 3.4 — Variables
| Variable | Type | Default | Purpose |
|----------|------|---------|---------|
| `bIsLocked` | Boolean | `true` | Door starts locked |
| `bIsOpen` | Boolean | `false` | Door starts closed |
| `RequiredKeyTag` | GameplayTag | `Framework.Item.KeyItem.KeycardOmega` | Which key unlocks this |
### 3.5 — Wire I_Lockable
```
Event Try Unlock (Interface -> ULockable)
│ Unlocker: AActor*, KeyTag: FGameplayTag
│ Return Boolean
├─ bIsLocked? → Branch
│ ├─ True:
│ │ ├─ KeyTag == RequiredKeyTag? → Branch
│ │ │ ├─ True:
│ │ │ │ ├─ Set bIsLocked = false
│ │ │ │ ├─ Unlocker → Get BPC_KeyItemSystem
│ │ │ │ │ └─ Consume Key (KeyTag) ← removes key from inventory
│ │ │ │ ├─ Print "Door unlocked with Omega Keycard"
│ │ │ │ └─ Return True
│ │ │ │
│ │ │ └─ False:
│ │ │ ├─ Print "Wrong key — requires Omega Keycard"
│ │ │ └─ Return False
│ │ │
│ │ └─ False: (already unlocked)
│ │ └─ Return True
Event Is Locked
│ Return Boolean
└─ Return bIsLocked
Event Get Required Key Tag
│ Return FGameplayTag
└─ Return RequiredKeyTag
Event Try Lock
│ Locker: AActor*
│ Return Boolean
└─ Set bIsLocked = true → Return True (re-lockable for puzzle purposes)
```
### 3.6 — Wire I_Interactable (on door)
```
Event Interact (Interface -> UInteractable)
│ Interactor: AActor*
│ Return Boolean
├─ bIsLocked? → Branch
│ ├─ True:
│ │ ├─ Interactor → Get BPC_KeyItemSystem
│ │ │ └─ Has Key(RequiredKeyTag)?
│ │ │ ├─ True:
│ │ │ │ ├─ Call Try Unlock (self, Interactor, RequiredKeyTag)
│ │ │ │ └─ (If unlocked) → Open Door (see below)
│ │ │ │
│ │ │ └─ False:
│ │ │ ├─ Print "Door is locked. Find Omega Keycard."
│ │ │ └─ Return False
│ │ │
│ │ └─ Return False
│ │
│ └─ False: (door is unlocked)
│ ├─ bIsOpen? → Branch
│ │ ├─ True → Close Door → Return True
│ │ └─ False → Open Door → Return True
Function: Open Door
├─ Timeline (Rotation track, 0 → OpenAngle over OpenDuration)
│ └─ Update: DoorPanel → Set Relative Rotation
├─ Set bIsOpen = true
└─ (Optional) Play door creak sound
Function: Close Door
├─ Timeline (Rotation track, OpenAngle → 0 over CloseDuration)
│ └─ Update: DoorPanel → Set Relative Rotation
├─ Set bIsOpen = false
└─ (Optional) Play door close sound
```
---
## 4. Wire BPC_KeyItemSystem (On Player Pawn)
The `BPC_KeyItemSystem` tracks which key items the player holds. It automates:
- Protecting key items from being dropped/cleared
- Validating key item requirements on door/lock interactions
- Removing consumed key items after use
In your BP child of `BPC_KeyItemSystem`:
```
Function: Has Key (KeyTag: GameplayTag) → Boolean
├─ Get Owner → Get BPC_InventorySystem
├─ Get All Items → For Each Loop
│ ├─ Item → Get bIsKeyItem → Branch
│ │ ├─ True: Item → Get ItemTag == KeyTag? → Return True
│ │ └─ False: Continue
│ │
│ └─ Loop end → Return False
Function: Consume Key (KeyTag: GameplayTag)
├─ Get Owner → Get BPC_InventorySystem
├─ Has Key(KeyTag)? → Branch
│ ├─ True: Inventory → Remove Item (by KeyTag, Quantity=1)
│ └─ False: Print "No such key in inventory"
```
> In the full framework, `BPC_KeyItemSystem` also prevents `Clear Inventory` and `Drop` operations from affecting key items by checking `bIsKeyItem` on the Data Asset.
---
## 5. Verification Checklist
**Step 1 — Pickup:**
- [ ] `BP_Pickup_KeycardOmega` placed in level, mesh visible
- [ ] Walk up → prompt "Pick up Omega Keycard" → press Interact → card goes to inventory
**Step 2 — Inventory Protection:**
- [ ] Keycard appears in inventory with "KEY" badge/indicator
- [ ] Try to drop keycard → blocked (cannot drop key items)
- [ ] Try "Clear Inventory" → keycard remains (protected)
**Step 3 — Door Interaction:**
- [ ] Walk up to `BP_Door_OmegaWing` while locked → prompt "Door is locked"
- [ ] Interact → door stays closed, shows "Requires Omega Keycard"
- [ ] Pick up keycard → walk to door → interact → door unlocks, keycard consumed from inventory
- [ ] Interact again → door opens
**Step 4 — Wrong Key:**
- [ ] Create a `DA_Item_Keycard_Beta` with a different tag
- [ ] Pick it up, try on Omega door → "Wrong key — requires Omega Keycard"
- [ ] Keycard_Beta stays in inventory (not consumed on wrong door)
---
## 6. KeyItem vs Regular Item — What's Different
| Behavior | Regular Item | Key Item (`bIsKeyItem = true`) |
|----------|-------------|-------------------------------|
| Can be dropped? | Yes (if `bCanBeDropped`) | No (auto-protected) |
| Can be cleared (death/new game)? | Yes | No (preserved across death) |
| Stacks | Yes (up to StackLimit) | Usually StackLimit=1 |
| Appears in inventory? | Yes | Yes, with KEY indicator |
| Consumed on use? | Depends on type | Yes (used once, then removed) |
| Sells to vendor? | Yes | No (protected from sale) |
Key items exist specifically to **gate progression**. They're a player's permanent proof of having solved a puzzle, explored an area, or defeated a boss. The framework ensures they can't be accidentally lost.