[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"navigation":3,"url-settings":86,"blog-\u002Fblog\u002Fflutter-game-pong-game-flame":663,"blog-author-\u002Fblog\u002Fflutter-game-pong-game-flame":2144,"i-material-symbols:arrow-back-rounded":2159},{"id":4,"extension":5,"footer":6,"header":73,"meta":83,"stem":84,"__hash__":85},"navigation\u002Fdata\u002Fshared\u002Fnavigation.yml","yml",{"brand":7,"columns":13,"legal":63},{"name":8,"tagline":9,"downloadCta":10},"Pieces","The memory layer for modern work.",{"label":11,"href":12},"All downloads","\u002Fdownloads",[14,26,39,51],{"title":15,"links":16},"Product",[17,20,23],{"label":18,"href":19},"Pieces Desktop","\u002F",{"label":21,"href":22},"Pieces Enterprise","\u002Fenterprise",{"label":24,"href":25},"Pieces MCP","\u002Fmcp",{"title":27,"links":28},"Resources",[29,33,36],{"label":30,"href":31,"external":32},"Documentation","url:docs.home",true,{"label":34,"href":35},"Blog","\u002Fblog",{"label":37,"href":38,"external":32},"GitHub","url:github.org",{"title":40,"links":41},"Community",[42,45,48],{"label":43,"href":44,"external":32},"Discord","url:social.discord",{"label":46,"href":47,"external":32},"X \u002F Twitter","url:social.x",{"label":49,"href":50,"external":32},"LinkedIn","url:social.linkedin",{"title":52,"links":53},"Company",[54,57,60],{"label":55,"href":56},"About","\u002Fabout",{"label":58,"href":59},"Updates","\u002Fupdates",{"label":61,"href":62},"Contact","\u002Fcontact",[64,67,70],{"label":65,"href":66},"Privacy Policy","\u002Flegal\u002Fprivacy",{"label":68,"href":69},"Refund Policy","\u002Flegal\u002Frefund",{"label":71,"href":72},"Terms of Service","\u002Flegal\u002Fterms",{"links":74,"signIn":75,"contact":78,"cta":80},[],{"label":76,"href":77},"Sign in","url:portal.home",{"label":79,"href":62},"Get in touch",{"label":81,"href":82},"Download","url:routes.downloads",{},"data\u002Fshared\u002Fnavigation","VdirGIa8cUm6xLprbBeTKcw914JfyJjWfF-9hSVOX5Y",{"id":87,"extension":5,"links":88,"meta":660,"stem":661,"__hash__":662},"urlSettings\u002Fdata\u002Fshared\u002Furls.yml",[89,93,97,101,105,109,113,117,121,125,129,133,137,141,145,149,153,157,161,165,169,173,177,181,185,189,192,196,200,204,208,212,216,220,224,228,232,236,240,244,248,252,256,260,264,268,272,276,280,284,288,292,295,299,303,306,310,314,318,322,326,330,334,338,342,346,350,354,358,362,366,370,374,378,382,386,390,394,398,402,406,410,414,418,422,426,430,434,438,442,446,450,453,457,461,465,469,473,477,480,483,486,489,493,497,500,504,508,512,516,520,524,528,532,536,540,544,548,551,555,559,563,567,571,574,577,580,584,587,591,595,599,603,606,610,614,617,620,623,627,631,635,638,641,644,648,652,656],{"key":90,"label":91,"href":92},"downloads.desktop","Desktop download page","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fdownload",{"key":94,"label":95,"href":96},"downloads.macOS.dmgArm64","macOS DMG Apple Silicon","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_for_x\u002Fdmg-arm64\u002Fdownload",{"key":98,"label":99,"href":100},"downloads.macOS.dmgIntel","macOS DMG Intel","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_for_x\u002Fdmg\u002Fdownload",{"key":102,"label":103,"href":104},"downloads.macOS.pkg","macOS PKG","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fmacos_packaging\u002Fpkg\u002Fdownload",{"key":106,"label":107,"href":108},"downloads.macOS.universal","Desktop macOS Universal","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_for_x\u002Fmacos-universal\u002Fdownload",{"key":110,"label":111,"href":112},"downloads.macOS.pkgArm64","Desktop macOS PKG Apple Silicon","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fmacos_packaging\u002Fpkg-pfd-arm64\u002Fdownload",{"key":114,"label":115,"href":116},"downloads.macOS.pkgIntel","Desktop macOS PKG Intel","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fmacos_packaging\u002Fpkg-pfd\u002Fdownload",{"key":118,"label":119,"href":120},"downloads.windows.appinstaller","Windows App Installer","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fappinstaller\u002Fpieces_for_x.appinstaller",{"key":122,"label":123,"href":124},"downloads.windows.exe","Windows EXE","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_for_x\u002Fwindows-exe\u002Fdownload",{"key":126,"label":127,"href":128},"downloads.windows.msix","Windows MSIX","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_for_x\u002Fwindows-msix\u002Fdownload",{"key":130,"label":131,"href":132},"downloads.windows.suiteManager","Windows Suite Manager","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_suite_windows\u002Fappinstaller\u002Fdownload",{"key":134,"label":135,"href":136},"downloads.linux.flatpakRepo","Linux Flatpak repository","https:\u002F\u002Fbuilds.pieces.app\u002Fpieces-flatpak-repo\u002Fpieces-flatpak.flatpakrepo",{"key":138,"label":139,"href":140},"downloads.linux.snapDesktop","Linux Snap Desktop","https:\u002F\u002Fsnapcraft.io\u002Fpieces-for-developers",{"key":142,"label":143,"href":144},"downloads.linux.snapPiecesOS","Linux Snap PiecesOS","https:\u002F\u002Fsnapcraft.io\u002Fpieces-os",{"key":146,"label":147,"href":148},"downloads.linux.snap","Desktop Linux Snap package","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_for_x\u002Fsnap\u002Fdownload",{"key":150,"label":151,"href":152},"downloads.piecesOS.macOS.dmgArm64","PiecesOS macOS DMG Apple Silicon","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fos_server\u002Fdmg-arm64\u002Fdownload",{"key":154,"label":155,"href":156},"downloads.piecesOS.macOS.dmgIntel","PiecesOS macOS DMG Intel","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fos_server\u002Fdmg\u002Fdownload",{"key":158,"label":159,"href":160},"downloads.piecesOS.macOS.universal","PiecesOS macOS Universal","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fos_server\u002Fmacos-universal\u002Fdownload",{"key":162,"label":163,"href":164},"downloads.piecesOS.macOS.pkgArm64","PiecesOS macOS PKG Apple Silicon","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fmacos_packaging\u002Fpkg-pos-launch-only-arm64\u002Fdownload",{"key":166,"label":167,"href":168},"downloads.piecesOS.macOS.pkgIntel","PiecesOS macOS PKG Intel","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fmacos_packaging\u002Fpkg-pos-launch-only\u002Fdownload",{"key":170,"label":171,"href":172},"downloads.piecesOS.windows.appinstaller","PiecesOS Windows App Installer","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fappinstaller\u002Fos_server.appinstaller",{"key":174,"label":175,"href":176},"downloads.piecesOS.windows.exe","PiecesOS Windows EXE","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fos_server\u002Fwindows-exe\u002Fdownload",{"key":178,"label":179,"href":180},"downloads.piecesOS.windows.msix","PiecesOS Windows MSIX","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fos_server\u002Fwindows-msix\u002Fdownload",{"key":182,"label":183,"href":184},"downloads.piecesOS.linux.snap","PiecesOS Linux Snap package","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fos_server\u002Fsnap\u002Fdownload",{"key":186,"label":187,"href":188},"downloads.combined.macOS.appleSilicon","Combined macOS installer Apple Silicon","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fmacos_packaging\u002Fpkg-arm64\u002Fdownload",{"key":190,"label":191,"href":104},"downloads.combined.macOS.intel","Combined macOS installer Intel",{"key":193,"label":194,"href":195},"downloads.guides.macOS","macOS installation guide","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Fmacos-installation-guide",{"key":197,"label":198,"href":199},"downloads.guides.windows","Windows installation guide","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Fwindows-installation-guide",{"key":201,"label":202,"href":203},"downloads.guides.linux","Linux installation guide","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Flinux-installation-guide",{"key":205,"label":206,"href":207},"downloads.guides.piecesOS","PiecesOS manual installation","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies\u002Fpieces-os\u002Fmanual-installation",{"key":209,"label":210,"href":211},"extensions.chrome","Chrome extension","https:\u002F\u002Fchrome.google.com\u002Fwebstore\u002Fdetail\u002Fpieces-save-code-snippets\u002Figbgibhbfonhmjlechmeefimncpekepm",{"key":213,"label":214,"href":215},"extensions.firefox","Firefox add-on","https:\u002F\u002Faddons.mozilla.org\u002Fen-US\u002Ffirefox\u002Faddon\u002Fpieces-save-code-from-the-web\u002F",{"key":217,"label":218,"href":219},"extensions.edge","Edge add-on","https:\u002F\u002Fmicrosoftedge.microsoft.com\u002Faddons\u002Fdetail\u002Fpieces-save-code-snippet\u002Fhglfimcdgonaeeobjckfdabcldfidmim",{"key":221,"label":222,"href":223},"extensions.vscode","VS Code extension","https:\u002F\u002Fmarketplace.visualstudio.com\u002Fitems?itemName=MeshIntelligentTechnologiesInc.pieces-vscode",{"key":225,"label":226,"href":227},"extensions.visualStudio","Visual Studio extension","https:\u002F\u002Fmarketplace.visualstudio.com\u002Fitems?itemName=MeshIntelligentTechnologiesInc.PiecesVisualStudio",{"key":229,"label":230,"href":231},"extensions.jetbrains","JetBrains plugin","https:\u002F\u002Fplugins.jetbrains.com\u002Fplugin\u002F17328-pieces--save-search-share--reuse-code-snippets",{"key":233,"label":234,"href":235},"extensions.obsidian","Obsidian plugin","https:\u002F\u002Fobsidian.md\u002Fplugins?id=pieces-for-developers",{"key":237,"label":238,"href":239},"extensions.sublime","Sublime package","https:\u002F\u002Fpackagecontrol.io\u002Fpackages\u002FPieces",{"key":241,"label":242,"href":243},"extensions.neovim","Neovim plugin","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fplugin_neo_vim",{"key":245,"label":246,"href":247},"extensions.jupyterlab","JupyterLab plugin","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fjupyterlab-pieces",{"key":249,"label":250,"href":251},"extensions.cli","Pieces CLI","https:\u002F\u002Fpypi.org\u002Fproject\u002Fpieces-cli\u002F",{"key":253,"label":254,"href":255},"docs.home","Documentation home","https:\u002F\u002Fdocs.pieces.app",{"key":257,"label":258,"href":259},"docs.getStarted","Get started docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces",{"key":261,"label":262,"href":263},"docs.api","API docs","https:\u002F\u002Fdocs.pieces.app\u002Fapi",{"key":265,"label":266,"href":267},"docs.desktop.overview","Desktop overview","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop",{"key":269,"label":270,"href":271},"docs.desktop.onboarding","Desktop onboarding","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fonboarding",{"key":273,"label":274,"href":275},"docs.desktop.timeline","Desktop timeline docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Ftimeline",{"key":277,"label":278,"href":279},"docs.desktop.summaries","Desktop summaries docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fsingle-click-summaries",{"key":281,"label":282,"href":283},"docs.desktop.search","Desktop conversational search docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fconversational-search",{"key":285,"label":286,"href":287},"docs.desktop.drive","Desktop drive docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fdrive",{"key":289,"label":290,"href":291},"docs.desktop.account","Desktop account settings docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fconfiguration\u002Faccount",{"key":293,"label":294,"href":92},"docs.desktop.download","Desktop download docs",{"key":296,"label":297,"href":298},"docs.piecesOS.overview","PiecesOS overview docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies",{"key":300,"label":301,"href":302},"docs.piecesOS.details","PiecesOS details docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies\u002Fpieces-os",{"key":304,"label":305,"href":207},"docs.piecesOS.install","PiecesOS install docs",{"key":307,"label":308,"href":309},"docs.piecesOS.quickMenu","PiecesOS quick menu docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies\u002Fpieces-os\u002Fquick-menu",{"key":311,"label":312,"href":313},"docs.piecesOS.storage","On-device storage docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies\u002Fon-device-storage",{"key":315,"label":316,"href":317},"docs.piecesOS.troubleshooting","PiecesOS troubleshooting docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies\u002Fpieces-os\u002Ftroubleshooting",{"key":319,"label":320,"href":321},"docs.mcp.overview","MCP overview docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp",{"key":323,"label":324,"href":325},"docs.mcp.cursor","MCP Cursor docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fcursor",{"key":327,"label":328,"href":329},"docs.mcp.vscode","MCP VS Code docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fvs-code",{"key":331,"label":332,"href":333},"docs.mcp.claudeDesktop","MCP Claude Desktop docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fclaude-desktop",{"key":335,"label":336,"href":337},"docs.mcp.claudeCode","MCP Claude Code docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fclaude-code",{"key":339,"label":340,"href":341},"docs.mcp.claudeCowork","MCP Claude Cowork docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fclaude-cowork",{"key":343,"label":344,"href":345},"docs.mcp.githubCopilot","MCP GitHub Copilot docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fgithub-copilot",{"key":347,"label":348,"href":349},"docs.mcp.goose","MCP Goose docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fgoose",{"key":351,"label":352,"href":353},"docs.mcp.windsurf","MCP Windsurf docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fwindsurf",{"key":355,"label":356,"href":357},"docs.mcp.zed","MCP Zed docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fzed",{"key":359,"label":360,"href":361},"docs.mcp.jetbrains","MCP JetBrains docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fjetbrains-ides",{"key":363,"label":364,"href":365},"docs.mcp.continueDev","MCP Continue docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fcontinue-dev",{"key":367,"label":368,"href":369},"docs.mcp.cline","MCP Cline docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fcline",{"key":371,"label":372,"href":373},"docs.mcp.raycast","MCP Raycast docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fraycast",{"key":375,"label":376,"href":377},"docs.mcp.rovoDevCli","MCP Rovo Dev CLI docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Frovo-dev-cli",{"key":379,"label":380,"href":381},"docs.mcp.openaiCodexCli","MCP OpenAI Codex CLI docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fopenai-codex-cli",{"key":383,"label":384,"href":385},"docs.mcp.googleGeminiCli","MCP Google Gemini CLI docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fgoogle-gemini-cli",{"key":387,"label":388,"href":389},"docs.mcp.amazonQ","MCP Amazon Q docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Famazon-q-developer",{"key":391,"label":392,"href":393},"docs.mcp.chatgptDev","MCP ChatGPT Developer Mode docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fchatgpt-developer-mode",{"key":395,"label":396,"href":397},"docs.mcp.openclaw","MCP OpenClaw docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fopenclaw",{"key":399,"label":400,"href":401},"docs.mcp.mcpRemote","MCP Remote docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fmcp-remote",{"key":403,"label":404,"href":405},"docs.mcp.ngrok","MCP ngrok docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fngrok-setup",{"key":407,"label":408,"href":409},"docs.troubleshooting.macOS","macOS troubleshooting docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Ftroubleshooting\u002Fmacos",{"key":411,"label":412,"href":413},"docs.troubleshooting.windows","Windows troubleshooting docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Ftroubleshooting\u002Fwindows",{"key":415,"label":416,"href":417},"docs.troubleshooting.linux","Linux troubleshooting docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Ftroubleshooting\u002Flinux",{"key":419,"label":420,"href":421},"docs.privacy","Privacy and security docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fprivacy-security-your-data",{"key":423,"label":424,"href":425},"docs.support","Support docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fsupport",{"key":427,"label":428,"href":429},"portal.home","Pieces portal","https:\u002F\u002Fportal.pieces.app",{"key":431,"label":432,"href":433},"site.home","Website home","https:\u002F\u002Fpieces.app",{"key":435,"label":436,"href":437},"site.about","About page","https:\u002F\u002Fpieces.app\u002Fabout",{"key":439,"label":440,"href":441},"site.features","Features page","https:\u002F\u002Fpieces.app\u002Ffeatures",{"key":443,"label":444,"href":445},"site.plugins","Plugins page","https:\u002F\u002Fpieces.app\u002Fplugins",{"key":447,"label":448,"href":449},"site.contact","Contact page","https:\u002F\u002Fpieces.app\u002Fcontact",{"key":451,"label":58,"href":452},"site.updates","https:\u002F\u002Fpieces.app\u002Fupdates",{"key":454,"label":455,"href":456},"site.news","News","https:\u002F\u002Fpieces.app\u002Fnews",{"key":458,"label":459,"href":460},"site.events","Community events","https:\u002F\u002Fpieces.app\u002Fcommunity\u002Fevents",{"key":462,"label":463,"href":464},"site.userStories","User stories","https:\u002F\u002Fpieces.app\u002Fuser-stories",{"key":466,"label":467,"href":468},"site.academy","Academy","https:\u002F\u002Fpieces.app\u002Flearn\u002Facademy",{"key":470,"label":471,"href":472},"site.support","Website support","https:\u002F\u002Fpieces.app\u002Fsupport",{"key":474,"label":475,"href":476},"site.standup","Standup","https:\u002F\u002Fpieces.app\u002Fstandup",{"key":478,"label":34,"href":479},"site.blog","https:\u002F\u002Fcode.pieces.app\u002Fblog",{"key":481,"label":43,"href":482},"social.discord","https:\u002F\u002Fdiscord.gg\u002Fgetpieces",{"key":484,"label":46,"href":485},"social.x","https:\u002F\u002Fx.com\u002Fgetpieces",{"key":487,"label":488,"href":485},"social.twitter","Twitter",{"key":490,"label":491,"href":492},"social.instagram","Instagram","https:\u002F\u002Fwww.instagram.com\u002Fgetpieces\u002F",{"key":494,"label":495,"href":496},"social.tiktok","TikTok","https:\u002F\u002Fwww.tiktok.com\u002F@getpieces",{"key":498,"label":49,"href":499},"social.linkedin","https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fgetpieces\u002F",{"key":501,"label":502,"href":503},"social.youtube","YouTube","https:\u002F\u002Fyoutube.com\u002F@getpieces",{"key":505,"label":506,"href":507},"github.org","GitHub organization","https:\u002F\u002Fgithub.com\u002Fpieces-app",{"key":509,"label":510,"href":511},"github.support","GitHub support","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fsupport",{"key":513,"label":514,"href":515},"github.issues","GitHub issues","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fsupport\u002Fissues",{"key":517,"label":518,"href":519},"github.discussions","GitHub discussions","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fsupport\u002Fdiscussions",{"key":521,"label":522,"href":523},"github.documentation","GitHub documentation","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fdocumentation",{"key":525,"label":526,"href":527},"github.opensource","GitHub open source","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fopensource",{"key":529,"label":530,"href":531},"github.sdks.python","Python SDK","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fpieces-os-client-sdk-for-python",{"key":533,"label":534,"href":535},"github.sdks.typescript","TypeScript SDK","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fpieces-os-client-sdk-for-typescript",{"key":537,"label":538,"href":539},"github.sdks.dart","Dart SDK","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fpieces-os-client-sdk-for-dart",{"key":541,"label":542,"href":543},"github.sdks.kotlin","Kotlin SDK","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fpieces-os-client-sdk-for-kotlin",{"key":545,"label":546,"href":547},"github.plugins.obsidian","Obsidian plugin repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fobsidian-pieces",{"key":549,"label":550,"href":247},"github.plugins.jupyterlab","JupyterLab plugin repository",{"key":552,"label":553,"href":554},"github.plugins.sublime","Sublime plugin repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fplugin_sublime",{"key":556,"label":557,"href":558},"github.plugins.neovim","Neovim plugin repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fplugin_neovim",{"key":560,"label":561,"href":562},"github.cliAgent","CLI agent repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fcli-agent",{"key":564,"label":565,"href":566},"github.mcpDart","MCP Dart repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fmcp_dart",{"key":568,"label":569,"href":570},"github.awesomePieces","Awesome Pieces repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fawesome-pieces",{"key":572,"label":573,"href":66},"legal.privacyPolicy","Privacy policy",{"key":575,"label":576,"href":69},"legal.refundPolicy","Refund policy",{"key":578,"label":579,"href":72},"legal.terms","Terms",{"key":581,"label":582,"href":583},"legal.security","Legal security","https:\u002F\u002Fpieces.app\u002Flegal\u002Fsecurity",{"key":585,"label":586,"href":503},"videos.youtubeChannel","YouTube channel",{"key":588,"label":589,"href":590},"videos.gettingStartedDesktop","Getting started desktop video","https:\u002F\u002Fyoutu.be\u002FdUr1lRM_TYk",{"key":592,"label":593,"href":594},"videos.snippetDiscoveryxR","Snippet discovery video","https:\u002F\u002Fyoutu.be\u002FG6vb1USw-30",{"key":596,"label":597,"href":598},"sales.bookACall","Book a sales call","https:\u002F\u002Fcalendar.app.google\u002FWVUDtUfNy5Vst3sH7",{"key":600,"label":601,"href":602},"sales.enterprise","Enterprise form","https:\u002F\u002Fgetpieces.typeform.com\u002Fto\u002FaVQFTvpE",{"key":604,"label":605,"href":519},"sales.feedback","Feedback discussions",{"key":607,"label":608,"href":609},"sales.earlyAccess","Early access form","https:\u002F\u002Fgetpieces.typeform.com\u002Fearlyaccess",{"key":611,"label":612,"href":613},"sales.supportEmail","Support email","mailto:support@pieces.app",{"key":615,"label":616,"href":19},"routes.home","Home route",{"key":618,"label":619,"href":56},"routes.about","About route",{"key":621,"label":622,"href":12},"routes.downloads","Downloads route",{"key":624,"label":625,"href":626},"routes.thanks","Post-install thanks route","\u002Finstall\u002Fthanks",{"key":628,"label":629,"href":630},"routes.pricing","Pricing route","\u002Fpricing",{"key":632,"label":633,"href":634},"cta.freeTrial","Start free trial CTA (Pieces Pro) — used by \u002Fcampaigns\u002F* landing pages","https:\u002F\u002Fcampaigns.pieces.app\u002F",{"key":636,"label":637,"href":22},"routes.enterprise","Enterprise route",{"key":639,"label":640,"href":62},"routes.contact","Contact route",{"key":642,"label":643,"href":59},"routes.updates","Updates route",{"key":645,"label":646,"href":647},"routes.migrationError","Migration error route","\u002Fmigration\u002Ferror",{"key":649,"label":650,"href":651},"routes.authenticated","Post-login authenticated route","\u002Fauth\u002Fsigned-in",{"key":653,"label":654,"href":655},"routes.signed-out","Signed out route","\u002Fauth\u002Fsigned-out",{"key":657,"label":658,"href":659},"routes.authentication-error","Authentication error route","\u002Fauth\u002Ferror",{},"data\u002Fshared\u002Furls","NLkVE1lIUws518K-1QsSB6T-GvlXYIzcRL1HDhqarGQ",{"id":664,"title":665,"author":666,"authorPhoto":667,"authorPhotoAlt":668,"authorSlug":669,"body":670,"buttonText":2131,"buttonUrl":2131,"category":2132,"date":2133,"description":2134,"draft":2135,"editorsPick":2135,"extension":2136,"featured":2135,"image":2137,"imageAlt":2138,"meta":2139,"navigation":32,"ogImage":2131,"ogImageAlt":2131,"path":2140,"seo":2141,"stem":2142,"tags":2131,"__hash__":2143},"blog\u002Fblog\u002Fflutter-game-pong-game-flame.md","Flutter Game: Build a Pong Game with Flutter Flame","Rutvik Tak","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fdart-cli-foundations\u002Fauthor.jpeg","Rutvik Tak.","rutvik-tak",{"type":671,"value":672,"toc":2111},"minimark",[673,681,692,701,704,720,727,730,735,738,749,752,758,761,767,774,777,783,789,792,798,804,819,833,836,843,847,850,855,858,876,881,885,891,897,904,910],[674,675,676,677,680],"p",{},"Building apps with Flutter is really cool. You know what’s cooler?",[678,679],"br",{},"\nBuilding games. This article will show you how to build a Flutter game from start to finish with a Flutter Flame tutorial.",[674,682,683,684,691],{},"Let’s create a Flutter game! We can build the classic pong game in Flutter using the 🔥",[685,686,690],"a",{"href":687,"rel":688},"https:\u002F\u002Fflame-engine.org\u002F",[689],"nofollow","Flame"," game engine.",[674,693,694,695,700],{},"Flame is a 2D game engine built for Flutter. It’s built on top of the framework and simplifies game development. Flame provides us with everything we’ll need to build a ",[685,696,699],{"href":697,"rel":698},"https:\u002F\u002Fvimeo.com\u002F744509891",[689],"flutter game",".",[674,702,703],{},"Some of the concepts we’ll learn are:",[705,706,707,711,714,717],"ul",{},[708,709,710],"li",{},"Flutter game development",[708,712,713],{},"Collision detection in Flame",[708,715,716],{},"Building a simple AI opponent",[708,718,719],{},"Using Flame audio flutter",[674,721,722,726],{},[723,724,725],"strong",{},"Note:"," Knowledge of the basics of Flutter and Flame are required for this tutorial.",[674,728,729],{},"Grab a coffee; let’s get started! Flutter game on! 🎮",[731,732,734],"h2",{"id":733},"flutter-game-getting-started-with-game-development-in-flutter","Flutter Game: Getting Started with game development in flutter",[674,736,737],{},"Let’s create a new flutter project and enter the folder with the following commands:",[739,740,745],"pre",{"className":741,"code":743,"language":744},[742],"language-text","flutter create pong_game\n  cd pong_game\n","text",[746,747,743],"code",{"__ignoreMap":748},"",[674,750,751],{},"Next, add the required Flame dependencies:",[739,753,756],{"className":754,"code":755,"language":744},[742],"flutter pub add flame\n",[746,757,755],{"__ignoreMap":748},[674,759,760],{},"For the game, our file structure will look like this:",[739,762,765],{"className":763,"code":764,"language":744},[742],"-lib\u002F\n··|---main.dart\n··|---pong_game.dart\n··|---player_paddle.dart\n··|---ball.dart\n··|---ai_paddle.dart\n··|---scoretext.dart\n",[746,766,764],{"__ignoreMap":748},[674,768,769],{},[685,770,773],{"href":771,"rel":772},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=fc3248810b",[689],"Save this code",[674,775,776],{},"We’ll update our main.dart with the following code:",[739,778,781],{"className":779,"code":780,"language":744},[742],"void main() {\n  final game = PongGame();\n  runApp(GameWidget(game: game));\n}\n",[746,782,780],{"__ignoreMap":748},[674,784,785],{},[685,786,773],{"href":787,"rel":788},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=310c4baaa2",[689],[674,790,791],{},"Now, in the pong_game.dart file, we’ll add the the following:",[739,793,796],{"className":794,"code":795,"language":744},[742],"class PongGame extends FlameGame\n with HasCollisionDetection, HasKeyboardHandlerComponents {\n PongGame();\n \n @override\n Future\u003Cvoid> onLoad() async {}\n \n @override\n @mustCallSuper\n KeyEventResult onKeyEvent(\n   RawKeyEvent event,\n Set\u003CLogicalKeyboardKey> keysPressed,\n ) {\n super.onKeyEvent(event, keysPressed);\n \n return KeyEventResult.handled;\n }\n}\n",[746,797,795],{"__ignoreMap":748},[674,799,800],{},[685,801,773],{"href":802,"rel":803},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=bc43458be4",[689],[674,805,806,807,810,811,814,815,818],{},"Here, we have the ",[746,808,809],{},"PongGame","declared. Notice that it has two mixings: ",[746,812,813],{},"HasCollisionDetection"," and ",[746,816,817],{},"HasKeyboardHandlerComponents",". This will let Flame know that our game is going to use these two things and allow us to work with collision detection and take keyboard inputs at the component level.",[674,820,821,822,825,826,829,830,832],{},"We’re also overriding the ",[746,823,824],{},"onKeyEvent"," here and returning ",[746,827,828],{},"KeyEventResult.handled",". This is because if you’re on macOS, then you’ll notice key press sounds as you’re receiving keyboard inputs in the game. Returning ",[746,831,828],{},"will disable those sounds.",[674,834,835],{},"Build & run:",[674,837,838],{},[839,840],"img",{"alt":841,"src":842},"Flutter Game: Flutter Flame Example.","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fflutter-game-pong-game-flame\u002Fimg-001.png",[731,844,846],{"id":845},"flutter-flame-collision-detection","Flutter Flame Collision Detection",[674,848,849],{},"Before moving on to building our game, let’s take a look at how collision detection works in Flame. This will be important for us as we’ll need to set up HitBoxes for our game bodies, know when these bodies collide with each other and react accordingly.",[851,852,854],"h3",{"id":853},"hitboxes","HitBoxes",[674,856,857],{},"In many game systems, collision detection works by having a HitBox around the game object. HitBoxes react to collisions and can send callbacks with the collision information.",[674,859,860,861,864,865,864,868,871,872,875],{},"Flame supports adding different HitBoxes to our components like ",[746,862,863],{},"PolygonHitBox",", ",[746,866,867],{},"RectangleHitBox",[746,869,870],{},"CircleHitBox"," or ",[746,873,874],{},"ScreenHitBox",", which is usually used for declaring the world boundaries\u002Fscreen edges that components may collide with.",[674,877,878,880],{},[723,879,725],{}," We can use multiple HitBoxes on a component to provide more accurate collision detection for it. For example, a game character can have separate HitBoxes around its arms, its legs, and so on.",[851,882,884],{"id":883},"enable-collision-detection","Enable Collision Detection",[674,886,887,888,890],{},"For this, we first need to add the ",[746,889,813],{},"mixing to our Flame game.",[674,892,893],{},[839,894],{"alt":895,"src":896}," add \"HasCollisionDetection\" .","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fflutter-game-pong-game-flame\u002Fimg-002.png",[674,898,899,900,903],{},"For the components, we want to get notified when they collide with other bodies that are capable of collision. For this, we’ll add the ",[746,901,902],{},"CollisionCallbacks"," mixing to those components.",[739,905,908],{"className":906,"code":907,"language":744},[742],"class MyComponent extends PositionComponent with CollisionCallbacks {\n\n @override\n void onCollision(Set\u003CVector2> intersectionPoints, PositionComponent other) {\n \u002F\u002F TODO: implement onCollision\n super.onCollision(intersectionPoints, other);\n  }  \n\n @override\n void onCollisionStart(Set\u003CVector2> intersectionPoints, PositionComponent other) {\n \u002F\u002F TODO: implement onCollisionStart\n super.onCollisionStart(intersectionPoints, other);\n  }\n\n @override\n void onCollisionEnd(PositionComponent other) {\n \u002F\u002F TODO: implement onCollisionEnd\n super.onCollisionEnd(other);\n  }\n\n}\n",[746,909,907],{"__ignoreMap":748},[911,912,913,927,932,935,939,942,959,962,965,971,981,987,993,998,1002,1005,1012,1018,1024,1047,1057,1069,1075,1081,1083,1089,1093,1096,1099,1115,1121,1127,1133,1146,1150,1153,1163,1169,1175,1177,1183,1186,1193,1199,1208,1214,1220,1223,1229,1235,1254,1259,1265,1271,1274,1290,1293,1299,1305,1313,1316,1319,1321,1327,1331,1334,1340,1346,1362,1368,1399,1413,1419,1425,1431,1433,1439,1443,1452,1460,1466,1472,1475,1479,1485,1491,1497,1503,1506,1520,1522,1528,1532,1537,1543,1549,1562,1567,1573],"save",{"this":748,"code":748},[674,914,915,916,864,919,922,923,926],{},"Adding this mixing allows us to be notified when a body collides with other bodies through callbacks such as ",[746,917,918],{},"onCollision",[746,920,921],{},"onCollisionStart","and",[746,924,925],{},"onCollisionEnd",". These callbacks also provide the intersection points and the reference to the other body the component is colliding with.",[674,928,929,931],{},[723,930,725],{}," The Collision Detection API only lets us know when two bodies collide. What happens upon collision is up to us!",[674,933,934],{},"Now, let’s move on to the different components of our game.",[731,936,938],{"id":937},"game-components","Game Components",[674,940,941],{},"Our Pong game mainly consists of the following components:",[705,943,944,947,950,953,956],{},[708,945,946],{},"Game Boundaries",[708,948,949],{},"Player paddle",[708,951,952],{},"Ball",[708,954,955],{},"AI opponent paddle",[708,957,958],{},"Scoring system",[851,960,946],{"id":961},"game-boundaries",[674,963,964],{},"Our ball is going to collide with the boundaries of our game\u002Fscreen. We need to know when this happens so that we can either bounce it off of the top or bottom of the screen or update the players’ score if it’s colliding with the left or right of the screen.",[674,966,967,968,970],{},"For this, we’ll declare game boundaries by adding the ",[746,969,874],{}," component.",[674,972,973,974,977,978,980],{},"Replace the ",[746,975,976],{},"onload","method within ",[746,979,809],{}," with the following:",[739,982,985],{"className":983,"code":984,"language":744},[742],"@override\n Future\u003Cvoid> onLoad() async {\n   addAll([\n     ScreenHitbox()\n   ]);\n }\n",[746,986,984],{"__ignoreMap":748},[674,988,989],{},[685,990,773],{"href":991,"rel":992},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=fce246ae92",[689],[674,994,995,997],{},[746,996,874],{}," will represent the edges of our game screen. If any other components collide with the edges, we’ll be notified of the collision.",[851,999,1001],{"id":1000},"player-paddle","Player Paddle",[674,1003,1004],{},"Now, we’ll add the player paddle to the flutter game tutorial.",[674,1006,1007,1008,1011],{},"Create a new file called ",[746,1009,1010],{},"player_paddle.dart"," and add the following to it:",[739,1013,1016],{"className":1014,"code":1015,"language":744},[742],"\u002F\u002F TODO: add key event enum\n \nclass PlayerPaddle extends PositionComponent\n with HasGameRef\u003CFlameGame>, CollisionCallbacks {\n late final RectangleHitbox paddleHitBox;\n late final RectangleComponent paddle;\n \n \u002F\u002F TODO: add variable key event and speed variables\n \n @override\n Future\u003Cvoid>? onLoad() {\n \u002F\u002F TODO: implement onLoad\n \n final worldRect = gameRef.size.toRect();\n \n   size = Vector2(10, 100);\n   position.x = worldRect.width * 0.9 - 10;\n   position.y = worldRect.height \u002F 2 - size.y \u002F 2;\n   paddle = RectangleComponent(\n     size: size,\n     paint: Paint()..color = Colors.blue,\n   );\n \n   paddleHitBox = RectangleHitbox(\n     size: size,\n   );\n \n   addAll([\n     paddle,\n     paddleHitBox,\n   ]);\n \n \u002F\u002F TODO: add keyboard listener component\n \n return super.onLoad();\n }\n \n \u002F\u002FTODO: add update code for moving paddle\n}\n",[746,1017,1015],{"__ignoreMap":748},[674,1019,1020],{},[685,1021,773],{"href":1022,"rel":1023},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=d72c49a5a3",[689],[674,1025,1026,1027,1030,1031,1034,1035,814,1038,1040,1041,1043,1044,1046],{},"Our ",[746,1028,1029],{},"PlayerPaddle","is a ",[746,1032,1033],{},"PositionComponent"," with the ",[746,1036,1037],{},"HasGameRef",[746,1039,902],{}," mixing. The ",[746,1042,1037],{}," mixing will allow us to get the game reference and check for any values in our game world. ",[746,1045,902],{}," mixing, as we discussed, will add support for setting collision callbacks.",[674,1048,1049,1050,1053,1054,1056],{},"In the ",[746,1051,1052],{},"onLoad"," method, we’re setting the size for our paddle component and positioning it at the center-right of the screen. We also added a ",[746,1055,867],{},"of the same size as our paddle so that it can detect collisions.",[674,1058,1059,1060,1062,1063,1065,1066,1068],{},"Within the ",[746,1061,976],{}," method of the ",[746,1064,809],{},"component*,* add the ",[746,1067,1029],{},":",[739,1070,1073],{"className":1071,"code":1072,"language":744},[742],"@override\n Future\u003Cvoid> onLoad() async {\n   addAll(\n     [\n       ...\n       .....\n       PlayerPaddle(),\n \n     ],\n   );\n }\n",[746,1074,1072],{"__ignoreMap":748},[674,1076,1077],{},[685,1078,773],{"href":1079,"rel":1080},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=432c44aadd",[689],[674,1082,835],{},[674,1084,1085],{},[839,1086],{"alt":1087,"src":1088},"Run Flutter flame classic pong.","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fflutter-game-pong-game-flame\u002Fimg-003.png",[851,1090,1092],{"id":1091},"player-keyboard-controls","Player keyboard controls",[674,1094,1095],{},"Flame offers two different ways to take keyboard inputs; one at the game level and the other at the component level.",[674,1097,1098],{},"Let’s take a look at receiving keyboard inputs at the component level.",[674,1100,1101,1102,1104,1105,1107,1108,1110,1111,1114],{},"We’ll make sure our ",[746,1103,809],{}," component has the ",[746,1106,817],{}," mixing. Within our ",[746,1109,1029],{}," component, we’ll use the ",[746,1112,1113],{},"KeyboardListenerComponent",", through which we can set callbacks for different key events.",[674,1116,1117,1118,1120],{},"Add the following component within your ",[746,1119,976],{}," method:",[739,1122,1125],{"className":1123,"code":1124,"language":744},[742],"add(\n     KeyboardListenerComponent(\n       keyDown: {\n         LogicalKeyboardKey.arrowDown: (keysPressed) {\n \n return true;\n         },\n         LogicalKeyboardKey.arrowUp: (keysPressed) {\n \n return true;\n         },\n       },\n       keyUp: {\n         LogicalKeyboardKey.arrowDown: (keysPressed) {\n \n return true;\n         },\n         LogicalKeyboardKey.arrowUp: (keysPressed) {\n \n return true;\n         },\n       },\n     ),\n   );\n",[746,1126,1124],{"__ignoreMap":748},[674,1128,1129],{},[685,1130,773],{"href":1131,"rel":1132},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=1cc447b348",[689],[674,1134,1135,1136,1138,1139,814,1142,1145],{},"This adds the ",[746,1137,1113],{},". We’ll be registering callbacks for ",[746,1140,1141],{},"arrowDown",[746,1143,1144],{},"arrowUp"," events when the respective keys are either pressed or released.",[851,1147,1149],{"id":1148},"moving-player-paddle","Moving Player Paddle",[674,1151,1152],{},"Now that we’re receiving keyboard events, let’s see how we can move our paddle.",[674,1154,1155,1156,1159,1160,1162],{},"Let’s try updating our paddle position along the y-axis by 50 when the down arrow is pressed and by -50 when the up arrow is pressed. Update ",[746,1157,1158],{},"keyDown"," within the ",[746,1161,1113],{}," with the following (you may need to hot restart your game to reflect the new changes):",[739,1164,1167],{"className":1165,"code":1166,"language":744},[742],"keyDown: {\n         LogicalKeyboardKey.arrowDown: (keysPressed) {\n           position.y += 50;\n \n return true;\n         },\n         LogicalKeyboardKey.arrowUp: (keysPressed) {\n           position.y -= 50;\n \n return true;\n         },\n       },\n",[746,1168,1166],{"__ignoreMap":748},[674,1170,1171],{},[685,1172,773],{"href":1173,"rel":1174},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=bb6849bba2",[689],[674,1176,835],{},[674,1178,1179],{},[839,1180],{"alt":1181,"src":1182},"Run game in Flutter using Flame.","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fflutter-game-pong-game-flame\u002Fimg-004.gif",[674,1184,1185],{},"You’ll see that the paddle moves, but its movement is janky. It’s not smooth. 🤷",[674,1187,1188,1189,1192],{},"This is because the position is not updated consistently with the passage of time. To achieve smooth movement, we’ll need to update its position from within the ",[746,1190,1191],{},"update"," method.",[674,1194,1195,1196,1198],{},"Currently, in Flame, there’s no possible way to know which keys are pressed within the ",[746,1197,1191],{}," method. For this, we’ll first set up a variable that’ll tell us which key was pressed so we can update the position accordingly.",[674,1200,973,1201,1204,1205,1207],{},[746,1202,1203],{},"\u002F\u002F TODO: add key event enum"," within ",[746,1206,1010],{}," with the following code:",[739,1209,1212],{"className":1210,"code":1211,"language":744},[742],"enum KeyEventEnum {\n  up,\n  down,\n  none,\n}\n",[746,1213,1211],{"__ignoreMap":748},[674,1215,1216],{},[685,1217,773],{"href":1218,"rel":1219},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=8aee42a78b",[689],[674,1221,1222],{},"Declare the following variables within the paddle component:",[739,1224,1227],{"className":1225,"code":1226,"language":744},[742],"KeyEventEnum keyPressed = KeyEventEnum.none;\n static const double speed = 400;\n",[746,1228,1226],{"__ignoreMap":748},[674,1230,1231],{},[685,1232,773],{"href":1233,"rel":1234},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=7c044eb7b0",[689],[1236,1237,1238,1248],"ol",{},[708,1239,1240,1243,1244,1247],{},[746,1241,1242],{},"keyPressed",": Lets us know which key is pressed. When none of the keys are pressed, we’ll update this variable to ",[746,1245,1246],{},"KeyboardEventEnum.none",", so we can know to stop updating the position.",[708,1249,1250,1253],{},[746,1251,1252],{},"speed",": Paddle moving speed.",[674,1255,1256,1257,980],{},"Replace the previously added ",[746,1258,1113],{},[739,1260,1263],{"className":1261,"code":1262,"language":744},[742],"add(\n      KeyboardListenerComponent(\n        keyDown: {\n          LogicalKeyboardKey.arrowDown: (keysPressed) {\n            keyPressed = KeyEventEnum.down;\n\n return true;\n          },\n          LogicalKeyboardKey.arrowUp: (keysPressed) {\n            keyPressed = KeyEventEnum.up;\n\n return true;\n          },\n        },\n        keyUp: {\n          LogicalKeyboardKey.arrowDown: (keysPressed) {\n            keyPressed = KeyEventEnum.none;\n\n return true;\n          },\n          LogicalKeyboardKey.arrowUp: (keysPressed) {\n            keyPressed = KeyEventEnum.none;\n\n return true;\n          },\n        },\n      ),\n    );\n",[746,1264,1262],{"__ignoreMap":748},[674,1266,1267],{},[685,1268,773],{"href":1269,"rel":1270},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=a14248b436",[689],[674,1272,1273],{},"Here, we’re doing two things:",[1236,1275,1276,1283],{},[708,1277,1278,1279,1282],{},"Setting the ",[746,1280,1281],{},"KeyboardEventEnum"," to up\u002Fdown based on the key pressed.",[708,1284,1285,1286,1289],{},"Resetting it to ",[746,1287,1288],{},"KeyEventEnum.none"," when the key is released.",[674,1291,1292],{},"Add the following code, which overrides the update method for the component:",[739,1294,1297],{"className":1295,"code":1296,"language":744},[742],"@override\n void update(double dt) {\n \u002F\u002F TODO: implement update\n super.update(dt);\n if (keyPressed == KeyEventEnum.down) {\n final updatedPosition = position.y + speed * dt;\n if (updatedPosition \u003C gameRef.size.y - paddle.height) {\n       position.y = updatedPosition;\n     }\n   }\n if (keyPressed == KeyEventEnum.up) {\n final updatedPosition = position.y - speed * dt;\n if (updatedPosition > 0) {\n       position.y = updatedPosition;\n     }\n   }\n }\n",[746,1298,1296],{"__ignoreMap":748},[674,1300,1301],{},[685,1302,773],{"href":1303,"rel":1304},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=39e043aef0",[689],[674,1306,1307,1308,1312],{},"Here, we update the paddle position based on the key pressed. This time, instead of passing a fixed displacement, we’re updating the position by the ",[1309,1310,1311],"em",{},"speed*dt(=distance)"," value.",[674,1314,1315],{},"We also check if our paddle is going out of the bounds of the game window. If it is, then we stop updating the position.",[674,1317,1318],{},"We can test our updates by holding down the up or down arrow keys and seeing the paddle move smoothly.",[674,1320,835],{},[674,1322,1323],{},[839,1324],{"alt":1325,"src":1326},"Run flutter flame.","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fflutter-game-pong-game-flame\u002Fimg-005.gif",[731,1328,1330],{"id":1329},"flutter-game-adding-the-ball","Flutter Game: Adding the Ball",[674,1332,1333],{},"Create a new file called ball.dart and add the following code to it:",[739,1335,1338],{"className":1336,"code":1337,"language":744},[742],"import 'dart:math' as math;\n \nclass Ball extends CircleComponent\n with HasGameRef\u003CPongGame>, CollisionCallbacks {\n Ball() {\n   paint = Paint()..color = Colors.white;\n   radius = 10;\n }\n \n \u002F\u002F 1.\n late Vector2 velocity;\n \u002F\u002F 2.\n static const double speed = 500;\n \u002F\u002F 3.\n static const degree = math.pi \u002F 180;\n \n \u002F\u002F 6.\n @override\n Future\u003Cvoid>? onLoad() {\n   _resetBall;\n final hitBox = CircleHitbox(\n     radius: radius,\n   );\n \n   addAll([\n     hitBox,\n   ]);\n \n return super.onLoad();\n }\n \n @override\n void update(double dt) {\n super.update(dt);\n    position += velocity * dt;\n  }\n \n \n \u002F\u002F 4.\n void get _resetBall {\n   position = gameRef.size \u002F 2;\n final spawnAngle = getSpawnAngle;\n \n final vx = math.cos(spawnAngle * degree) * speed;\n final vy = math.sin(spawnAngle * degree) * speed;\n   velocity = Vector2(\n     vx,\n     vy,\n   );\n }\n \n \u002F\u002F 5.\n double get getSpawnAngle {\n final sideToThrow = math.Random().nextBool();\n \n final random = math.Random().nextDouble();\n final spawnAngle = sideToThrow\n       ? lerpDouble(-35, 35, random)!\n       : lerpDouble(145, 215, random)!;\n \n return spawnAngle;\n }\n }\n",[746,1339,1337],{"__ignoreMap":748},[674,1341,1342],{},[685,1343,773],{"href":1344,"rel":1345},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=1b4245b15f",[689],[674,1347,1348,1349,1352,1353,1356,1357,814,1359,1361],{},"Our ball is a ",[746,1350,1351],{},"CircleComponent",", which is a ",[746,1354,1355],{},"PositionedComponent"," but circular with ",[746,1358,1037],{},[746,1360,902],{}," mixing. We also defined the color and radius of the ball within its constructor.",[674,1363,1364,1365,1367],{},"Along with defining the HitBox for our ball in the ",[746,1366,976],{},"method, we have some other things here:",[1236,1369,1370,1376,1381,1387,1393],{},[708,1371,1372,1375],{},[746,1373,1374],{},"velocity",": A 2D vector representing the ball's velocity.",[708,1377,1378,1380],{},[746,1379,1252],{},": A constant value that will calculate the ball's velocity.",[708,1382,1383,1386],{},[746,1384,1385],{},"degree",": The degree to radian constant.",[708,1388,1389,1392],{},[746,1390,1391],{},"_resetBall",": Spawns (positions) the ball at the center of the screen and launches it in a random direction with some initial velocity.",[708,1394,1395,1398],{},[746,1396,1397],{},"getSpawnAngle",": Calculates the angle at which the ball will be thrown upon spawning.",[674,1400,1401,1402,1404,1405,1407,1408,1411],{},"Finally, within the ",[746,1403,1191],{}," method, we update the ball's position with respect to its ",[746,1406,1374],{}," and the time passed, i.e., ",[746,1409,1410],{},"dt",[1309,1412,700],{},[674,1414,1415,1416,1418],{},"Let’s add the ball component to our ",[746,1417,809],{}," component:",[739,1420,1423],{"className":1421,"code":1422,"language":744},[742],"@override\n Future\u003Cvoid> onLoad() async {\n   addAll(\n     [\n       ...\n       .....\n       Ball(),\n     ],\n   );\n }\n",[746,1424,1422],{"__ignoreMap":748},[674,1426,1427],{},[685,1428,773],{"href":1429,"rel":1430},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=3bd642b823",[689],[674,1432,835],{},[674,1434,1435],{},[839,1436],{"alt":1437,"src":1438},"Runing the pong game that's starting to take shape.","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fflutter-game-pong-game-flame\u002Fimg-006.gif",[851,1440,1442],{"id":1441},"collision-detection-with-the-ball","Collision Detection with the Ball",[674,1444,1445,1446,1448,1449,1451],{},"Now that we have our ",[746,1447,952],{}," spawning in the center of the screen and moving, let's get to the interesting part of the game: making the ball bounce when it collides with a ",[746,1450,1029],{}," or the top and bottom edges of the game.",[674,1453,1454,1455,1457,1458,970],{},"Add the following code, which overrides the ",[746,1456,921],{},"method within the ",[746,1459,952],{},[739,1461,1464],{"className":1462,"code":1463,"language":744},[742],"@override\n @mustCallSuper\n void onCollisionStart(\n Set\u003CVector2> intersectionPoints,\n   PositionComponent other,\n ) {\n super.onCollisionStart(intersectionPoints, other);\n final collisionPoint = intersectionPoints.first;\n\n \u002F\u002F TODO: add edges collision update\n \n  \u002F\u002F TODO: add player paddle collision update\n \n \u002F\u002F TODO: add ai paddle collision update\n }\n",[746,1465,1463],{"__ignoreMap":748},[674,1467,1468],{},[685,1469,773],{"href":1470,"rel":1471},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=8aa94480c9",[689],[674,1473,1474],{},"This callback provides us with the intersection\u002Fcollision points for our component and the ref to the component we are colliding with. These will be useful in the next section, where we deal with collision logic for different bodies.",[851,1476,1478],{"id":1477},"edge-collision-update","Edge Collision Update",[674,1480,1481,1482,1207],{},"We’ll first update the ball velocity to bounce off of the top and bottom edges of the screen. Replace the ",[746,1483,1484],{},"\u002F\u002F TODO: add edge collision update",[739,1486,1489],{"className":1487,"code":1488,"language":744},[742],"if (other is ScreenHitbox) {\n \u002F\u002F Left Side Collision\n if (collisionPoint.x == 0) {\n \u002F\u002F TODO: update player score\n     }\n \u002F\u002F Right Side Collision\n if (collisionPoint.x == gameRef.size.x) {\n \u002F\u002F TODO: update ai score\n     }\n \u002F\u002F Top Side Collision\n if (collisionPoint.y == 0) {\n       velocity.x = velocity.x;\n       velocity.y = -velocity.y;\n   \u002F\u002F TODO: play the collision sound\n     }\n \u002F\u002F Bottom Side Collision\n if (collisionPoint.y == gameRef.size.y) {\n       velocity.x = velocity.x;\n       velocity.y = -velocity.y;\n  \u002F\u002F TODO: play the collision sound\n     }\n   }\n",[746,1490,1488],{"__ignoreMap":748},[674,1492,1493],{},[685,1494,773],{"href":1495,"rel":1496},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=afa941beb6",[689],[674,1498,1499,1500,1502],{},"Here, we’re first checking if the body that our ball collided with is ",[746,1501,874],{}," or not. If it is, we check for the edge with which our ball collided.",[674,1504,1505],{},"We don’t want the ball to bounce off of the left and right edges. We’ll later add some code there to update the players’ scores.",[674,1507,1508,1509,1511,1512,1515,1516,1519],{},"If it’s the top or bottom edge, we reverse the ball’s ",[746,1510,1374],{}," in the y direction. Test it by changing the ball’s ",[746,1513,1514],{},"spawnAngle","to ",[723,1517,1518],{},"90"," such that it’ll be thrown towards the top or bottom edges.",[674,1521,835],{},[674,1523,1524],{},[839,1525],{"alt":1526,"src":1527},"Use \"spawnAngle\" for the pong game to move at an angle within this pong game.","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fflutter-game-pong-game-flame\u002Fimg-007.gif",[851,1529,1531],{"id":1530},"paddle-collision-update","Paddle Collision Update",[674,1533,973,1534,980],{},[746,1535,1536],{},"\u002F\u002F TODO: paddle collision update",[739,1538,1541],{"className":1539,"code":1540,"language":744},[742],"if (other is PlayerPaddle) {\n final paddleRect = other.paddle.toAbsoluteRect();\n \n     updateBallTrajectory(collisionPoint, paddleRect);\n \u002F\u002F TODO: play the collision sound\n   }\n",[746,1542,1540],{"__ignoreMap":748},[674,1544,1545],{},[685,1546,773],{"href":1547,"rel":1548},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=82044fa5e4",[689],[674,1550,1551,1552,1554,1557,1558,1561],{},"If the collided object is the ",[746,1553,1029],{},[1309,1555,1556],{},","," we first calculate the ",[746,1559,1560],{},"paddleRect",", which is the bounding rectangle of the component in the global coordinate space.",[674,1563,1059,1564,1566],{},[746,1565,952],{}," component, add the following method:",[739,1568,1571],{"className":1569,"code":1570,"language":744},[742],"void updateBallTrajectory(Vector2 collisionPoint, Rect paddleRect) {\n final isLeftHit = collisionPoint.x == paddleRect.left;\n final isRightHit = collisionPoint.x == paddleRect.right;\n final isTopHit = collisionPoint.y == paddleRect.bottom;\n final isBottomHit = collisionPoint.y == paddleRect.top;\n \n final isLeftOrRight = isLeftHit || isRightHit;\n final isTopOrBottom = isTopHit || isBottomHit;\n \n if (isLeftOrRight) {\n     velocity.x = -velocity.x;\n     velocity.y = velocity.y;\n   }\n if (isTopOrBottom) {\n     velocity.x = velocity.x;\n     velocity.y = -velocity.y;\n   }\n }\n",[746,1572,1570],{"__ignoreMap":748},[911,1574,1575,1590,1592,1598,1602,1605,1611,1614,1620,1626,1632,1641,1647,1653,1655,1661,1665,1668,1671,1674,1693,1701,1707,1713,1719,1725,1731,1737,1740,1742,1748,1752,1763,1769,1775,1781,1783,1789,1793,1796,1802,1808,1826,1833,1847,1853,1859,1866,1872,1878,1881,1885,1901,1907,1913,1923,1929,1935,1947,1950,1952,1958,1962,1965,1972,1978,1991,1997,2002,2008,2014,2026,2032,2038,2041,2047,2051,2058,2064,2068,2071,2074,2088,2095,2099,2108],{"this":748,"code":748},[674,1576,1577,1578,1580,1581,1034,1584,1586,1587,1589],{},"This method will reverse the ball’s ",[746,1579,1374],{}," along the x- or y-axis, depending on where it touches the paddle, which is known by checking the ",[746,1582,1583],{},"collisionPoint",[746,1585,1560],{}," position. If the collision is on the left or right side, we reverse the ",[746,1588,1374],{}," along the x-axis. If the collision is on the top or bottom, we reverse the velocity along the y-axis.",[674,1591,835],{},[674,1593,1594],{},[839,1595],{"alt":1596,"src":1597},"Run the game with the moving paddle and working pong game.","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fflutter-game-pong-game-flame\u002Fimg-008.gif",[731,1599,1601],{"id":1600},"flutter-game-ai-paddle","Flutter Game: AI Paddle",[674,1603,1604],{},"Now that we’ve got the ball bouncing off the edges and the paddle, let’s add the AI opponent 🤖 you can play against.",[674,1606,1607,1608,1610],{},"It’ll be very similar to how we did the ",[746,1609,1029],{},"; the only part that’s going to be different is how it moves.",[674,1612,1613],{},"Add the following code to a new file called ai_paddle.dart:",[739,1615,1618],{"className":1616,"code":1617,"language":744},[742],"class AIPaddle extends PositionComponent\n with HasGameRef\u003CFlameGame>, CollisionCallbacks {\n late final RectangleHitbox paddleHitBox;\n late final RectangleComponent paddle;\n \n @override\n Future\u003Cvoid>? onLoad() {\n \u002F\u002F TODO: implement onLoad\n \n final worldRect = gameRef.size.toRect();\n \n   size = Vector2(10, 100);\n   position.x = worldRect.width * 0.1;\n   position.y = worldRect.height \u002F 2 - size.y \u002F 2;\n   paddle = RectangleComponent(\n     size: size,\n     paint: Paint()..color = Colors.red,\n   );\n \n   paddleHitBox = RectangleHitbox(\n     size: size,\n   );\n \n   addAll([\n     paddle,\n     paddleHitBox,\n   ]);\n \n return super.onLoad();\n }\n}\n",[746,1619,1617],{"__ignoreMap":748},[674,1621,1622],{},[685,1623,773],{"href":1624,"rel":1625},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=6be148818d",[689],[674,1627,1628,1629,1631],{},"Construction of our AI paddle is very similar to the ",[746,1630,1029],{},", except we position it at the center on the left side.",[674,1633,1634,1635,1638,1639,1418],{},"Don’t forget to add the ",[746,1636,1637],{},"AIPaddle"," component to our ",[746,1640,809],{},[739,1642,1645],{"className":1643,"code":1644,"language":744},[742],"@override\n Future\u003Cvoid> onLoad() async {\n   addAll(\n     [\n       ...\n       .....\n       AIPaddle(),\n     ],\n   );\n }\n",[746,1646,1644],{"__ignoreMap":748},[674,1648,1649],{},[685,1650,773],{"href":1651,"rel":1652},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=67934c853f",[689],[674,1654,835],{},[674,1656,1657],{},[839,1658],{"alt":1659,"src":1660},"Add a flutter game opponent.","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fflutter-game-pong-game-flame\u002Fimg-009.png",[851,1662,1664],{"id":1663},"ai-paddle-movement-logic","AI Paddle Movement Logic",[674,1666,1667],{},"There are many different ways to build this AI opponent, control its behavior, detect how it should move, set how fast it should move and decide how challenging it should be to play against.",[674,1669,1670],{},"For our game, we won’t be building an AI that will be literally impossible to beat, just a simple AI that we can play against peacefully. ✌️",[674,1672,1673],{},"Our AI Paddle will follow two rules depending on the ball's position:",[1236,1675,1676,1685],{},[708,1677,1678,1679,1681,1682,1684],{},"If the ",[746,1680,1637],{}," is below the ",[746,1683,952],{},", it should move up towards the ball.",[708,1686,1678,1687,1689,1690,1692],{},[746,1688,1637],{}," is above the ",[746,1691,952],{},", it should move down towards the ball.",[674,1694,1695,1696,1698,1699,980],{},"Following these rules, override the ",[746,1697,1191],{}," method for ",[746,1700,1637],{},[739,1702,1705],{"className":1703,"code":1704,"language":744},[742],"@override\n void update(double dt) {\n \u002F\u002F TODO: implement update\n super.update(dt);\n \n final ball = gameRef.children.singleWhere((child) => child is Ball) as Ball;\n \n if (ball.y > position.y) {\n     position.y += (400 * dt);\n   }\n \n if (ball.y \u003C position.y) {\n     position.y -= (400 * dt);\n   }\n }\n",[746,1706,1704],{"__ignoreMap":748},[674,1708,1709],{},[685,1710,773],{"href":1711,"rel":1712},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=970440b656",[689],[674,1714,1715,1716,1718],{},"Here, we first get the reference to the ball from our game world. Depending on the earlier rules we defined, we move the ",[746,1717,1637],{},"up or down.",[674,1720,1721,1722,1724],{},"In some cases, the ",[746,1723,1637],{}," will follow the ball even if it goes outside the game boundaries; to prevent this, replace the code after we query\u002Fget the ball with the following:",[739,1726,1729],{"className":1727,"code":1728,"language":744},[742],"final ballPositionWrtPaddleHeight = ball.y + (size.y);\n final isOutOfBounds =\n       ballPositionWrtPaddleHeight > gameRef.size.y || ball.y \u003C 0;\n \n if (!isOutOfBounds) {\n if (ball.y > position.y) {\n       position.y += (400 * dt);\n     }\n \n if (ball.y \u003C position.y) {\n       position.y -= (400 * dt);\n     }\n   }\n",[746,1730,1728],{"__ignoreMap":748},[674,1732,1733],{},[685,1734,773],{"href":1735,"rel":1736},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=80b54181a3",[689],[674,1738,1739],{},"Here, we check if the updated position will be within the boundaries of our game world. If it isn’t, we don’t update the position of the paddle.",[674,1741,835],{},[674,1743,1744],{},[839,1745],{"alt":1746,"src":1747},"Allow your opponent to move within the flutter game.","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fflutter-game-pong-game-flame\u002Fimg-010.gif",[851,1749,1751],{"id":1750},"ai-collision-update","AI Collision Update",[674,1753,1059,1754,1756,1757,1759,1760,980],{},[746,1755,952],{}," component’s ",[746,1758,1191],{}," method, replace",[746,1761,1762],{},"\u002F\u002F TODO: add AI paddle collision update",[739,1764,1767],{"className":1765,"code":1766,"language":744},[742],"if (other is AIPaddle) {\n final paddleRect = other.paddle.toAbsoluteRect();\n\n      updateBallTrajectory(collisionPoint, paddleRect);\n\n \u002F\u002F TODO: play the collision sound\n    }\n",[746,1768,1766],{"__ignoreMap":748},[674,1770,1771],{},[685,1772,773],{"href":1773,"rel":1774},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=c2944ca779",[689],[674,1776,1777,1778,1780],{},"Now, our ball will also collide with the ",[746,1779,1637],{}," and bounce off of it after a collision.",[674,1782,835],{},[674,1784,1785],{},[839,1786],{"alt":1787,"src":1788},"Add collision update to pong game.","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fflutter-game-pong-game-flame\u002Fimg-011.gif",[731,1790,1792],{"id":1791},"add-the-scoring-system","Add the Scoring System",[674,1794,1795],{},"Now onto the final part of the game— adding the scoring system. Create a new file called score_text.dart and add the following to it:",[739,1797,1800],{"className":1798,"code":1799,"language":744},[742],"class ScoreText extends TextComponent with HasGameRef\u003CPongGame> {\n late int score;\n \n ScoreText.aiScore({\n this.score = 0,\n })  : _textPaint = TextPaint(textDirection: TextDirection.ltr),\n super(\n         anchor: Anchor.center,\n       );\n \n ScoreText.playerScore({\n this.score = 0,\n })  : _textPaint = TextPaint(textDirection: TextDirection.rtl),\n super(\n         anchor: Anchor.center,\n       );\n \n late final TextPaint _textPaint;\n \n @override\n Future\u003Cvoid>? onLoad() {\n   score = 0;\n final textOffset =\n       (_textPaint.textDirection == TextDirection.ltr ? -1 : 1) * 50;\n   position.setValues(gameRef.size.x \u002F 2 + textOffset, gameRef.size.y * 0.1);\n   text = score.toString();\n \n return super.onLoad();\n }\n \n @override\n void render(Canvas canvas) {\n   _textPaint.render(canvas, '$score', Vector2.zero());\n }\n}\n",[746,1801,1799],{"__ignoreMap":748},[674,1803,1804],{},[685,1805,773],{"href":1806,"rel":1807},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=50b444a1e9",[689],[674,1809,1810,1811,1814,1815,1818,1819,1822,1823,1825],{},"This ",[746,1812,1813],{},"ScoreText"," will hold and display the score for each player. It has two factory constructors; one for ",[746,1816,1817],{},"aiScore"," and one for ",[746,1820,1821],{},"player",". Within its ",[746,1824,1052],{}," method, we position our scores at the top center and offset them a little in the left or right direction based on whether it’s the player’s or the AI’s score.",[674,1827,1828,1829,1832],{},"We’ve also overridden the ",[746,1830,1831],{},"render","method to show the latest score as it’s updated.",[674,1834,1835,1836,1838,1839,814,1841,1844,1845,1418],{},"Now, within our ",[746,1837,809],{},"component, add the following ",[746,1840,1817],{},[746,1842,1843],{},"playerScore"," variables which will hold the ",[746,1846,1813],{},[739,1848,1851],{"className":1849,"code":1850,"language":744},[742],"late final ScoreText aiPlayer;\nlate final ScoreText player;\n",[746,1852,1850],{"__ignoreMap":748},[674,1854,1855],{},[685,1856,773],{"href":1857,"rel":1858},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=d927459402",[689],[674,1860,1861,1862,1865],{},"Update the ",[746,1863,1864],{},"addAll"," method by adding these two components:",[739,1867,1870],{"className":1868,"code":1869,"language":744},[742],"aiPlayer = ScoreText.aiScore(),\n  player = ScoreText.playerScore(),\n",[746,1871,1869],{"__ignoreMap":748},[674,1873,1874],{},[685,1875,773],{"href":1876,"rel":1877},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=0e0443b912",[689],[674,1879,1880],{},"Now that we have the score components in place, the next thing we want to do is update the scores whenever the player or the AI scores.",[851,1882,1884],{"id":1883},"update-the-score","Update the Score",[674,1886,1059,1887,1889,1890,1892,1893,1896,1897,1900],{},[746,1888,921],{}," method of our ",[746,1891,952],{},"component*,* replace the code from ",[746,1894,1895],{},"\u002F\u002F Left Side Collision","to",[746,1898,1899],{},"\u002F\u002F Right Side Collision","with the following:",[739,1902,1905],{"className":1903,"code":1904,"language":744},[742],"\u002F\u002F Left Side Collision\n if (collisionPoint.x == 0) {\n final player = gameRef.player;\n       updatePlayerScore(player);\n     }\n \u002F\u002F Right Side Collision\n if (collisionPoint.x == gameRef.size.x) {\n final player = gameRef.aiPlayer;\n       updatePlayerScore(player);\n     }\n",[746,1906,1904],{"__ignoreMap":748},[674,1908,1909],{},[685,1910,773],{"href":1911,"rel":1912},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=11ae4abbb5",[689],[674,1914,1915,1916,1919,1920,1922],{},"Add the following ",[746,1917,1918],{},"updatePlayerScore"," method in the ",[746,1921,952],{},"component:",[739,1924,1927],{"className":1925,"code":1926,"language":744},[742],"import 'dart:async' as dartAsync;\n \nvoid updatePlayerScore(ScoreText player) {\n   player.score += 1;\n   dartAsync.Timer(const Duration(seconds: 1), () {\n     _resetBall;\n   });\n }\n",[746,1928,1926],{"__ignoreMap":748},[674,1930,1931],{},[685,1932,773],{"href":1933,"rel":1934},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=5e5c4ebd3b",[689],[674,1936,1937,1938,1940,1941,1944,1945,700],{},"This method takes in the ",[746,1939,1813],{}," object and increments its ",[746,1942,1943],{},"score"," by 1. After that, we set up a timer for 1 second to respawn the ball in the center by calling ",[746,1946,1391],{},[674,1948,1949],{},"Now as you or the AI opponent misses the ball, the opposite player will get the point and their score will be updated.",[674,1951,835],{},[674,1953,1954],{},[839,1955],{"alt":1956,"src":1957},"Run the AI opponent in the flutter game.","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fflutter-game-pong-game-flame\u002Fimg-012.gif",[731,1959,1961],{"id":1960},"flutter-game-adding-collision-audio","Flutter Game: Adding Collision Audio",[674,1963,1964],{},"A game without audio is definitely not something you would play. So, let’s add a collision sound whenever the ball collides with other game bodies.",[674,1966,1967,1968,1971],{},"Run the following command to add the ",[723,1969,1970],{},"flame_audio"," dependency:",[739,1973,1976],{"className":1974,"code":1975,"language":744},[742],"flutter pub add flame_audio\n",[746,1977,1975],{"__ignoreMap":748},[674,1979,1980,1981,1986,1987,1990],{},"Once that’s done, download the audio file for the collision sound ",[685,1982,1985],{"href":1983,"rel":1984},"https:\u002F\u002Fgithub.com\u002Frutvik110\u002FPong-Game\u002Ftree\u002Fmaster\u002Fassets\u002Faudio",[689],"here",". Add the audio files to the ",[746,1988,1989],{},"assets\u002Faudio"," folder. Make sure to add the audio folder to the assets section in the pubspec as shown:",[674,1992,1993],{},[839,1994],{"alt":1995,"src":1996},"Download the audio file for the collision sound for your flutter game.","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fflutter-game-pong-game-flame\u002Fimg-013.png",[674,1998,1999,2000,1418],{},"Let’s add the following method in our ",[746,2001,952],{},[739,2003,2006],{"className":2004,"code":2005,"language":744},[742],"void get _playCollisionAudio {\n  FlameAudio.play(\"ball_hit.wav\");\n}\n",[746,2007,2005],{"__ignoreMap":748},[674,2009,2010],{},[685,2011,773],{"href":2012,"rel":2013},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=6b35449a4c",[689],[674,2015,2016,2017,1062,2019,2021,2022,2025],{},"We’ll need to play the collision sound after every collision. Within the ",[746,2018,921],{},[746,2020,952],{}," component, replace the ",[746,2023,2024],{},"\u002F\u002FTODO: play the collision sound"," with:",[739,2027,2030],{"className":2028,"code":2029,"language":744},[742],"_playCollisionAudio\n",[746,2031,2029],{"__ignoreMap":748},[674,2033,2034],{},[685,2035,773],{"href":2036,"rel":2037},"https:\u002F\u002Ftakrutvik.pieces.cloud\u002F?p=f9454e864c",[689],[674,2039,2040],{},"Final demo:",[674,2042,2043],{},[839,2044],{"alt":2045,"src":2046},"Successfully playing your pong game.","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fflutter-game-pong-game-flame\u002Fimg-014.gif",[731,2048,2050],{"id":2049},"bonus","Bonus",[674,2052,2053,2054,2057],{},"In the final demo, our Ball speeds up a little when it collides with either the player’s paddle or the AI paddle. For this, we’re simply increasing the ball's velocity in the y-direction by giving it some additional ",[746,2055,2056],{},"nudgeSpeed",". I suggest making the nudge speed 300\u002F200, but you can make it whatever you prefer.",[674,2059,2060],{},[839,2061],{"alt":2062,"src":2063},"Bonus code to all the pong game ball to speed up.","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fflutter-game-pong-game-flame\u002Fimg-015.png",[731,2065,2067],{"id":2066},"flutter-game-summary","Flutter Game: Summary",[674,2069,2070],{},"Congrats! 🥳 We just built a Pong game with Flame!🔥",[674,2072,2073],{},"While building this game, we learned about:",[705,2075,2076,2079,2082,2085],{},[708,2077,2078],{},"CollisionDetection API in Flame.",[708,2080,2081],{},"Building a simple AI opponent.",[708,2083,2084],{},"Adding a scoring system to the game.",[708,2086,2087],{},"Adding audio to your game.",[674,2089,2090,2091,700],{},"You can download the source code ",[685,2092,1985],{"href":2093,"rel":2094},"https:\u002F\u002Fgithub.com\u002Frutvik110\u002FPong-Game",[689],[851,2096,2098],{"id":2097},"next","Next",[674,2100,2101,2102,2107],{},"Flame has been growing steadily in the Flutter community and many exciting things are coming up in the recent updates. Check out the ",[685,2103,2106],{"href":2104,"rel":2105},"https:\u002F\u002Fgithub.com\u002Fflame-engine\u002Fawesome-flame#articles--tutorials",[689],"Awesome Flame"," repository for some amazing examples built with Flame.😋",[674,2109,2110],{},"Flame will continue to grow and allow us to build cool games with Flutter. We at Pieces are really excited about it. Stay tuned for our upcoming articles where we’ll explore Flame to build amazing games! 🎮",{"title":748,"searchDepth":2112,"depth":2112,"links":2113},2,[2114,2115,2120,2126],{"id":733,"depth":2112,"text":734},{"id":845,"depth":2112,"text":846,"children":2116},[2117,2119],{"id":853,"depth":2118,"text":854},3,{"id":883,"depth":2118,"text":884},{"id":937,"depth":2112,"text":938,"children":2121},[2122,2123,2124,2125],{"id":961,"depth":2118,"text":946},{"id":1000,"depth":2118,"text":1001},{"id":1091,"depth":2118,"text":1092},{"id":1148,"depth":2118,"text":1149},{"id":1329,"depth":2112,"text":1330,"children":2127},[2128,2129,2130],{"id":1441,"depth":2118,"text":1442},{"id":1477,"depth":2118,"text":1478},{"id":1530,"depth":2118,"text":1531},null,"Software Development","2022-09-21T00:00:00.000Z","Let's Build a Flutter Game! Building apps with Flutter is simple, let’s create the classic pong game using the Flame game engine.",false,"md","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fflutter-game-pong-game-flame\u002Fhero.jpeg","Build a Pong Game with Flutter Flame.",{},"\u002Fblog\u002Fflutter-game-pong-game-flame",{"title":665,"description":2134},"blog\u002Fflutter-game-pong-game-flame","K_kj2tjRr_44aV7n1tgczIKy9riLQtgNIPFYqCje0aw",{"id":2145,"title":666,"body":2146,"description":2150,"draft":2135,"extension":2136,"meta":2153,"navigation":32,"path":2154,"photo":2155,"photoAlt":2131,"seo":2156,"stem":2157,"__hash__":2158},"authors\u002Fauthors\u002Frutvik-tak.md",{"type":671,"value":2147,"toc":2151},[2148],[674,2149,2150],{},"Rutvik was introduced to world of coding through animations and games. From there he loved the creative aspects of coding and solving interesting problems. At Pieces, he takes care of the Web Extension and making sure it works across all different browsers. Beside coding, he loves to help with support, dev rel content. When he's not doing either of that, you can find him working on animations, doing calisthenics or indoor rock climbing on weekends.",{"title":748,"searchDepth":2112,"depth":2112,"links":2152},[],{},"\u002Fauthors\u002Frutvik-tak","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fauthors\u002Frutvik-tak.jpg",{"title":666,"description":2150},"authors\u002Frutvik-tak","HWkSe6txnNhDCCk74fgii1tqQGBDyeC8l7Qydvtn8ko",{"left":2160,"top":2160,"width":2161,"height":2161,"rotate":2160,"vFlip":2135,"hFlip":2135,"body":2162},0,24,"\u003Cpath fill=\"currentColor\" d=\"m7.825 13l4.9 4.9q.3.3.288.7t-.313.7q-.3.275-.7.288t-.7-.288l-6.6-6.6q-.15-.15-.213-.325T4.426 12t.063-.375t.212-.325l6.6-6.6q.275-.275.688-.275t.712.275q.3.3.3.713t-.3.712L7.825 11H19q.425 0 .713.288T20 12t-.288.713T19 13z\"\u002F>"]