📖 About This Sketch
This sketch creates an engaging clicker game called 'Aim Tester' where players race against a 45-second timer to click as many randomly positioned black squares as possible. What makes this sketch visually interesting is the animated hexagon background, the rank system with custom-drawn icons (circles, crowns, stars), and smooth state transitions between screens. The code demonstrates key p5.js techniques including the draw loop for continuous animation, collision detection for click interactions, off-screen graphics buffers for performance, and the native localStorage API for persisting game data.
The code is organized into several distinct sections: game state management (which screen to show), box creation and interaction logic, a leaderboard system with persistent storage, and a rank calculation engine based on average match scores. By studying this sketch, you will learn how to build a complete game with multiple screens, handle user input both through clicks and text fields, manage complex game data with objects and arrays, and optimize rendering by pre-computing expensive graphics like the background once and reusing them every frame.
🔄 Code Flow
Code flow showing setup, draw, drawstartscreen, drawplayingscreen, drawpostgameoptionsscreen, drawpostgamescreencontent, drawleaderboardscreen, drawnameentryscreen, createbox, mousepressed, startgame, endgame, showleaderboard, showstartscreen, handlenamesubmission, handlenameenter, loadgamedata, savegamedata, cleargamedata, addtoleaderboard, updatecmatchhistory, calculaterank, getrankfromscore, drawrankicon, drawhexagon, drawhexagonbackground, createui, stylebutton, updateui, windowresized
💡 Click on function names in the diagram to jump to their code
graph TD
start[Start] --> setup[setup]
setup --> canvas-setup[Canvas and text configuration]
setup --> glow-effect[Text glow effect]
setup --> background-creation[Background graphics preparation]
background-creation --> background-display[Display pre-rendered background]
setup --> createui[createUI]
setup --> loadgamedata[loadGameData]
setup --> cleargamedata[clearGameData]
setup --> updateui[updateUI]
setup --> windowresized[windowResized]
setup --> draw[draw loop]
draw --> state-switch[Game state dispatcher]
state-switch --> drawstartscreen[drawStartScreen]
state-switch --> drawplayingscreen[drawPlayingScreen]
state-switch --> drawpostgameoptionsscreen[drawPostGameOptionsScreen]
state-switch --> drawleaderboardscreen[drawLeaderboardScreen]
state-switch --> drawnameentryscreen[drawNameEntryScreen]
click setup href "#fn-setup"
click canvas-setup href "#sub-canvas-setup"
click glow-effect href "#sub-glow-effect"
click background-creation href "#sub-background-creation"
click background-display href "#sub-background-display"
click createui href "#fn-createui"
click loadgamedata href "#fn-loadgamedata"
click cleargamedata href "#fn-cleargamedata"
click updateui href "#fn-updateui"
click windowresized href "#fn-windowresized"
click draw href "#fn-draw"
draw --> title-display[Game title]
draw --> rank-calculation[Current rank lookup]
draw --> rank-icon-and-text[Rank icon and text display]
draw --> timer-calculation[Countdown timer math]
draw --> time-formatting[MM:SS time formatting]
draw --> box-rendering[Draw all active boxes]
box-rendering --> size-randomization[Random box size]
box-rendering --> position-randomization[Random position within bounds]
draw --> game-end-check[Check if time has run out]
game-end-check -->|if time runs out| endgame[endGame]
endgame --> rank-recalc[Rank recalculation at game end]
endgame --> qualification-check[High score qualification test]
qualification-check -->|if qualifies| drawnameentryscreen
qualification-check -->|if not qualifies| showstartscreen[showStartScreen]
click draw href "#fn-draw"
click title-display href "#sub-title-display"
click rank-calculation href "#sub-rank-calculation"
click rank-icon-and-text href "#sub-rank-icon-and-text"
click timer-calculation href "#sub-timer-calculation"
click time-formatting href "#sub-time-formatting"
click box-rendering href "#sub-box-rendering"
click size-randomization href "#sub-size-randomization"
click position-randomization href "#sub-position-randomization"
click game-end-check href "#sub-game-end-check"
click rank-recalc href "#sub-rank-recalc"
click qualification-check href "#sub-qualification-check"
click endgame href "#fn-endgame"
drawplayingscreen --> state-check[Game state validation]
state-check --> mousepressed[mousePressed]
mousepressed --> collision-detection[Box collision test]
collision-detection -->|if hit| score-update[Score increment and box removal]
score-update --> box-initialization[Initial box spawn]
score-update --> state-transition[Game state change]
state-transition --> drawplayingscreen
click drawplayingscreen href "#fn-drawplayingscreen"
click state-check href "#sub-state-check"
click mousepressed href "#fn-mousepressed"
click collision-detection href "#sub-collision-detection"
click score-update href "#sub-score-update"
click box-initialization href "#sub-box-initialization"
click state-transition href "#sub-state-transition"
drawpostgameoptionsscreen --> drawpostgamescreencontent[drawPostGameScreenContent]
drawpostgamescreencontent --> updateui[updateUI]
drawleaderboardscreen --> empty-check[Leaderboard empty check]
empty-check -->|if empty| showstartscreen
empty-check --> leaderboard-loop[Display top 10 scores]
leaderboard-loop -->|for each entry| push-entry[Create and add entry object]
push-entry --> sort-descending[Sort by score descending]
sort-descending --> trim-top-10[Keep only top 10]
click drawpostgameoptionsscreen href "#fn-drawpostgameoptionsscreen"
click drawpostgamescreencontent href "#fn-drawpostgamescreencontent"
click updateui href "#fn-updateui"
click drawleaderboardscreen href "#fn-drawleaderboardscreen"
click empty-check href "#sub-empty-check"
click leaderboard-loop href "#sub-leaderboard-loop"
click push-entry href "#sub-push-entry"
click sort-descending href "#sub-sort-descending"
click trim-top-10 href "#sub-trim-top-10"
drawnameentryscreen --> name-input[Read and validate name]
name-input --> empty-default[Default to Anonymous]
empty-default --> leaderboard-add[Add score to leaderboard]
leaderboard-add --> savegamedata[saveGameData]
savegamedata --> showstartscreen
click drawnameentryscreen href "#fn-drawnameentryscreen"
click name-input href "#sub-name-input"
click empty-default href "#sub-empty-default"
click leaderboard-add href "#sub-leaderboard-add"
drawstartscreen --> startgame[startGame]
startgame --> box-initialization
box-initialization --> timer-start[Record game start time]
timer-start --> state-transition
click drawstartscreen href "#fn-drawstartscreen"
click startgame href "#fn-startgame"
draw --> windowresized
windowresized --> canvas-resize[Resize canvas to window]
canvas-resize --> background-recreate[Redraw background for new size]
background-recreate --> ui-reposition[Reposition UI elements]
click windowresized href "#fn-windowresized"
click canvas-resize href "#sub-canvas-resize"
click background-recreate href "#sub-background-recreate"
click ui-reposition href "#sub-ui-reposition"