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

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_FlashlightViewport 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 Radius200.0
  • Collision → Collision PresetsOverlapOnlyPawn

Select Mesh → Details:

  • Collision → Collision EnabledNo 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 → ✓
    • CategoryPickup
    • 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 InterfacesAdd → 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:

  • Intensity5000.0 (bright)
  • Inner Cone Angle15.0
  • Outer Cone Angle30.0
  • Visible → false (starts off — player toggles it on)

3.3 — Add Toggle State Variable

+ Variable → Name: bIsOn → Type: Boolean → Default: false

  • ReplicationReplicated (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