Unreal Blueprint Clicker, Learn Blueprints
JVDesignStudio - https://jvdesignstudio.co.uk/workshops/unreal-clicker-builder.html

Offline reference pack for this workshop.
This is a companion copy of the build steps, so you can follow along without
the website. Open the workshop online for the interactive screenshots, videos
and the copy buttons.

What you build: Unreal Blueprint Clicker, Learn Blueprints

--------------------------------------------------------------------
STEP-BY-STEP BUILD
--------------------------------------------------------------------

## STEP 01, BEGINPLAY - Event BeginPlay, Start the Game
- A Blueprint Actor runs logic through its Event Graph. Connect Event BeginPlay to Print String to confirm your game launches.

## STEP 06, IDLE TIMER - Event BeginPlay',x:20,y:30,w:155,rp:[{id:'r1',type:'exec',label:''}]}, {id:'n2',type:'func',title:'Print String',sub:'Hello Clicker!',x:210,y:30,w:160,lp:[{id:'l1',type:'exec',label:''}],rp:[{id:'r2',type:'exec',label:''}]}, ],[{f:'r1',t:'l1'}],[]), boxes:[ {t:'concept',tx:'The white exec pin controls order. BeginPlay fires once when the game starts. Drag from its right exec pin to Print String\'s left exec pin to wire them.'}, {t:'ue',tx:'Right-click the Event Graph ? search "BeginPlay". Right-click again ? search "Print String". Drag from the white arrow to connect.'}, ], onApply(){G.on=true;sout('✅ BeginPlay connected, game is live! Click the coin!');}, quiz:{q:"What does Event BeginPlay do?",opts:["Fires every frame","Fires once when the game starts","Fires when you press Play in the editor"],correct:1} }, {title:"Variables, Coins & CoinsPerClick",tag:"STEP 02, VARIABLES", intro:`Connect BeginPlay ? Set Coins ? Set CoinsPerClick. Click the orange value fields on each node to type the starting values.`, pz:mkPuzzle([ {id:'n1',type:'event',title:'Event BeginPlay',x:8,y:40,w:150,rp:[{id:'r1',type:'exec',label:''}]}, {id:'n2',type:'vset',title:'Set Coins',x:178,y:18,w:148, lp:[{id:'l2e',type:'exec',label:''},{id:'l2v',type:'int',label:'Value'}], rp:[{id:'r2',type:'exec',label:''}], vf:{hint:'Starting coins (e.g. 0)',val:''}}, {id:'n3',type:'vset',title:'Set CoinsPerClick',x:348,y:18,w:172, lp:[{id:'l3e',type:'exec',label:''},{id:'l3v',type:'int',label:'Value'}], rp:[{id:'r3',type:'exec',label:''}], vf:{hint:'Coins per click (1-10)',val:''}}, ],[{f:'r1',t:'l2e'},{f:'r2',t:'l3e'}],[ {id:'n2',ok:v=>v!==''&&!isNaN(parseInt(v))}, {id:'n3',ok:v=>parseInt(v)>=1&&parseInt(v)Integer variables store whole numbers. Create them in the Variables panel (left side in UE5), then drag them into the graph as Set nodes by right-clicking and choosing "Set Variable".'}, {t:'tip',tx:'Type 0 for Coins and 1 for CoinsPerClick. You\'ll increase these with upgrades later.'}, ], onApply(v){ G.coins=parseInt(v['n2'])||0; G.cpc=parseInt(v['n3'])||1; hud();sout('✅ Variables set, Coins: '+v['n2']+', CoinsPerClick: '+v['n3']); }, quiz:{q:"What type of variable stores whole numbers like coin counts?",opts:["Float","String","Integer"],correct:2} }, {title:"OnClicked Event, Register the Click",tag:"STEP 03, ON CLICKED", intro:`Add an OnClicked event to your Button Widget or Actor. Connect it to Add Coins (a custom event you'll call). Wire the exec pins.`, pz:mkPuzzle([ {id:'n1',type:'event',title:'OnClicked',sub:'(Button / Actor)',x:10,y:35,w:162,rp:[{id:'r1',type:'exec',label:''}]}, {id:'n2',type:'custom',title:'Add Coins',sub:'Custom Event',x:198,y:25,w:148,lp:[{id:'l2',type:'exec',label:''}],rp:[{id:'r2',type:'exec',label:''}]}, ],[{f:'r1',t:'l2'}],[]), boxes:[ {t:'ue',tx:'In a UMG Widget Blueprint: click your button in the Designer, scroll to the Events section in Details, and click the + next to OnClicked to auto-add the event.'}, {t:'concept',tx:'Custom Events are like named functions you trigger by calling them elsewhere. They make your graph tidy, instead of long chains everywhere, call AddCoins from any place you need.'}, ], onApply(){sout('✅ OnClicked → AddCoins wired. Clicking the coin now fires the event!');}, quiz:{q:"What is a Custom Event in Blueprints?",opts:["A built-in engine event","A named event you create and call from anywhere","A visual effect node"],correct:1} }, {title:"Add Integer, Coins + CoinsPerClick",tag:"STEP 04, ADD INT", intro:`Wire Get Coins + Get CoinsPerClick ? ADD Integer, then feed the result into Set Coins. This is the core click formula.`, pz:mkPuzzle([ {id:'n1',type:'vget',title:'Get Coins',x:8,y:10,w:130,rp:[{id:'r1',type:'int',label:''}]}, {id:'n2',type:'vget',title:'Get CoinsPerClick',x:8,y:58,w:155,rp:[{id:'r2',type:'int',label:''}]}, {id:'n3',type:'math',title:'ADD (int)',x:185,y:22,w:108, lp:[{id:'l3a',type:'int',label:'A'},{id:'l3b',type:'int',label:'B'}], rp:[{id:'r3',type:'int',label:''}]}, {id:'n4',type:'vset',title:'Set Coins',x:315,y:8,w:138, lp:[{id:'l4e',type:'exec',label:''},{id:'l4v',type:'int',label:'Value'}], rp:[{id:'r4',type:'exec',label:''}]}, ],[{f:'r1',t:'l3a'},{f:'r2',t:'l3b'},{f:'r3',t:'l4v'}],[]), boxes:[ {t:'concept',tx:'Blueprint math nodes (ADD, MULTIPLY, DIVIDE) appear as pure nodes, no exec pin, because they compute instantly without side effects. They run when their output is needed.'}, {t:'tip',tx:'Later you\'ll multiply CoinsPerClick by a PrestigeMultiplier float variable before adding, giving you the prestige bonus without changing this graph.'}, ], onApply(){sout('✅ Coins = Coins + CoinsPerClick. Click the coin to earn!');}, quiz:{q:"Why don't math nodes like ADD have exec (white arrow) pins?",opts:["They're broken","They compute instantly with no side effects","They only work with floats"],correct:1} }, {title:"Update HUD, SetText on Coin Label",tag:"STEP 05, HUD TEXT", intro:`After setting Coins, chain into Get Coins ? ToText (int) ? Set Text on your Coin Label widget. Wire exec pins and feed the text output.`, pz:mkPuzzle([ {id:'n1',type:'vget',title:'Get Coins',x:8,y:28,w:130,rp:[{id:'r1',type:'int',label:''}]}, {id:'n2',type:'func',title:'ToText (int)',x:158,y:14,w:130, lp:[{id:'l2',type:'int',label:'Value'}], rp:[{id:'r2',type:'string',label:'Result'}]}, {id:'n3',type:'widget',title:'Set Text',sub:'CoinLabel',x:308,y:8,w:152, lp:[{id:'l3e',type:'exec',label:''},{id:'l3t',type:'string',label:'In Text'}], rp:[{id:'r3',type:'exec',label:''}]}, ],[{f:'r1',t:'l2'},{f:'r2',t:'l3t'}],[]), boxes:[ {t:'concept',tx:'ToText (int) converts a number to a display string. Always convert before feeding into widget text fields, Blueprints are strongly typed, no silent coercion.'}, {t:'ue',tx:'Create a UMG Widget Blueprint: File ? New Blueprint ? Widget Blueprint. Add a TextBlock named "CoinLabel". Promote it as a variable (tick "Is Variable" in Details) so you can set it from code.'}, ], onApply(){sout('✅ HUD updating! Coin label shows the live total.');}, quiz:{q:"Why must you convert an integer to text before feeding it into a Set Text node?",opts:["Blueprints don't support integers","Blueprints are strongly typed, no silent coercion","Text nodes only accept floats"],correct:1}, challenge:{type:'concept-fill',sentence:'The ____ node converts a number into a display string for a Text widget.
- On BeginPlay, call Set Timer by Event. Bind it to your IdleTick custom event and set the Rate. Tick "Looping" so it fires every second.

## STEP 07, IDLE COINS - Event BeginPlay',x:8,y:38,w:152,rp:[{id:'r1',type:'exec',label:''}]}, {id:'n2',type:'func',title:'Set Timer by Event',x:180,y:10,w:175, lp:[{id:'l2e',type:'exec',label:''},{id:'l2ev',type:'any',label:'Event'},{id:'l2r',type:'float',label:'Rate'},{id:'l2l',type:'bool',label:'Looping'}], rp:[{id:'r2',type:'exec',label:''}], vf:{hint:'Rate in seconds (e.g. 1.0)',val:''}}, {id:'n3',type:'custom',title:'IdleTick',sub:'Custom Event',x:8,y:138,w:142,rp:[{id:'r3',type:'any',label:''}]}, ],[{f:'r1',t:'l2e'},{f:'r3',t:'l2ev'}],[ {id:'n2',ok:v=>parseFloat(v)>0&&parseFloat(v)Set Timer by Event fires the bound event repeatedly at the set rate. Unlike Event Tick (every frame), timers run at fixed intervals, much better for idle income because you control exactly when coins appear.'}, {t:'tip',tx:'Type 1.0 for a 1-second tick. The Looping pin is a bool, drag a True literal node onto it.'}, ], onApply(v){ G.idle=true; sout('✅ IdleTick timer set to '+v['n2']+'s. Idle income is live!'); }, quiz:{q:"Why use Set Timer by Event instead of Event Tick for idle income?",opts:["Event Tick is deprecated","Timers run at fixed intervals you control","Event Tick doesn't work with integers"],correct:1}, challenge:{type:'tf',items:[ {statement:'Set Timer by Event fires every frame like Event Tick.',answer:'false'}, {statement:'Setting Looping to True makes the timer repeat.
- Inside IdleTick, wire Get Coins + Get CoinsPerSecond ? ADD ? Set Coins ? Update HUD. This is the auto-earn loop.

## STEP 08, BRANCH - IdleTick',sub:'Custom Event',x:8,y:30,w:145,rp:[{id:'r1',type:'exec',label:''}]}, {id:'n2',type:'vget',title:'Get Coins',x:8,y:100,w:130,rp:[{id:'r2',type:'int',label:''}]}, {id:'n3',type:'vget',title:'Get CoinsPerSecond',x:8,y:148,w:162,rp:[{id:'r3',type:'int',label:''}]}, {id:'n4',type:'math',title:'ADD (int)',x:192,y:112,w:108, lp:[{id:'l4a',type:'int',label:'A'},{id:'l4b',type:'int',label:'B'}], rp:[{id:'r4',type:'int',label:''}]}, {id:'n5',type:'vset',title:'Set Coins',x:322,y:14,w:138, lp:[{id:'l5e',type:'exec',label:''},{id:'l5v',type:'int',label:'Value'}], rp:[{id:'r5',type:'exec',label:''}]}, ],[{f:'r1',t:'l5e'},{f:'r2',t:'l4a'},{f:'r3',t:'l4b'},{f:'r4',t:'l5v'}],[]), boxes:[ {t:'concept',tx:'The same Add?Set pattern from clicking now runs automatically every second. CoinsPerSecond will grow as players buy upgrades, you only need to change that variable, not this logic.'}, ], onApply(){G.cps=G.upgrades.miners+G.upgrades.bakeries*10;hud();sout('✅ Idle income ticking! Coins grow automatically now.');}, quiz:{q:"If you change CoinsPerSecond, do you need to update the idle tick logic?",opts:["Yes, you must rewire the graph","No, it reads the variable each tick automatically","Only if you restart the game"],correct:1}, challenge:{type:'concept-fill',sentence:'The idle tick adds ____ to Coins every second using the same Add and Set pattern.
- Wire Get Coins ? Greater Equal ? Branch. Connect the upgrade cost into the Greater Equal. Route True to Apply Upgrade, False to Print "Not enough coins".

## STEP 09, SUBTRACT - Get Coins',x:8,y:28,w:128,rp:[{id:'r1',type:'int',label:''}]}, {id:'n2',type:'vget',title:'Get UpgradeCost',x:8,y:76,w:148,rp:[{id:'r2',type:'int',label:''}]}, {id:'n3',type:'math',title:'>= (int)',x:178,y:30,w:100, lp:[{id:'l3a',type:'int',label:'A'},{id:'l3b',type:'int',label:'B'}], rp:[{id:'r3',type:'bool',label:''}]}, {id:'n4',type:'branch',title:'Branch',x:298,y:22,w:110, lp:[{id:'l4e',type:'exec',label:''},{id:'l4c',type:'bool',label:'Condition'}], rp:[{id:'r4t',type:'exec',label:'True'},{id:'r4f',type:'exec',label:'False'}]}, {id:'n5',type:'custom',title:'Apply Upgrade',sub:'Custom Event',x:428,y:8,w:128,lp:[{id:'l5',type:'exec',label:''}]}, ],[{f:'r1',t:'l3a'},{f:'r2',t:'l3b'},{f:'r3',t:'l4c'},{f:'r4t',t:'l5'}],[]), boxes:[ {t:'concept',tx:'Branch is Blueprint\'s if/else. The Greater Equal node (=) returns true when Coins = UpgradeCost. This pattern gates any action behind a resource check.'}, {t:'tip',tx:'For multiple upgrade tiers, put each one in a collapsed graph and call it from a shared "Try Buy" macro. Keeps your main graph clean.'}, ], onApply(){sout('✅ Afford check added, can\'t buy what you can\'t afford!');}, quiz:{q:"What does the Branch node do in Blueprints?",opts:["Loops through an array","Executes one of two paths based on a boolean condition","Creates a new variable"],correct:1}, challenge:{type:'concept-fill',sentence:'The ____ node returns True when Coins is greater than or equal to UpgradeCost.
- Inside Apply Upgrade: wire Get Coins - Get UpgradeCost ? SUBTRACT ? Set Coins. This deducts the cost before granting the upgrade effect.

## STEP 10, CPS BOOST - Get Coins',x:8,y:10,w:128,rp:[{id:'r1',type:'int',label:''}]}, {id:'n2',type:'vget',title:'Get UpgradeCost',x:8,y:58,w:148,rp:[{id:'r2',type:'int',label:''}]}, {id:'n3',type:'math',title:'SUBTRACT (int)',x:180,y:20,w:138, lp:[{id:'l3a',type:'int',label:'A'},{id:'l3b',type:'int',label:'B'}], rp:[{id:'r3',type:'int',label:''}]}, {id:'n4',type:'vset',title:'Set Coins',x:340,y:8,w:135, lp:[{id:'l4e',type:'exec',label:''},{id:'l4v',type:'int',label:'Value'}], rp:[{id:'r4',type:'exec',label:''}]}, ],[{f:'r1',t:'l3a'},{f:'r2',t:'l3b'},{f:'r3',t:'l4v'}],[]), boxes:[ {t:'concept',tx:'Pure math nodes, ADD, SUBTRACT, MULTIPLY, don\'t need exec pins. They calculate when their output is pulled. Only Set nodes need exec wires because they change state.'}, ], onApply(){sout('✅ Coins deducted on purchase. Economy is working!');}, quiz:{q:"Why do pure math nodes (ADD, SUBTRACT) not have exec pins?",opts:["They only work with floats","They compute instantly with no side effects","They are deprecated in UE5"],correct:1}, challenge:{type:'tf',items:[ {statement:'SUBTRACT is a pure node with no exec pins.
- After subtracting cost, wire Get CoinsPerSecond + Get UpgradeValue ? ADD ? Set CoinsPerSecond. Then update the CPS label on your HUD.

## STEP 11, COST SCALE - Get CoinsPerSecond',x:8,y:10,w:158,rp:[{id:'r1',type:'int',label:''}]}, {id:'n2',type:'vget',title:'Get UpgradeValue',x:8,y:58,w:148,rp:[{id:'r2',type:'int',label:''}]}, {id:'n3',type:'math',title:'ADD (int)',x:185,y:20,w:108, lp:[{id:'l3a',type:'int',label:'A'},{id:'l3b',type:'int',label:'B'}], rp:[{id:'r3',type:'int',label:''}]}, {id:'n4',type:'vset',title:'Set CoinsPerSecond',x:315,y:8,w:168, lp:[{id:'l4e',type:'exec',label:''},{id:'l4v',type:'int',label:'Value'}], rp:[{id:'r4',type:'exec',label:''}]}, ],[{f:'r1',t:'l3a'},{f:'r2',t:'l3b'},{f:'r3',t:'l4v'}],[]), boxes:[ {t:'concept',tx:'Upgrades in clickers are additive boosts to a rate variable. The idle tick reads that variable, so buying more upgrades automatically earns more without changing the tick logic at all.'}, ], onApply(){ G.upgrades.miners++; G.cps=calcCPS(); hud(); sout('✅ CoinsPerSecond increased! Watch the idle income grow.'); }, quiz:{q:"How do upgrades increase idle income without changing the tick logic?",opts:["They add new timer nodes","They increase the CoinsPerSecond variable the tick already reads","They modify the ADD node directly"],correct:1}, challenge:{type:'concept-fill
- Connect Get UpgradeCost × 1.5 (float) ? Truncate ? Set UpgradeCost so each purchase makes the next one more expensive. Type the multiplier value.

## STEP 12, PRESTIGE MULT - Get UpgradeCost',x:8,y:28,w:148,rp:[{id:'r1',type:'int',label:''}]}, {id:'n2',type:'math',title:'MULTIPLY (float)',x:178,y:14,w:140, lp:[{id:'l2a',type:'int',label:'A'},{id:'l2b',type:'float',label:'B'}], rp:[{id:'r2',type:'float',label:''}], vf:{hint:'Multiplier (e.g. 1.5)',val:''}}, {id:'n3',type:'func',title:'Truncate',sub:'float?int',x:340,y:20,w:115, lp:[{id:'l3',type:'float',label:'A'}], rp:[{id:'r3',type:'int',label:''}]}, {id:'n4',type:'vset',title:'Set UpgradeCost',x:340,y:90,w:160, lp:[{id:'l4e',type:'exec',label:''},{id:'l4v',type:'int',label:'Value'}], rp:[{id:'r4',type:'exec',label:''}]}, ],[{f:'r1',t:'l2a'},{f:'r2',t:'l3'},{f:'r3',t:'l4v'}],[ {id:'n2',ok:v=>parseFloat(v)>1&&parseFloat(v)Truncate cuts the decimal, it\'s like int casting. You need it here because Multiply returns a float, but UpgradeCost is an int. Mixing types causes Blueprint compile errors.'}, {t:'tip',tx:'Classic idle game values: ×1.15 (gentle), ×1.5 (standard Cookie Clicker style), ×2.0 (aggressive). Type 1.5 for that familiar feel.'}, ], onApply(v){sout('? Cost scaling ×'+v['n2']+' active, upgrades get pricier!');}, quiz:{q:"Why do you need a Truncate node after MULTIPLY (float)?",opts:["Floats are slower than integers","UpgradeCost is an int, mixing types causes compile errors","Truncate adds rounding"],correct:1}, challenge:{type:'tf',items:[ {statement:'Multiply (float) returns an integer.',answer:'false'}, {statement:'A cost multiplier of 1.5 means each upgrade costs 50% more.
- Wire Get CoinsPerClick · Get PrestigeMultiplier ? MULTIPLY and feed the result into your ADD node instead of raw CoinsPerClick. Type a float multiplier.

## STEP 13, FORMAT TEXT - Get CoinsPerClick',x:8,y:8,w:155,rp:[{id:'r1',type:'float',label:''}]}, {id:'n2',type:'vget',title:'Get PrestigeMultiplier',x:8,y:56,w:172,rp:[{id:'r2',type:'float',label:''}]}, {id:'n3',type:'math',title:'MULTIPLY (float)',x:202,y:18,w:148, lp:[{id:'l3a',type:'float',label:'A'},{id:'l3b',type:'float',label:'B'}], rp:[{id:'r3',type:'float',label:''}]}, {id:'n4',type:'func',title:'Truncate',sub:'float?int',x:372,y:22,w:115, lp:[{id:'l4',type:'float',label:'A'}], rp:[{id:'r4',type:'int',label:''}]}, ],[{f:'r1',t:'l3a'},{f:'r2',t:'l3b'},{f:'r3',t:'l4'}],[]), boxes:[ {t:'concept',tx:'Prestige resets progress but multiplies all future earnings. By routing CoinsPerClick through a multiplier variable, the entire economy scales automatically, you never rewrite the earn logic.'}, ], onApply(){sout('✅ Prestige multiplier wired. Earnings now scale with prestige level!');}, quiz:{q:"What is the advantage of routing earnings through a multiplier variable?",opts:["It makes the graph look better","The entire economy scales without rewriting earn logic","It reduces compile time"],correct:1}, challenge:{type:'concept-fill',sentence:'Prestige resets progress but ____ all future earnings via a multiplier variable.
- Wire Get Coins ? Format Text using the pattern Coins: {0}. Connect the output to Set Text on your HUD label.

## STEP 14, SAVEGAME - SaveGame Object, Create Save
- Wire Create Save Game Object (class: MySaveGame), Cast to MySaveGame, then Set CoinsSaved on it. Feed Coins in.

## STEP 15, SAVE SLOT - Save Game to Slot, Write to Disk
- After setting variables on the save object, wire into Save Game to Slot. Type a slot name and player index. Connect the completion exec.

## STEP 16, LOAD GAME - Load Game from Slot, Restore Data
- On BeginPlay, wire Does Save Game Exist ? Branch ? Load Game from Slot ? Cast to MySaveGame ? Get CoinsSaved ? Set Coins.

## STEP 17, ARRAYS - Array, Upgrade Name List
- Wire Get UpgradeNames (Array) ? Get (index 0) ? Format Text ? Set Text on your first upgrade button. Type an upgrade name.

## STEP 18, FOR LOOP - FOR Loop, Build Upgrade Buttons
- Wire BeginPlay ? For Loop (0 to 3). Inside, use the Index to get the upgrade name array entry and call Create Upgrade Button with it.

## STEP 19, DELEGATE - Dispatch, OnUpgradePurchased
- Create an Event Dispatcher called OnUpgradePurchased. After buying, Call OnUpgradePurchased. In your HUD widget, Bind to it and refresh the display.

## STEP 20, TIMELINE - Timeline, Click Bounce Animation
- Connect OnClicked → Play Timeline from Start → Set Render Scale. Add a float curve in the timeline that goes 1.0 → 1.2 → 1.0 over 0.15s.

## STEP 21, SOUND - Sound, Play Click SFX
- Wire OnClicked ? Play Sound 2D. Connect a Sound variable (your coin SFX) into the Sound pin. Type a volume multiplier.

## STEP 22, PRESTIGE - Prestige, Reset & Double Multiplier
- Wire OnPrestigeClicked ? Set Coins (0) ? Set CoinsPerSecond (0) ? Get PrestigeMultiplier · 2 ? Set PrestigeMultiplier. The player resets but earns double.

## STEP 23, INPUT - Enable Input, Keyboard Shortcut
- Wire BeginPlay ? Enable Input (PlayerController 0). Then add a Keyboard C Event ? call Add Coins (keyboard click) for accessibility.

## STEP 24, VFX - Particle System, Coin Burst on Click
- Wire OnClicked ? Spawn System at Location. Feed a Niagara System reference into the System pin and Get Actor Location into Location.

## STEP 25, SHOP FLOW - Upgrade Shop Full Flow, Putting It Together
- Wire the complete shop flow: Buy Button OnClicked ? Check Afford (Branch) ? Subtract Cost ? Add CPS ? Scale Cost ? Save ? Update HUD ? Dispatch Event.
