Skip to content

Step 5: PowerUp Shop Guide

Can TATAR edited this page Feb 23, 2026 · 1 revision

PowerUp Shop System - Quickstart Guide

Table of Contents

  1. Overview
  2. Quick Start
  3. Creating PowerUp Data Assets
  4. Shop System Component Setup
  5. Creating Shop UI Widget
  6. Listening to Events
  7. Global Functions (Easy UI)
  8. Vampire Survivors Features
  9. Example Usage Scenarios
  10. Troubleshooting

Overview

PowerUp Shop System allows players to purchase upgrades with persistent currency. Designed for Vampire Survivors-style roguelike games.

Features

  • ✅ Event-driven Blueprint UI integration
  • ✅ Automatic shop refresh (on currency/PowerUp changes)
  • ✅ Global static functions (easy UI access)
  • ✅ Refund system (refund all PowerUps)
  • ✅ Disable system (disable max level PowerUps)
  • ✅ Cost scaling (BaseCost * CostScaling ^ Level)
  • ✅ Save/Load support

System Components

  • URFPowerUpDataAsset: PowerUp definitions (stats, cost, max level)
  • URFPowerUpSubsystem: PowerUp state management and persistence
  • URFCurrencySubsystem: Currency management
  • URFPowerUpShopSystem: Shop UI integration (Component)

Quick Start

Step 1: Create PowerUp Data Asset

  1. Right-click in Content Browser → Miscellaneous → Data Asset
  2. Select URFPowerUpDataAsset
  3. Name it (e.g., DA_PowerUp_Might)
  4. Open asset and configure settings

Step 2: Add Shop System Component

  1. Open Player Pawn Blueprint
  2. Add Component → Search URFPowerUpShopSystem and add
  3. Configure component settings (Currency Type, Debug Logging)

Step 3: Create Shop UI Widget

  1. Create User Interface → Widget Blueprint
  2. Design UI to display shop items
  3. Listen to shop events in Event Graph
  4. Bind purchase buttons

Step 4: Open Shop

Call Open Shop or Open Shop (Easy UI) function in Blueprint.


Creating PowerUp Data Assets

Basic Settings

Identity

PowerUpID: "Might" (Must be unique!)
DisplayName: "Might"
Description: "Increases damage dealt by 10% per level"
Icon: [Select Texture2D]
Category: Stats / Utility / Special

Cost

BaseCost: 100 (First level cost)
CostScaling: 1.5 (50% increase per level)
MaxLevel: 10 (Maximum level)

Cost Formula: Cost = BaseCost * (CostScaling ^ CurrentLevel)

Example Costs (BaseCost=100, CostScaling=1.5):

  • Level 1: 100
  • Level 2: 150
  • Level 3: 225
  • Level 4: 338
  • Level 5: 506

Stats

StatType: Might / Armor / MaxHealth / MoveSpeed / Area / etc.
ValuePerLevel: 0.1 (Add +0.1 per level)
ValueType: Additive / Multiplicative

Value Type Explanation:

  • Additive: FinalStat = BaseStat + (Value * Level)
    • Example: MaxHealth, Level 5 → BaseStat + (50 * 5) = +250 HP
  • Multiplicative: FinalStat = BaseStat * (1 + Value * Level)
    • Example: Might, Level 5 → BaseStat * (1 + 0.1 * 5) = 150% damage

PowerUp Types

Combat Stats

  • Might: Damage multiplier (recommended: 0.1, Multiplicative)
  • Armor: Damage reduction (recommended: 5, Additive)
  • MaxHealth: Maximum health (recommended: 50, Additive)
  • Recovery: Health regeneration (recommended: 0.5, Additive)
  • MoveSpeed: Movement speed (recommended: 10, Additive)

Weapon Stats

  • Area: Weapon area multiplier (recommended: 0.1, Multiplicative)
  • Speed: Projectile speed (recommended: 50, Additive)
  • Duration: Weapon duration (recommended: 0.5, Additive)
  • Cooldown: Cooldown reduction (recommended: 0.05, Multiplicative)
  • Amount: Projectile count (recommended: 1, Additive)

Progression Stats

  • Growth: XP multiplier (recommended: 0.1, Multiplicative)
  • Greed: Gold multiplier (recommended: 0.1, Multiplicative)
  • Magnet: Pickup radius (recommended: 50, Additive)
  • Luck: Critical and drop rate (recommended: 5, Additive)

Example PowerUp Data Asset

DA_PowerUp_Might:

PowerUpID: "Might"
DisplayName: "Might"
Description: "Increases all damage by 10% per level"
Icon: T_Icon_Might
Category: Stats
BaseCost: 100
CostScaling: 1.5
MaxLevel: 10
StatType: Might
ValuePerLevel: 0.1
ValueType: Multiplicative

Shop System Component Setup

Adding Component

  1. Open Player Pawn Blueprint (e.g., BP_TestPlayer)
  2. In Components panel, click Add → Search URFPowerUpShopSystem
  3. Add component

Component Settings

Settings

  • Currency Type: Gold (must match currency type in URFCurrencySubsystem)

Debug

  • Enable Debug Logging: true (enable during development)

Registering PowerUps

PowerUp system works intelligently:

Automatic Mode (Default - Simple)

If you don't add anything in Game Instance, system automatically finds ALL PowerUps.

How it works:

No PowerUp addition in Game Instance
  ↓
System automatically finds ALL PowerUp Data Assets
  ↓
ALL PowerUps appear in shop

Advantages:

  • ✅ Zero configuration
  • ✅ Fast prototyping
  • ✅ New PowerUps automatically appear

Usage:

Do nothing, system works automatically!

Manual Mode (Advanced - Controlled)

If you add PowerUps in Game Instance, system uses ONLY added PowerUps.

How it works:

Create PowerUp list in Game Instance
  ↓
System uses ONLY added PowerUps
  ↓
ONLY added PowerUps appear in shop

In Game Instance Blueprint (e.g., BP_URFGameInstance):

Event Init
  ↓
Get PowerUp Subsystem
  ↓
Add PowerUp Data Array
  ├─ DA_PowerUp_Might
  ├─ DA_PowerUp_Armor
  ├─ DA_PowerUp_MaxHealth
  └─ ... (only PowerUps you want)

Advantages:

  • ✅ Full control over which PowerUps are active
  • ✅ Can hide test PowerUps
  • ✅ Control PowerUp ordering
  • ✅ Different PowerUp sets for different game modes

Summary:

  • No addition in Game Instance → Automatic discovery (ALL PowerUps)
  • Addition in Game Instance → Manual control (ONLY added ones)

Creating Shop UI Widget

Widget Structure

Basic UI Elements

  1. Shop Panel (Canvas Panel / Overlay)
  2. Currency Display (Text Block) - "Gold: 1250"
  3. Shop Item List (Scroll Box / Vertical Box)
  4. Shop Item Widget (Repeating widget for each PowerUp)
  5. Close Button
  6. Refund Button (optional)

Shop Item Widget (WBP_ShopItem)

Variables

ShopItemData: FURFPowerUpShopItem (Instance Editable: false)

UI Elements

  • Icon (Image)
  • Name (Text Block)
  • Description (Text Block)
  • Level (Text Block) - "Level: 5/10"
  • Cost (Text Block) - "Cost: 225 Gold"
  • Buy Button (Button)
  • Max Level Indicator (Text Block / Image) - "MAX LEVEL"
  • Disabled Indicator (Image / Overlay) - Show if disabled

Shop Item Widget - Event Graph

Function: Update Shop Item

Input: Shop Item Data (FURFPowerUpShopItem)
  ↓
Set Icon (ShopItemData.Icon)
  ↓
Set Name (ShopItemData.DisplayName)
  ↓
Set Description (ShopItemData.Description)
  ↓
Set Level Text (Format: "Level: {CurrentLevel}/{MaxLevel}")
  ↓
Set Cost Text (Format: "Cost: {CurrentCost} Gold")
  ↓
Branch: Is Max Level?
  ├─ True: Hide Buy Button, Show "MAX LEVEL"
  └─ False: Show Buy Button
  ↓
Branch: Can Afford?
  ├─ True: Enable Buy Button
  └─ False: Disable Buy Button (gray out)
  ↓
Branch: Is Disabled?
  ├─ True: Show Disabled Overlay
  └─ False: Hide Disabled Overlay

Event: On Buy Button Clicked

On Clicked (Buy Button)
  ↓
Purchase PowerUp (Easy UI)
  └─ PowerUpID: ShopItemData.PowerUpID

Main Shop Widget (WBP_PowerUpShop)

Variables

ShopItemWidgetClass: WBP_ShopItem (Class Reference)
ShopItemContainer: Vertical Box (Widget Reference)
CurrencyText: Text Block (Widget Reference)

Event Graph

Event: On Shop Opened

Event Construct
  ↓
Get PowerUp Shop System (from Player Pawn)
  ↓
Bind Event to On Shop Opened
  ↓
Open Shop

Event: Update Currency Display

Event Construct (or Custom Event)
  ↓
Get Current Currency (Easy UI)
  ↓
Format Text ("{0} Gold")
  └─ {0}: Currency Amount
  ↓
Set Text (Currency Text)

Event: On Currency Changed (Automatic Update)

Event Construct
  ↓
Get Currency Subsystem
  ↓
Bind Event to OnPersistentCurrencyChanged
  ↓
[Custom Event: Handle Currency Changed]
    ↓
    Get Current Currency (Easy UI)
    ↓
    Update Currency Display

Function: Populate Shop Items

Input: Shop Items (Array of FURFPowerUpShopItem)
  ↓
Clear Children (ShopItemContainer)
  ↓
ForEachLoop (Shop Items)
  ↓
  Create Widget (WBP_ShopItem)
    ↓
  Update Shop Item (ShopItem)
    ↓
  Add Child to Vertical Box (ShopItemContainer)

Event: On Close Button Clicked

On Clicked (Close Button)
  ↓
Close Shop
  ↓
Remove from Parent

Listening to Events

Shop System Events

1. OnShopOpened

Triggered when shop opens or refreshes.

Event BeginPlay
  ↓
Get PowerUp Shop System
  ↓
Bind Event to OnShopOpened
  ↓
[Custom Event: Handle Shop Opened]
    Input: Shop Items (Array)
    ↓
    Populate Shop Items (Shop Items)

2. OnPowerUpPurchased

Triggered when PowerUp successfully purchased.

Bind Event to OnPowerUpPurchased
  ↓
[Custom Event: Handle Purchase Success]
    Input: PowerUpID (Name), NewLevel (Integer)
    ↓
    Play Success Sound
    ↓
    Show Notification ("Might upgraded to level 5!")

3. OnPurchaseFailed

Triggered when purchase fails.

Bind Event to OnPurchaseFailed
  ↓
[Custom Event: Handle Purchase Failed]
    Input: PowerUpID (Name), FailureReason (Text)
    ↓
    Play Error Sound
    ↓
    Show Error Message (FailureReason)

4. OnPowerUpsRefunded

Triggered when all PowerUps refunded.

Bind Event to OnPowerUpsRefunded
  ↓
[Custom Event: Handle Refund Success]
    Input: RefundedAmount (Integer)
    ↓
    Show Notification ("Refunded {RefundedAmount} Gold!")
    ↓
    Refresh Shop

5. OnPowerUpDisabled / OnPowerUpEnabled

Triggered when PowerUp disabled/enabled.

Bind Event to OnPowerUpDisabled
  ↓
[Custom Event: Handle PowerUp Disabled]
    Input: PowerUpID (Name)
    ↓
    Show Notification ("{PowerUpID} disabled")

Example Event Binding (Complete)

Event BeginPlay
  ↓
Get PowerUp Shop System (from Player Pawn)
  ↓
Bind Event to OnShopOpened → [Handle Shop Opened]
  ↓
Bind Event to OnPowerUpPurchased → [Handle Purchase Success]
  ↓
Bind Event to OnPurchaseFailed → [Handle Purchase Failed]
  ↓
Bind Event to OnPowerUpsRefunded → [Handle Refund Success]
  ↓
Bind Event to OnRefundFailed → [Handle Refund Failed]
  ↓
Bind Event to OnPowerUpDisabled → [Handle PowerUp Disabled]
  ↓
Bind Event to OnPowerUpEnabled → [Handle PowerUp Enabled]

Global Functions (Easy UI)

Global functions allow shop control without component reference. Can be called directly from UI widgets.

Available Global Functions

Open Shop (Easy UI)

Opens shop and triggers OnShopOpened event.

On Button Clicked (Open Shop Button)
  ↓
Open Shop (Easy UI)

Purchase PowerUp (Easy UI)

Purchases PowerUp.

On Button Clicked (Buy Button)
  ↓
Purchase PowerUp (Easy UI)
  └─ PowerUpID: "Might"

Get Shop Items (Easy UI)

Returns current shop items.

Event Tick
  ↓
Get Shop Items (Easy UI)
  ↓
ForEachLoop
  └─ Update UI

Refresh Shop (Easy UI)

Manually refreshes shop.

On Button Clicked (Refresh Button)
  ↓
Refresh Shop (Easy UI)

Refund All PowerUps (Easy UI)

Refunds all PowerUps.

On Button Clicked (Refund Button)
  ↓
Refund All PowerUps (Easy UI)

Toggle PowerUp Enabled (Easy UI)

Enables/disables PowerUp.

On Button Clicked (Toggle Button)
  ↓
Toggle PowerUp Enabled (Easy UI)
  └─ PowerUpID: "Might"

Get Current Currency (Easy UI)

Returns current currency amount (for displaying in UI).

Event Tick (or Event Construct)
  ↓
Get Current Currency (Easy UI)
  ↓
Set Text (Currency Text)
  └─ Format: "{Currency} Gold"

Note: This function uses shop system's Currency Type setting. If no shop system, defaults to "Gold".

Get PowerUp Cost (Easy UI)

Returns next level cost for specific PowerUp.

Event Construct (or Update UI)
  ↓
Get PowerUp Cost (Easy UI)
  └─ PowerUpID: "MaxHealth"
  ↓
Set Text (Cost Text)
  └─ Format: "Cost: {Cost} Gold"

Features:

  • Always returns current price
  • Automatically updates after purchase
  • Works even if shop not open
  • Returns -1 at max level

Get PowerUp Level (Easy UI)

Returns current level of specific PowerUp.

Event Construct (or Update UI)
  ↓
Get PowerUp Level (Easy UI)
  └─ PowerUpID: "MaxHealth"
  ↓
Set Text (Level Text)
  └─ Format: "Level: {Level}/10"

Features:

  • Always returns current level
  • Automatically updates after purchase
  • Works even if shop not open
  • Starts at level 0

Usage Example (Combined):

// Display PowerUp information
Get PowerUp Level (Easy UI) → "MaxHealth"
  ↓
Get PowerUp Cost (Easy UI) → "MaxHealth"
  ↓
Format Text ("MaxHealth - Level {0} | Cost: {1} Gold")
  ├─ {0}: Level
  └─ {1}: Cost
  ↓
Set Text (Info Text)

Global vs Instance Functions

Global Functions (Easy UI):

  • ✅ No component reference required
  • ✅ Can be called from any Blueprint
  • ✅ Ideal for UI widgets
  • ⚠️ Automatically finds active shop system

Instance Functions:

  • ✅ More performant (cached reference)
  • ✅ Specific control if multiple shop systems
  • ⚠️ Requires component reference

Recommendation: Use global functions in UI widgets, instance functions in gameplay code.


Vampire Survivors Features

1. Refund System

Allows players to refund all PowerUps and get spent money back.

Usage

On Button Clicked (Refund All Button)
  ↓
Get Total Spent Currency
  ↓
Show Confirmation Dialog
  └─ "Refund all PowerUps for {TotalSpent} Gold?"
  ↓
On Confirmed
  ↓
Refund All PowerUps (Easy UI)

Refund Calculation

System calculates total cost spent for each PowerUp:

TotalSpent = Sum of (BaseCost * CostScaling^Level) for all PowerUps

Example:

  • Might Level 3: 100 + 150 + 225 = 475 Gold
  • Armor Level 2: 100 + 150 = 250 Gold
  • Total Refund: 725 Gold

2. Disable System

Prevents max level PowerUps from appearing in level up screen.

Usage

On Button Clicked (Disable Button)
  ↓
Branch: Is Max Level?
  ├─ True: Toggle PowerUp Enabled (PowerUpID)
  └─ False: Show Error ("PowerUp must be max level")

Adding Disable Button (Shop Item Widget)

Function: Update Shop Item
  ↓
Branch: Is Max Level?
  ├─ True: Show Disable Button
  │   ↓
  │   Branch: Is Disabled?
  │   ├─ True: Set Button Text "Enable"
  │   └─ False: Set Button Text "Disable"
  └─ False: Hide Disable Button

Event: On Disable Button Clicked

On Clicked (Disable Button)
  ↓
Toggle PowerUp Enabled (Easy UI)
  └─ PowerUpID: ShopItemData.PowerUpID

Example Usage Scenarios

Scenario 1: Simple Shop UI

Goal: Open shop button at game start, purchase PowerUps in shop.

Steps:

  1. Add "Open Shop" button to Main Menu widget
  2. Button Click → Open Shop (Easy UI)
  3. Shop widget opens automatically (via OnShopOpened event)
  4. Player clicks PowerUp → Purchase PowerUp (Easy UI)
  5. Successful purchase → OnPowerUpPurchased event → Show notification

Scenario 2: End of Run Shop

Goal: Shop opens with earned money when run ends.

Steps:

  1. Transfer currency when run ends (URFCurrencyComponent)
  2. Show shop widget
  3. Call Open Shop (Easy UI)
  4. Player purchases PowerUps
  5. "Continue" button → Return to main menu

Scenario 3: Refund Confirmation Dialog

Goal: Ask for confirmation before refunding.

On Button Clicked (Refund Button)
  ↓
Get Total Spent Currency
  ↓
Branch: Total Spent > 0?
  ├─ True:
  │   ↓
  │   Create Widget (WBP_ConfirmationDialog)
  │   ↓
  │   Set Message ("Refund all PowerUps for {TotalSpent} Gold?")
  │   ↓
  │   On Confirmed:
  │     ↓
  │     Refund All PowerUps (Easy UI)
  │     ↓
  │     Show Success Message
  │   ↓
  │   On Cancelled:
  │     ↓
  │     Close Dialog
  └─ False:
      ↓
      Show Error ("No PowerUps to refund")

Scenario 4: Category Filtering

Goal: Filter PowerUps by category (Stats, Utility, Special).

On Button Clicked (Category Button)
  ↓
Get Shop Items (Easy UI)
  ↓
Filter by Category (Selected Category)
  ↓
Populate Shop Items (Filtered Items)

Filter Function:

Input: Shop Items (Array), Category (Enum)
  ↓
ForEachLoop (Shop Items)
  ↓
  Branch: Item.Category == Selected Category?
    ├─ True: Add to Filtered Array
    └─ False: Skip
  ↓
Return: Filtered Array

Scenario 5: Search/Filter by Name

Goal: Player can search PowerUps by name.

On Text Changed (Search Box)
  ↓
Get Shop Items (Easy UI)
  ↓
Filter by Name (Search Text)
  ↓
Populate Shop Items (Filtered Items)

Scenario 6: Real-Time Currency Display

Goal: Display current money in shop and auto-update.

Shop Widget (WBP_PowerUpShop):

Event Construct
  ↓
Update Currency Display
  ↓
Get Game Instance
  ↓
Get Currency Subsystem
  ↓
Bind Event to OnPersistentCurrencyChanged
  ↓
[Custom Event: On Currency Changed]
    Input: Currency Type, New Amount, Delta
    ↓
    Branch: Currency Type == "Gold"?
      ├─ True: Update Currency Display
      └─ False: Ignore

Function: Update Currency Display

Get Current Currency (Easy UI)
  ↓
Format Text ("Gold: {0}")
  └─ {0}: Currency Amount
  ↓
Set Text (Currency Text Block)
  ↓
[Optional] Animate if changed
  ↓
Play Animation (Currency Pulse)

Bonus: Affordability Indicator

ForEachLoop (Shop Items)
  ↓
  Get Current Currency (Easy UI)
  ↓
  Branch: Currency >= Item.Cost?
    ├─ True: Set Button Color (Green)
    └─ False: Set Button Color (Red/Gray)

Troubleshooting

Shop Not Opening

Problem: Nothing happens when Open Shop called.

Solutions:

  1. Is component added?

    • Player Pawn must have URFPowerUpShopSystem component
    • Enable "Enable Debug Logging" in Component Details
    • Check Output Log
  2. Is event binding done?

    • Is widget bound to OnShopOpened event?
    • Check binding code in Event Graph
  3. Does PowerUp Data Asset exist?

    • At least one PowerUp Data Asset must be created
    • PowerUps must be registered in Game Instance

Purchase Not Working

Problem: Purchase doesn't happen when Buy button clicked.

Solutions:

  1. Is currency sufficient?

    • Is there enough Gold in URFCurrencySubsystem?
    • Debug: Check with Get Persistent Currency
  2. Does Currency Type match?

    • Shop System Component Currency Type: "Gold"
    • Does currency with same name exist in Currency Subsystem?
  3. Is PowerUpID correct?

    • PowerUpID sent to Purchase PowerUp must match PowerUpID in Data Asset
    • Debug: Print PowerUpID
  4. Is it at max level?

    • PowerUp at max level cannot be purchased
    • Listen to OnPurchaseFailed event and show error message

Shop Not Auto-Refreshing

Problem: Shop doesn't update when currency changes.

Solutions:

  1. Does Currency Type match?

    • Currency Type in Shop System must match changing currency
    • Example: If shop uses "Gold", "Gems" change won't refresh shop
  2. Is event binding correct?

    • Currency Subsystem's OnPersistentCurrencyChanged event automatically listened
    • If debug logging active, "Currency changed" message should appear in logs

Refund Not Working

Problem: Nothing happens when Refund All PowerUps called.

Solutions:

  1. Are there PowerUp levels?

    • At least one PowerUp must be purchased
    • GetTotalSpentCurrency must be > 0
  2. Are subsystems ready?

    • PowerUpSubsystem and CurrencySubsystem might be null
    • Check with debug logging
  3. Listen to OnRefundFailed event:

    • Bind event to see error message

Disable Button Not Showing

Problem: No disable button on max level PowerUp.

Solutions:

  1. Is PowerUp at max level?

    • Only max level PowerUps can be disabled
    • ShopItemData.bIsMaxLevel must be true
  2. Does UI widget have disable button?

    • Disable button must be added to Shop Item Widget
    • Show/hide logic must exist in Update Shop Item function

Global Functions Not Working

Problem: Nothing happens when global functions called.

Solutions:

  1. Is Shop System Component added?

    • Component must be on Player Pawn
    • FindActiveShopSystem finds automatically but can't find if component missing
  2. Is Player Pawn spawned?

    • Global functions search for player pawn
    • Can't find if called before game starts
  3. Enable debug logging:

    • If "No active shop system found" warning appears, component is missing

Save/Load Not Working

Problem: PowerUp levels or disabled state not saving.

Solutions:

  1. Is Auto Save active?

    • PowerUpSubsystem bAutoSave must be true
    • CurrencySubsystem bAutoSave must also be true
  2. Are save slot names correct?

    • PowerUpSubsystem: SaveSlotName = "URFPowerUpSave"
    • CurrencySubsystem: SaveSlotName = "URFCurrencySave"
  3. Manual save/load:

    Get PowerUp Subsystem
      ↓
    Save PowerUps
    

Advanced Customizations

Custom Shop Item Sorting

Display PowerUps with custom sorting:

Get Shop Items (Easy UI)
  ↓
Sort Array (Custom Comparator)
  ├─ By Cost (Ascending/Descending)
  ├─ By Category
  ├─ By Current Level
  └─ By Can Afford
  ↓
Populate Shop Items

Dynamic Pricing

Custom pricing system (discounts, events):

Function: Calculate Discounted Cost
  Input: Base Cost, Discount Percentage
  ↓
  Return: Base Cost * (1 - Discount / 100)

PowerUp Bundles

Sell multiple PowerUps as package:

On Button Clicked (Buy Bundle)
  ↓
  ForEachLoop (PowerUps in Bundle)
    ↓
    Purchase PowerUp (PowerUpID)
  ↓
  Show Bundle Purchased Message

Analytics Integration

PowerUp purchase statistics:

On PowerUp Purchased
  ↓
  Log Analytics Event
    ├─ Event Name: "PowerUp_Purchased"
    ├─ PowerUpID: PowerUpID
    ├─ Level: NewLevel
    └─ Cost: Cost

Clone this wiki locally