-
Notifications
You must be signed in to change notification settings - Fork 0
Step 5: PowerUp Shop Guide
- Overview
- Quick Start
- Creating PowerUp Data Assets
- Shop System Component Setup
- Creating Shop UI Widget
- Listening to Events
- Global Functions (Easy UI)
- Vampire Survivors Features
- Example Usage Scenarios
- Troubleshooting
PowerUp Shop System allows players to purchase upgrades with persistent currency. Designed for Vampire Survivors-style roguelike games.
- ✅ 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
- URFPowerUpDataAsset: PowerUp definitions (stats, cost, max level)
- URFPowerUpSubsystem: PowerUp state management and persistence
- URFCurrencySubsystem: Currency management
- URFPowerUpShopSystem: Shop UI integration (Component)
- Right-click in Content Browser → Miscellaneous → Data Asset
- Select URFPowerUpDataAsset
- Name it (e.g.,
DA_PowerUp_Might) - Open asset and configure settings
- Open Player Pawn Blueprint
- Add Component → Search URFPowerUpShopSystem and add
- Configure component settings (Currency Type, Debug Logging)
- Create User Interface → Widget Blueprint
- Design UI to display shop items
- Listen to shop events in Event Graph
- Bind purchase buttons
Call Open Shop or Open Shop (Easy UI) function in Blueprint.
PowerUpID: "Might" (Must be unique!)
DisplayName: "Might"
Description: "Increases damage dealt by 10% per level"
Icon: [Select Texture2D]
Category: Stats / Utility / Special
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
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
- 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)
- 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)
- 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)
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
- Open Player Pawn Blueprint (e.g., BP_TestPlayer)
- In Components panel, click Add → Search URFPowerUpShopSystem
- Add component
-
Currency Type:
Gold(must match currency type in URFCurrencySubsystem)
-
Enable Debug Logging:
true(enable during development)
PowerUp system works intelligently:
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!
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)
- Shop Panel (Canvas Panel / Overlay)
- Currency Display (Text Block) - "Gold: 1250"
- Shop Item List (Scroll Box / Vertical Box)
- Shop Item Widget (Repeating widget for each PowerUp)
- Close Button
- Refund Button (optional)
ShopItemData: FURFPowerUpShopItem (Instance Editable: false)
- 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
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
ShopItemWidgetClass: WBP_ShopItem (Class Reference)
ShopItemContainer: Vertical Box (Widget Reference)
CurrencyText: Text Block (Widget Reference)
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
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)
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!")
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)
Triggered when all PowerUps refunded.
Bind Event to OnPowerUpsRefunded
↓
[Custom Event: Handle Refund Success]
Input: RefundedAmount (Integer)
↓
Show Notification ("Refunded {RefundedAmount} Gold!")
↓
Refresh Shop
Triggered when PowerUp disabled/enabled.
Bind Event to OnPowerUpDisabled
↓
[Custom Event: Handle PowerUp Disabled]
Input: PowerUpID (Name)
↓
Show Notification ("{PowerUpID} disabled")
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 allow shop control without component reference. Can be called directly from UI widgets.
Opens shop and triggers OnShopOpened event.
On Button Clicked (Open Shop Button)
↓
Open Shop (Easy UI)
Purchases PowerUp.
On Button Clicked (Buy Button)
↓
Purchase PowerUp (Easy UI)
└─ PowerUpID: "Might"
Returns current shop items.
Event Tick
↓
Get Shop Items (Easy UI)
↓
ForEachLoop
└─ Update UI
Manually refreshes shop.
On Button Clicked (Refresh Button)
↓
Refresh Shop (Easy UI)
Refunds all PowerUps.
On Button Clicked (Refund Button)
↓
Refund All PowerUps (Easy UI)
Enables/disables PowerUp.
On Button Clicked (Toggle Button)
↓
Toggle PowerUp Enabled (Easy UI)
└─ PowerUpID: "Might"
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".
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
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 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.
Allows players to refund all PowerUps and get spent money back.
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)
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
Prevents max level PowerUps from appearing in level up screen.
On Button Clicked (Disable Button)
↓
Branch: Is Max Level?
├─ True: Toggle PowerUp Enabled (PowerUpID)
└─ False: Show Error ("PowerUp must be max level")
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
Goal: Open shop button at game start, purchase PowerUps in shop.
Steps:
- Add "Open Shop" button to Main Menu widget
- Button Click →
Open Shop (Easy UI) - Shop widget opens automatically (via OnShopOpened event)
- Player clicks PowerUp →
Purchase PowerUp (Easy UI) - Successful purchase → OnPowerUpPurchased event → Show notification
Goal: Shop opens with earned money when run ends.
Steps:
- Transfer currency when run ends (URFCurrencyComponent)
- Show shop widget
- Call
Open Shop (Easy UI) - Player purchases PowerUps
- "Continue" button → Return to main menu
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")
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
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)
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)
Problem: Nothing happens when Open Shop called.
Solutions:
-
Is component added?
- Player Pawn must have URFPowerUpShopSystem component
- Enable "Enable Debug Logging" in Component Details
- Check Output Log
-
Is event binding done?
- Is widget bound to OnShopOpened event?
- Check binding code in Event Graph
-
Does PowerUp Data Asset exist?
- At least one PowerUp Data Asset must be created
- PowerUps must be registered in Game Instance
Problem: Purchase doesn't happen when Buy button clicked.
Solutions:
-
Is currency sufficient?
- Is there enough Gold in URFCurrencySubsystem?
- Debug: Check with
Get Persistent Currency
-
Does Currency Type match?
- Shop System Component Currency Type: "Gold"
- Does currency with same name exist in Currency Subsystem?
-
Is PowerUpID correct?
- PowerUpID sent to Purchase PowerUp must match PowerUpID in Data Asset
- Debug: Print PowerUpID
-
Is it at max level?
- PowerUp at max level cannot be purchased
- Listen to OnPurchaseFailed event and show error message
Problem: Shop doesn't update when currency changes.
Solutions:
-
Does Currency Type match?
- Currency Type in Shop System must match changing currency
- Example: If shop uses "Gold", "Gems" change won't refresh shop
-
Is event binding correct?
- Currency Subsystem's OnPersistentCurrencyChanged event automatically listened
- If debug logging active, "Currency changed" message should appear in logs
Problem: Nothing happens when Refund All PowerUps called.
Solutions:
-
Are there PowerUp levels?
- At least one PowerUp must be purchased
- GetTotalSpentCurrency must be > 0
-
Are subsystems ready?
- PowerUpSubsystem and CurrencySubsystem might be null
- Check with debug logging
-
Listen to OnRefundFailed event:
- Bind event to see error message
Problem: No disable button on max level PowerUp.
Solutions:
-
Is PowerUp at max level?
- Only max level PowerUps can be disabled
- ShopItemData.bIsMaxLevel must be true
-
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
Problem: Nothing happens when global functions called.
Solutions:
-
Is Shop System Component added?
- Component must be on Player Pawn
- FindActiveShopSystem finds automatically but can't find if component missing
-
Is Player Pawn spawned?
- Global functions search for player pawn
- Can't find if called before game starts
-
Enable debug logging:
- If "No active shop system found" warning appears, component is missing
Problem: PowerUp levels or disabled state not saving.
Solutions:
-
Is Auto Save active?
- PowerUpSubsystem bAutoSave must be true
- CurrencySubsystem bAutoSave must also be true
-
Are save slot names correct?
- PowerUpSubsystem: SaveSlotName = "URFPowerUpSave"
- CurrencySubsystem: SaveSlotName = "URFCurrencySave"
-
Manual save/load:
Get PowerUp Subsystem ↓ Save PowerUps
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
Custom pricing system (discounts, events):
Function: Calculate Discounted Cost
Input: Base Cost, Discount Percentage
↓
Return: Base Cost * (1 - Discount / 100)
Sell multiple PowerUps as package:
On Button Clicked (Buy Bundle)
↓
ForEachLoop (PowerUps in Bundle)
↓
Purchase PowerUp (PowerUpID)
↓
Show Bundle Purchased Message
PowerUp purchase statistics:
On PowerUp Purchased
↓
Log Analytics Event
├─ Event Name: "PowerUp_Purchased"
├─ PowerUpID: PowerUpID
├─ Level: NewLevel
└─ Cost: Cost