[{"data":1,"prerenderedAt":676},["ShallowReactive",2],{"navigation_docs":3,"-game-dev-best-practices":64,"-game-dev-best-practices-surround":671},[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":53,"body":66,"description":664,"extension":665,"links":666,"meta":667,"navigation":118,"path":54,"seo":669,"stem":55,"__hash__":670},"docs\u002F3.game-dev\u002F1.best-practices.md",{"type":67,"value":68,"toc":648},"minimark",[69,73,77,87,90,132,140,144,157,234,242,249,288,292,305,334,344,348,354,406,413,421,427,480,487,490,519,527,534,554,557,561,564,637,644],[70,71,53],"h1",{"id":72},"best-practices",[74,75,76],"p",{},"These are the patterns the Parallax agent applies by default. Understanding them makes you a better collaborator with the agent — and a better Love2D developer.",[78,79,81,82,86],"h2",{"id":80},"always-use-dt-for-movement","Always use ",[83,84,85],"code",{},"dt"," for movement",[74,88,89],{},"Never move objects by a fixed pixel amount per frame. Frame rate varies — fixed movement causes different speeds on different machines:",[91,92,97],"pre",{"className":93,"code":94,"language":95,"meta":96,"style":96},"language-lua shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","-- Bad\nself.x = self.x + 5\n\n-- Good\nself.x = self.x + self.speed * dt\n","lua","",[83,98,99,107,113,120,126],{"__ignoreMap":96},[100,101,104],"span",{"class":102,"line":103},"line",1,[100,105,106],{},"-- Bad\n",[100,108,110],{"class":102,"line":109},2,[100,111,112],{},"self.x = self.x + 5\n",[100,114,116],{"class":102,"line":115},3,[100,117,119],{"emptyLinePlaceholder":118},true,"\n",[100,121,123],{"class":102,"line":122},4,[100,124,125],{},"-- Good\n",[100,127,129],{"class":102,"line":128},5,[100,130,131],{},"self.x = self.x + self.speed * dt\n",[74,133,134,136,137,139],{},[83,135,85],{}," is the time (in seconds) since the last frame. Multiply any per-frame delta by ",[83,138,85],{}," to get frame-rate-independent movement.",[78,141,143],{"id":142},"separate-update-and-draw-concerns","Separate update and draw concerns",[74,145,146,149,150,153,154,156],{},[83,147,148],{},"love.update"," handles logic. ",[83,151,152],{},"love.draw"," handles rendering. Never modify game state inside ",[83,155,152],{},":",[91,158,160],{"className":93,"code":159,"language":95,"meta":96,"style":96},"-- Bad — side effect inside draw\nfunction love.draw()\n  score = score + 1 -- don't do this\n  love.graphics.print(score, 10, 10)\nend\n\n-- Good\nfunction love.update(dt)\n  if coinCollected then score = score + 10 end\nend\n\nfunction love.draw()\n  love.graphics.print(score, 10, 10)\nend\n",[83,161,162,167,172,177,182,187,192,197,203,209,214,219,224,229],{"__ignoreMap":96},[100,163,164],{"class":102,"line":103},[100,165,166],{},"-- Bad — side effect inside draw\n",[100,168,169],{"class":102,"line":109},[100,170,171],{},"function love.draw()\n",[100,173,174],{"class":102,"line":115},[100,175,176],{},"  score = score + 1 -- don't do this\n",[100,178,179],{"class":102,"line":122},[100,180,181],{},"  love.graphics.print(score, 10, 10)\n",[100,183,184],{"class":102,"line":128},[100,185,186],{},"end\n",[100,188,190],{"class":102,"line":189},6,[100,191,119],{"emptyLinePlaceholder":118},[100,193,195],{"class":102,"line":194},7,[100,196,125],{},[100,198,200],{"class":102,"line":199},8,[100,201,202],{},"function love.update(dt)\n",[100,204,206],{"class":102,"line":205},9,[100,207,208],{},"  if coinCollected then score = score + 10 end\n",[100,210,212],{"class":102,"line":211},10,[100,213,186],{},[100,215,217],{"class":102,"line":216},11,[100,218,119],{"emptyLinePlaceholder":118},[100,220,222],{"class":102,"line":221},12,[100,223,171],{},[100,225,227],{"class":102,"line":226},13,[100,228,181],{},[100,230,232],{"class":102,"line":231},14,[100,233,186],{},[78,235,237,238,241],{"id":236},"use-lovegraphicspushpop-for-transforms","Use ",[83,239,240],{},"love.graphics.push\u002Fpop"," for transforms",[74,243,244,245,248],{},"When drawing entities with offsets, rotations, or scale, wrap in ",[83,246,247],{},"push\u002Fpop"," to avoid transform bleed:",[91,250,252],{"className":93,"code":251,"language":95,"meta":96,"style":96},"function Player:draw()\n  love.graphics.push()\n  love.graphics.translate(self.x + self.w\u002F2, self.y + self.h\u002F2)\n  love.graphics.rotate(self.angle)\n  love.graphics.draw(sprites.player, -self.w\u002F2, -self.h\u002F2)\n  love.graphics.pop()\nend\n",[83,253,254,259,264,269,274,279,284],{"__ignoreMap":96},[100,255,256],{"class":102,"line":103},[100,257,258],{},"function Player:draw()\n",[100,260,261],{"class":102,"line":109},[100,262,263],{},"  love.graphics.push()\n",[100,265,266],{"class":102,"line":115},[100,267,268],{},"  love.graphics.translate(self.x + self.w\u002F2, self.y + self.h\u002F2)\n",[100,270,271],{"class":102,"line":122},[100,272,273],{},"  love.graphics.rotate(self.angle)\n",[100,275,276],{"class":102,"line":128},[100,277,278],{},"  love.graphics.draw(sprites.player, -self.w\u002F2, -self.h\u002F2)\n",[100,280,281],{"class":102,"line":189},[100,282,283],{},"  love.graphics.pop()\n",[100,285,286],{"class":102,"line":194},[100,287,186],{},[78,289,291],{"id":290},"preload-all-assets","Preload all assets",[74,293,294,295,298,299,301,302,304],{},"Load images, audio, and fonts in ",[83,296,297],{},"love.load",". Loading inside ",[83,300,148],{}," or ",[83,303,152],{}," causes hitches:",[91,306,308],{"className":93,"code":307,"language":95,"meta":96,"style":96},"function love.load()\n  -- All assets here\n  sprites.tileset = love.graphics.newImage('assets\u002Fimages\u002Ftileset.png')\n  music.theme     = love.audio.newSource('assets\u002Faudio\u002Ftheme.ogg', 'stream')\nend\n",[83,309,310,315,320,325,330],{"__ignoreMap":96},[100,311,312],{"class":102,"line":103},[100,313,314],{},"function love.load()\n",[100,316,317],{"class":102,"line":109},[100,318,319],{},"  -- All assets here\n",[100,321,322],{"class":102,"line":115},[100,323,324],{},"  sprites.tileset = love.graphics.newImage('assets\u002Fimages\u002Ftileset.png')\n",[100,326,327],{"class":102,"line":122},[100,328,329],{},"  music.theme     = love.audio.newSource('assets\u002Faudio\u002Ftheme.ogg', 'stream')\n",[100,331,332],{"class":102,"line":128},[100,333,186],{},[74,335,237,336,339,340,343],{},[83,337,338],{},"'stream'"," for long audio (music) and ",[83,341,342],{},"'static'"," for short audio (sound effects).",[78,345,347],{"id":346},"use-quads-for-sprite-sheets","Use quads for sprite sheets",[74,349,350,351,156],{},"Don't use one image per frame. Pack sprites into a sheet and use ",[83,352,353],{},"love.graphics.newQuad",[91,355,357],{"className":93,"code":356,"language":95,"meta":96,"style":96},"local sheet   = love.graphics.newImage('assets\u002Fimages\u002Fplayer-sheet.png')\nlocal frameW, frameH = 16, 24\n\nlocal frames = {}\nfor i = 0, 3 do -- 4 walk frames\n  frames[i+1] = love.graphics.newQuad(i * frameW, 0, frameW, frameH, sheet)\nend\n\n-- Draw frame 2\nlove.graphics.draw(sheet, frames[2], player.x, player.y)\n",[83,358,359,364,369,373,378,383,388,392,396,401],{"__ignoreMap":96},[100,360,361],{"class":102,"line":103},[100,362,363],{},"local sheet   = love.graphics.newImage('assets\u002Fimages\u002Fplayer-sheet.png')\n",[100,365,366],{"class":102,"line":109},[100,367,368],{},"local frameW, frameH = 16, 24\n",[100,370,371],{"class":102,"line":115},[100,372,119],{"emptyLinePlaceholder":118},[100,374,375],{"class":102,"line":122},[100,376,377],{},"local frames = {}\n",[100,379,380],{"class":102,"line":128},[100,381,382],{},"for i = 0, 3 do -- 4 walk frames\n",[100,384,385],{"class":102,"line":189},[100,386,387],{},"  frames[i+1] = love.graphics.newQuad(i * frameW, 0, frameW, frameH, sheet)\n",[100,389,390],{"class":102,"line":194},[100,391,186],{},[100,393,394],{"class":102,"line":199},[100,395,119],{"emptyLinePlaceholder":118},[100,397,398],{"class":102,"line":205},[100,399,400],{},"-- Draw frame 2\n",[100,402,403],{"class":102,"line":211},[100,404,405],{},"love.graphics.draw(sheet, frames[2], player.x, player.y)\n",[74,407,408,409,412],{},"The ",[83,410,411],{},"anim8"," library automates this — the agent will suggest it for animated characters.",[78,414,416,417,420],{"id":415},"keep-conflua-explicit","Keep ",[83,418,419],{},"conf.lua"," explicit",[74,422,423,424,426],{},"Always have a ",[83,425,419],{}," that sets your window size, title, and disables unused modules:",[91,428,430],{"className":93,"code":429,"language":95,"meta":96,"style":96},"function love.conf(t)\n  t.window.title  = 'Dragon Dash'\n  t.window.width  = 1280\n  t.window.height = 720\n  t.window.resizable = false\n\n  -- Disable modules you don't use (faster startup)\n  t.modules.joystick = false\n  t.modules.video    = false\nend\n",[83,431,432,437,442,447,452,457,461,466,471,476],{"__ignoreMap":96},[100,433,434],{"class":102,"line":103},[100,435,436],{},"function love.conf(t)\n",[100,438,439],{"class":102,"line":109},[100,440,441],{},"  t.window.title  = 'Dragon Dash'\n",[100,443,444],{"class":102,"line":115},[100,445,446],{},"  t.window.width  = 1280\n",[100,448,449],{"class":102,"line":122},[100,450,451],{},"  t.window.height = 720\n",[100,453,454],{"class":102,"line":128},[100,455,456],{},"  t.window.resizable = false\n",[100,458,459],{"class":102,"line":189},[100,460,119],{"emptyLinePlaceholder":118},[100,462,463],{"class":102,"line":194},[100,464,465],{},"  -- Disable modules you don't use (faster startup)\n",[100,467,468],{"class":102,"line":199},[100,469,470],{},"  t.modules.joystick = false\n",[100,472,473],{"class":102,"line":205},[100,474,475],{},"  t.modules.video    = false\n",[100,477,478],{"class":102,"line":211},[100,479,186],{},[78,481,483,484],{"id":482},"error-handling-with-loveerrorhandler","Error handling with ",[83,485,486],{},"love.errorhandler",[74,488,489],{},"Override the default error screen for production builds:",[91,491,493],{"className":93,"code":492,"language":95,"meta":96,"style":96},"function love.errorhandler(err)\n  -- Log the error, show a friendly message\n  print('Error: ' .. tostring(err))\n  -- Optionally restart or show a custom screen\nend\n",[83,494,495,500,505,510,515],{"__ignoreMap":96},[100,496,497],{"class":102,"line":103},[100,498,499],{},"function love.errorhandler(err)\n",[100,501,502],{"class":102,"line":109},[100,503,504],{},"  -- Log the error, show a friendly message\n",[100,506,507],{"class":102,"line":115},[100,508,509],{},"  print('Error: ' .. tostring(err))\n",[100,511,512],{"class":102,"line":122},[100,513,514],{},"  -- Optionally restart or show a custom screen\n",[100,516,517],{"class":102,"line":128},[100,518,186],{},[78,520,522,523,526],{"id":521},"structure-your-require-paths","Structure your ",[83,524,525],{},"require"," paths",[74,528,529,530,533],{},"Keep your module paths consistent. All game modules live in the project root (or named subdirectories) and are required without the ",[83,531,532],{},".\u002F"," prefix:",[91,535,537],{"className":93,"code":536,"language":95,"meta":96,"style":96},"local Player  = require('player')\nlocal Tilemap = require('world.tilemap')\nlocal bump    = require('lib.bump')\n",[83,538,539,544,549],{"__ignoreMap":96},[100,540,541],{"class":102,"line":103},[100,542,543],{},"local Player  = require('player')\n",[100,545,546],{"class":102,"line":109},[100,547,548],{},"local Tilemap = require('world.tilemap')\n",[100,550,551],{"class":102,"line":115},[100,552,553],{},"local bump    = require('lib.bump')\n",[74,555,556],{},"The Parallax agent follows this convention by default.",[78,558,560],{"id":559},"camera-pattern","Camera pattern",[74,562,563],{},"For scrolling worlds, use a simple camera offset rather than a full camera library for small games:",[91,565,567],{"className":93,"code":566,"language":95,"meta":96,"style":96},"local cam = { x = 0, y = 0 }\n\nfunction love.draw()\n  love.graphics.push()\n  love.graphics.translate(-cam.x, -cam.y)\n  -- draw world, entities...\n  love.graphics.pop()\n  -- draw HUD (not affected by camera)\nend\n\nfunction love.update(dt)\n  -- Follow player\n  cam.x = player.x - love.graphics.getWidth() \u002F 2\n  cam.y = player.y - love.graphics.getHeight() \u002F 2\nend\n",[83,568,569,574,578,582,586,591,596,600,605,609,613,617,622,627,632],{"__ignoreMap":96},[100,570,571],{"class":102,"line":103},[100,572,573],{},"local cam = { x = 0, y = 0 }\n",[100,575,576],{"class":102,"line":109},[100,577,119],{"emptyLinePlaceholder":118},[100,579,580],{"class":102,"line":115},[100,581,171],{},[100,583,584],{"class":102,"line":122},[100,585,263],{},[100,587,588],{"class":102,"line":128},[100,589,590],{},"  love.graphics.translate(-cam.x, -cam.y)\n",[100,592,593],{"class":102,"line":189},[100,594,595],{},"  -- draw world, entities...\n",[100,597,598],{"class":102,"line":194},[100,599,283],{},[100,601,602],{"class":102,"line":199},[100,603,604],{},"  -- draw HUD (not affected by camera)\n",[100,606,607],{"class":102,"line":205},[100,608,186],{},[100,610,611],{"class":102,"line":211},[100,612,119],{"emptyLinePlaceholder":118},[100,614,615],{"class":102,"line":216},[100,616,202],{},[100,618,619],{"class":102,"line":221},[100,620,621],{},"  -- Follow player\n",[100,623,624],{"class":102,"line":226},[100,625,626],{},"  cam.x = player.x - love.graphics.getWidth() \u002F 2\n",[100,628,629],{"class":102,"line":231},[100,630,631],{},"  cam.y = player.y - love.graphics.getHeight() \u002F 2\n",[100,633,635],{"class":102,"line":634},15,[100,636,186],{},[74,638,639,640,643],{},"For complex camera behaviour (lerp, screenshake, zoom), the agent will suggest the ",[83,641,642],{},"hump.camera"," module.",[645,646,647],"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":96,"searchDepth":109,"depth":109,"links":649},[650,652,653,655,656,657,659,661,663],{"id":80,"depth":109,"text":651},"Always use dt for movement",{"id":142,"depth":109,"text":143},{"id":236,"depth":109,"text":654},"Use love.graphics.push\u002Fpop for transforms",{"id":290,"depth":109,"text":291},{"id":346,"depth":109,"text":347},{"id":415,"depth":109,"text":658},"Keep conf.lua explicit",{"id":482,"depth":109,"text":660},"Error handling with love.errorhandler",{"id":521,"depth":109,"text":662},"Structure your require paths",{"id":559,"depth":109,"text":560},"Proven patterns for Love2D games — what works, what to avoid, and why. Used by the Parallax agent.","md",null,{"ogImage":668},"\u002Flogo.png",{"title":53,"description":664},"UL1y2Ev9H-XZ8Z8TzLjq-UFTBRX37Ldx3kaK0OZ1q2I",[672,674],{"title":49,"path":50,"stem":51,"description":673,"children":-1},"How to think about building Love2D games — the conceptual framework the Parallax agent uses.",{"title":57,"path":58,"stem":59,"description":675,"children":-1},"Common reusable patterns for Love2D games — copy, adapt, use as agent prompts.",1778701173953]