15 KiB
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):
- Click + Variable → name:
ItemData→ type: DA_ItemData → Object Reference - 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
- Class Settings (toolbar button)
- Implemented Interfaces → Add → search and select
UInteractable - 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.0gives 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.0Outer 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 inventoryUToggleable— 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:
- Get the equipped item from
BPC_EquipmentSlotSystem - Spawn
BP_Flashlightactor (if not already spawned) - Call
I_UsableItem.UseItem() - 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.AddItemwas called - In debug:
Get All Itemson 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.Toolslot - 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 |