For the complete documentation index, see llms.txt. This page is also available as Markdown.
Ch 3: Game UI Components
In this chapter, you'll build the game interface — the clown sprite with punch animations, action buttons, and the entry screen. Estimated time: ~15 minutes
ShowClown: Animated clown sprite
The clown sprite changes appearance based on how many times it's been hit. Create src/components/game/ShowClown.tsx:
import{motion,useAnimation}from'framer-motion'import{useEffect,useMemo}from'react'import{Box}from'@mui/material'typeClownProps={isKO:booleanisShakingAnimation:booleanisHittingAnimation:booleanpunchCount:number}constShowClown:React.FC<ClownProps>= ({isKO,isShakingAnimation,isHittingAnimation,punchCount,}) =>{constcontrols=useAnimation()useEffect(()=>{if (isShakingAnimation) {controls.start({rotate: [0,-5,5,-5,5,0],transition:{duration:0.5},})}elseif (isHittingAnimation) {controls.start({scale: [1,0.9,1.1,1],transition:{duration:0.3},})}}, [isShakingAnimation,isHittingAnimation,controls]) // Select the appropriate clown image based on punch count and KO state // Using useMemo to prevent recalculating on every renderconstclownImage=useMemo(()=>{ // If shaking, show the shaking clown imageif (isShakingAnimation) {return'/clown_shaking.png'}if (isKO) {return'/clownko.png'}letimagePathswitch (punchCount) {case0:imagePath='/clown1.png'breakcase1:imagePath='/clown2.png'breakcase2:case3:imagePath='/clown3.png'breakdefault:imagePath='/clown1.png'}returnimagePath}, [isKO,isShakingAnimation,punchCount])return (<Boxsx={{width:{xs:'70%',sm:'70%',md:'80%',lg:'80%',xl:'100%'},height:{xs:'100%',sm:'70%',md:'80%',lg:'80%',xl:'100%'},display:'flex',justifyContent:'center',alignItems:'center',}}><divclassName="relative"><motion.divanimate={controls}className="relative"><imgsrc={clownImage}alt="Clown"style={{maxWidth:'100%',height:'auto',objectFit:'contain',}}/></motion.div></div></Box> )}exportdefaultShowClown
The sprite progression creates visual feedback as the clown takes damage:
0 hits — clown1.png (full health)
1 hit — clown2.png (damaged)
2–3 hits — clown3.png (heavily damaged)
KO — clownko.png (knocked out)
Shaking — clown_shaking.png (mid-animation)
Framer Motion's useAnimation hook controls two animations: a shake (rotation) when the clown gets hit, and a scale punch effect for impact feedback.
ButtonContainer: Action buttons
The button container renders the game's action buttons — hit, rob, and reset. Create src/components/game/ButtonContainer.tsx:
The button layout adapts based on game state and screen size:
Desktop — rob button on the left, hit/reset on the right
Mobile — both buttons side by side below the clown
Clown standing — show Hit button (right)
Clown KO — show Reset button (right), Rob button now callable (left)
ClownPuncher: Main game component
This component ties everything together. Create src/components/game/ClownPuncher.tsx:
EntryScreen: Wallet connection
The entry screen prompts the user to connect their wallet before playing. Create src/components/game/EntryScreen.tsx:
Clicking the logo opens the RainbowKit wallet connection modal. Once authenticated, the onEnter callback fires and the ClownPuncher component takes over.
WalletConnectButton: Auth context
Create the auth context and wallet button used throughout the app. Create src/components/chain/WalletConnectButton.tsx:
Running the frontend
Start the frontend dev server:
Make sure sanvil is running and the contract is deployed (see Deploying). Open http://localhost:5173 in your browser, connect your wallet, and start punching the clown!