[{"data":1,"prerenderedAt":559},["ShallowReactive",2],{"navigation_docs":3,"-game-dev-mental-model":64,"-game-dev-mental-model-surround":554},[4,26,43],{"title":5,"path":6,"stem":7,"children":8,"page":25},"Using Parallax","\u002Fusing-parallax","1.using-parallax",[9,13,17,21],{"title":10,"path":11,"stem":12},"Getting Started","\u002Fusing-parallax\u002Fgetting-started","1.using-parallax\u002F0.getting-started",{"title":14,"path":15,"stem":16},"Project Structure","\u002Fusing-parallax\u002Fproject-structure","1.using-parallax\u002F1.project-structure",{"title":18,"path":19,"stem":20},"Working with the Agent","\u002Fusing-parallax\u002Fworking-with-agent","1.using-parallax\u002F2.working-with-agent",{"title":22,"path":23,"stem":24},"Deploying Your Game","\u002Fusing-parallax\u002Fdeploying","1.using-parallax\u002F3.deploying",false,{"title":27,"path":28,"stem":29,"children":30,"page":25},"Features","\u002Ffeatures","2.features",[31,35,39],{"title":32,"path":33,"stem":34},"Feature Overview","\u002Ffeatures\u002Foverview","2.features\u002F0.overview",{"title":36,"path":37,"stem":38},"Roadmap","\u002Ffeatures\u002Froadmap","2.features\u002F1.roadmap",{"title":40,"path":41,"stem":42},"Feature Requests","\u002Ffeatures\u002Frequests","2.features\u002F2.requests",{"title":44,"path":45,"stem":46,"children":47,"page":25},"Game Dev","\u002Fgame-dev","3.game-dev",[48,52,56,60],{"title":49,"path":50,"stem":51},"The Mental Model","\u002Fgame-dev\u002Fmental-model","3.game-dev\u002F0.mental-model",{"title":53,"path":54,"stem":55},"Best Practices","\u002Fgame-dev\u002Fbest-practices","3.game-dev\u002F1.best-practices",{"title":57,"path":58,"stem":59},"Love2D Patterns","\u002Fgame-dev\u002Flove2d-patterns","3.game-dev\u002F2.love2d-patterns",{"title":61,"path":62,"stem":63},"Agent Integration (MCP + Context7)","\u002Fgame-dev\u002Fagent-integration","3.game-dev\u002F3.agent-integration",{"id":65,"title":49,"body":66,"description":547,"extension":548,"links":549,"meta":550,"navigation":120,"path":50,"seo":552,"stem":51,"__hash__":553},"docs\u002F3.game-dev\u002F0.mental-model.md",{"type":67,"value":68,"toc":538},"minimark",[69,73,77,82,85,173,188,192,195,276,287,291,294,367,370,374,385,443,453,457,467,509,513,524,528,534],[70,71,49],"h1",{"id":72},"the-mental-model",[74,75,76],"p",{},"Before writing a line of Lua, it helps to have a clear mental model for how Love2D games are structured. This is the same model the Parallax agent uses internally — so the more you think in these terms, the better your collaboration with the agent will be.",[78,79,81],"h2",{"id":80},"the-game-loop","The game loop",[74,83,84],{},"Love2D exposes three core callbacks. Everything in your game flows through them:",[86,87,92],"pre",{"className":88,"code":89,"language":90,"meta":91,"style":91},"language-lua shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","function love.load()\n  -- Run once at startup: load assets, initialise state\nend\n\nfunction love.update(dt)\n  -- Run every frame: advance game state, handle physics, check input\n  -- dt = time since last frame in seconds (always use this for movement)\nend\n\nfunction love.draw()\n  -- Run every frame: render the current state to the screen\n  -- Never modify game state here — only read it\nend\n","lua","",[93,94,95,103,109,115,122,128,134,140,145,150,156,162,168],"code",{"__ignoreMap":91},[96,97,100],"span",{"class":98,"line":99},"line",1,[96,101,102],{},"function love.load()\n",[96,104,106],{"class":98,"line":105},2,[96,107,108],{},"  -- Run once at startup: load assets, initialise state\n",[96,110,112],{"class":98,"line":111},3,[96,113,114],{},"end\n",[96,116,118],{"class":98,"line":117},4,[96,119,121],{"emptyLinePlaceholder":120},true,"\n",[96,123,125],{"class":98,"line":124},5,[96,126,127],{},"function love.update(dt)\n",[96,129,131],{"class":98,"line":130},6,[96,132,133],{},"  -- Run every frame: advance game state, handle physics, check input\n",[96,135,137],{"class":98,"line":136},7,[96,138,139],{},"  -- dt = time since last frame in seconds (always use this for movement)\n",[96,141,143],{"class":98,"line":142},8,[96,144,114],{},[96,146,148],{"class":98,"line":147},9,[96,149,121],{"emptyLinePlaceholder":120},[96,151,153],{"class":98,"line":152},10,[96,154,155],{},"function love.draw()\n",[96,157,159],{"class":98,"line":158},11,[96,160,161],{},"  -- Run every frame: render the current state to the screen\n",[96,163,165],{"class":98,"line":164},12,[96,166,167],{},"  -- Never modify game state here — only read it\n",[96,169,171],{"class":98,"line":170},13,[96,172,114],{},[74,174,175,179,180,183,184,187],{},[176,177,178],"strong",{},"The golden rule:"," ",[93,181,182],{},"love.update"," changes state. ",[93,185,186],{},"love.draw"," reads state and renders it. Never cross these concerns.",[78,189,191],{"id":190},"entities-as-tables","Entities as tables",[74,193,194],{},"In Love2D, the natural way to represent game objects (player, enemies, bullets, items) is as Lua tables with method-like functions:",[86,196,198],{"className":88,"code":197,"language":90,"meta":91,"style":91},"local Player = {}\nPlayer.__index = Player\n\nfunction Player.new(x, y)\n  return setmetatable({ x = x, y = y, speed = 200, vy = 0 }, Player)\nend\n\nfunction Player:update(dt)\n  -- movement, gravity, collision\nend\n\nfunction Player:draw()\n  love.graphics.rectangle('fill', self.x, self.y, 16, 24)\nend\n\nreturn Player\n",[93,199,200,205,210,214,219,224,228,232,237,242,246,250,255,260,265,270],{"__ignoreMap":91},[96,201,202],{"class":98,"line":99},[96,203,204],{},"local Player = {}\n",[96,206,207],{"class":98,"line":105},[96,208,209],{},"Player.__index = Player\n",[96,211,212],{"class":98,"line":111},[96,213,121],{"emptyLinePlaceholder":120},[96,215,216],{"class":98,"line":117},[96,217,218],{},"function Player.new(x, y)\n",[96,220,221],{"class":98,"line":124},[96,222,223],{},"  return setmetatable({ x = x, y = y, speed = 200, vy = 0 }, Player)\n",[96,225,226],{"class":98,"line":130},[96,227,114],{},[96,229,230],{"class":98,"line":136},[96,231,121],{"emptyLinePlaceholder":120},[96,233,234],{"class":98,"line":142},[96,235,236],{},"function Player:update(dt)\n",[96,238,239],{"class":98,"line":147},[96,240,241],{},"  -- movement, gravity, collision\n",[96,243,244],{"class":98,"line":152},[96,245,114],{},[96,247,248],{"class":98,"line":158},[96,249,121],{"emptyLinePlaceholder":120},[96,251,252],{"class":98,"line":164},[96,253,254],{},"function Player:draw()\n",[96,256,257],{"class":98,"line":170},[96,258,259],{},"  love.graphics.rectangle('fill', self.x, self.y, 16, 24)\n",[96,261,263],{"class":98,"line":262},14,[96,264,114],{},[96,266,268],{"class":98,"line":267},15,[96,269,121],{"emptyLinePlaceholder":120},[96,271,273],{"class":98,"line":272},16,[96,274,275],{},"return Player\n",[74,277,278,279,282,283,286],{},"Every entity has ",[93,280,281],{},":update(dt)"," and ",[93,284,285],{},":draw()",". The game loop calls both for every active entity. This pattern scales from 3 entities to 300.",[78,288,290],{"id":289},"state-machines-for-game-flow","State machines for game flow",[74,292,293],{},"Use a simple state machine to manage high-level game states — menus, gameplay, pause, game over:",[86,295,297],{"className":88,"code":296,"language":90,"meta":91,"style":91},"local state = 'menu' -- 'menu' | 'playing' | 'paused' | 'game_over'\n\nfunction love.update(dt)\n  if state == 'playing' then\n    player:update(dt)\n    for _, e in ipairs(enemies) do e:update(dt) end\n  end\nend\n\nfunction love.draw()\n  if state == 'menu' then drawMenu()\n  elseif state == 'playing' then drawGame()\n  elseif state == 'paused' then drawPause()\n  end\nend\n",[93,298,299,304,308,312,317,322,327,332,336,340,344,349,354,359,363],{"__ignoreMap":91},[96,300,301],{"class":98,"line":99},[96,302,303],{},"local state = 'menu' -- 'menu' | 'playing' | 'paused' | 'game_over'\n",[96,305,306],{"class":98,"line":105},[96,307,121],{"emptyLinePlaceholder":120},[96,309,310],{"class":98,"line":111},[96,311,127],{},[96,313,314],{"class":98,"line":117},[96,315,316],{},"  if state == 'playing' then\n",[96,318,319],{"class":98,"line":124},[96,320,321],{},"    player:update(dt)\n",[96,323,324],{"class":98,"line":130},[96,325,326],{},"    for _, e in ipairs(enemies) do e:update(dt) end\n",[96,328,329],{"class":98,"line":136},[96,330,331],{},"  end\n",[96,333,334],{"class":98,"line":142},[96,335,114],{},[96,337,338],{"class":98,"line":147},[96,339,121],{"emptyLinePlaceholder":120},[96,341,342],{"class":98,"line":152},[96,343,155],{},[96,345,346],{"class":98,"line":158},[96,347,348],{},"  if state == 'menu' then drawMenu()\n",[96,350,351],{"class":98,"line":164},[96,352,353],{},"  elseif state == 'playing' then drawGame()\n",[96,355,356],{"class":98,"line":170},[96,357,358],{},"  elseif state == 'paused' then drawPause()\n",[96,360,361],{"class":98,"line":262},[96,362,331],{},[96,364,365],{"class":98,"line":267},[96,366,114],{},[74,368,369],{},"The Parallax agent will suggest this pattern automatically for any project that involves multiple game screens.",[78,371,373],{"id":372},"collision-via-bumplua","Collision via bump.lua",[74,375,376,377,384],{},"Love2D has no built-in collision resolution. The standard choice is ",[378,379,383],"a",{"href":380,"rel":381},"https:\u002F\u002Fgithub.com\u002Fkikito\u002Fbump.lua",[382],"nofollow","bump.lua"," — a simple AABB library that handles tunnelling, slopes, and one-way platforms.",[86,386,388],{"className":88,"code":387,"language":90,"meta":91,"style":91},"local bump = require('lib.bump')\nlocal world = bump.newWorld(64) -- cell size\n\n-- Add items to the world\nworld:add(player, player.x, player.y, player.w, player.h)\n\n-- In player:update(dt)\nlocal goalX = self.x + dx\nlocal goalY = self.y + dy\nlocal actualX, actualY, cols = world:move(player, goalX, goalY)\nself.x, self.y = actualX, actualY\n",[93,389,390,395,400,404,409,414,418,423,428,433,438],{"__ignoreMap":91},[96,391,392],{"class":98,"line":99},[96,393,394],{},"local bump = require('lib.bump')\n",[96,396,397],{"class":98,"line":105},[96,398,399],{},"local world = bump.newWorld(64) -- cell size\n",[96,401,402],{"class":98,"line":111},[96,403,121],{"emptyLinePlaceholder":120},[96,405,406],{"class":98,"line":117},[96,407,408],{},"-- Add items to the world\n",[96,410,411],{"class":98,"line":124},[96,412,413],{},"world:add(player, player.x, player.y, player.w, player.h)\n",[96,415,416],{"class":98,"line":130},[96,417,121],{"emptyLinePlaceholder":120},[96,419,420],{"class":98,"line":136},[96,421,422],{},"-- In player:update(dt)\n",[96,424,425],{"class":98,"line":142},[96,426,427],{},"local goalX = self.x + dx\n",[96,429,430],{"class":98,"line":147},[96,431,432],{},"local goalY = self.y + dy\n",[96,434,435],{"class":98,"line":152},[96,436,437],{},"local actualX, actualY, cols = world:move(player, goalX, goalY)\n",[96,439,440],{"class":98,"line":158},[96,441,442],{},"self.x, self.y = actualX, actualY\n",[74,444,445,448,449,452],{},[176,446,447],{},"The agent uses bump.lua for all collision by default."," If you want a different approach, tell it in ",[93,450,451],{},".parallax\u002Fcontext.json",".",[78,454,456],{"id":455},"assets-as-module-level-variables","Assets as module-level variables",[74,458,459,460,463,464,466],{},"Load assets in ",[93,461,462],{},"love.load"," and store them in module-level variables. Never load inside ",[93,465,186],{}," — that causes frame drops:",[86,468,470],{"className":88,"code":469,"language":90,"meta":91,"style":91},"-- main.lua\nlocal sprites = {}\nlocal sounds  = {}\n\nfunction love.load()\n  sprites.player = love.graphics.newImage('assets\u002Fimages\u002Fplayer.png')\n  sounds.jump    = love.audio.newSource('assets\u002Faudio\u002Fjump.ogg', 'static')\nend\n",[93,471,472,477,482,487,491,495,500,505],{"__ignoreMap":91},[96,473,474],{"class":98,"line":99},[96,475,476],{},"-- main.lua\n",[96,478,479],{"class":98,"line":105},[96,480,481],{},"local sprites = {}\n",[96,483,484],{"class":98,"line":111},[96,485,486],{},"local sounds  = {}\n",[96,488,489],{"class":98,"line":117},[96,490,121],{"emptyLinePlaceholder":120},[96,492,493],{"class":98,"line":124},[96,494,102],{},[96,496,497],{"class":98,"line":130},[96,498,499],{},"  sprites.player = love.graphics.newImage('assets\u002Fimages\u002Fplayer.png')\n",[96,501,502],{"class":98,"line":136},[96,503,504],{},"  sounds.jump    = love.audio.newSource('assets\u002Faudio\u002Fjump.ogg', 'static')\n",[96,506,507],{"class":98,"line":142},[96,508,114],{},[78,510,512],{"id":511},"coordinate-system","Coordinate system",[74,514,515,516,519,520,523],{},"Love2D's origin ",[93,517,518],{},"(0, 0)"," is the ",[176,521,522],{},"top-left"," corner of the window. X increases to the right, Y increases downward. This trips up developers coming from other engines. The Parallax agent accounts for this automatically.",[78,525,527],{"id":526},"what-this-means-for-prompting","What this means for prompting",[74,529,530,531,533],{},"When you prompt the agent using these concepts — entities, state machines, bump.lua collisions — it produces cleaner, more idiomatic code with fewer revisions. The more your ",[93,532,451],{}," captures these conventions, the better.",[535,536,537],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":91,"searchDepth":105,"depth":105,"links":539},[540,541,542,543,544,545,546],{"id":80,"depth":105,"text":81},{"id":190,"depth":105,"text":191},{"id":289,"depth":105,"text":290},{"id":372,"depth":105,"text":373},{"id":455,"depth":105,"text":456},{"id":511,"depth":105,"text":512},{"id":526,"depth":105,"text":527},"How to think about building Love2D games — the conceptual framework the Parallax agent uses.","md",null,{"ogImage":551},"\u002Flogo.png",{"title":49,"description":547},"ZOpqnDxiDPnWZ5ZI7LPvXkevNl8K3p2Q02u9_wESNRo",[555,557],{"title":40,"path":41,"stem":42,"description":556,"children":-1},"Community-submitted feature requests for Parallax — vote, discuss, and submit your own.",{"title":53,"path":54,"stem":55,"description":558,"children":-1},"Proven patterns for Love2D games — what works, what to avoid, and why. Used by the Parallax agent.",1778756091134]