# 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 │ │ │ │ │ │ │ └─ 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 |