{"id":29908231,"url":"https://github.com/zhuravkovigor/love-scenes","last_synced_at":"2026-05-18T00:03:18.824Z","repository":{"id":305271523,"uuid":"1019293751","full_name":"zhuravkovigor/love-scenes","owner":"zhuravkovigor","description":"File-system based scene routing for LÖVE 2D games inspired by Next.js","archived":false,"fork":false,"pushed_at":"2025-07-23T09:05:42.000Z","size":56,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-31T10:51:49.314Z","etag":null,"topics":["docs","love2d","lua","lua-module","routing","types"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zhuravkovigor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-14T05:38:26.000Z","updated_at":"2025-07-23T09:05:46.000Z","dependencies_parsed_at":"2025-07-19T09:45:18.774Z","dependency_job_id":"56d3c3a5-0e71-45b8-9296-451f6adade9a","html_url":"https://github.com/zhuravkovigor/love-scenes","commit_stats":null,"previous_names":["zhuravkovigor/love-scenes"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/zhuravkovigor/love-scenes","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhuravkovigor%2Flove-scenes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhuravkovigor%2Flove-scenes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhuravkovigor%2Flove-scenes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhuravkovigor%2Flove-scenes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zhuravkovigor","download_url":"https://codeload.github.com/zhuravkovigor/love-scenes/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhuravkovigor%2Flove-scenes/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268315793,"owners_count":24231056,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-01T02:00:08.611Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docs","love2d","lua","lua-module","routing","types"],"created_at":"2025-08-02T00:00:35.387Z","updated_at":"2026-05-18T00:03:08.807Z","avatar_url":"https://github.com/zhuravkovigor.png","language":"Lua","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Love Scenes 🎮\n\nFile-system based scene routing for LÖVE 2D games inspired by Next.js\n\n## Features\n\n- 📁 **File-system based routing** - Create scenes by simply adding files to your scenes directory\n- 🔀 **Dynamic routes** - Support for parameterized routes like `[id].lua`\n- 📱 **Layout system** - Wrap scenes with reusable layouts\n- 🎯 **Simple API** - Easy to integrate into existing LÖVE 2D projects\n- 🔧 **Configurable** - Customize scenes directory and behavior\n\n## Installation\n\n### LuaRocks (Recommended)\n\n```bash\nluarocks install love-scenes\n```\n\n### Git/GitHub\n\nInstall directly from GitHub:\n\n```bash\nluarocks install --server=https://luarocks.org/manifests/zhuravkovigor love-scenes\n```\n\nOr clone and install manually:\n\n```bash\ngit clone https://github.com/zhuravkovigor/love-scenes.git\ncd love-scenes\nluarocks make love-scenes-1.0-1.rockspec\n```\n\n### Manual Installation\n\n1. Download the latest release from [GitHub Releases](https://github.com/zhuravkovigor/love-scenes/releases)\n2. Extract the files to your project directory\n3. Require the library in your `main.lua`\n\n### Development Installation\n\nClone the repository and install locally:\n\n```bash\ngit clone https://github.com/zhuravkovigor/love-scenes.git\ncd love-scenes\nmake install\n```\n\n## Quick Start\n\n### 1. Basic Setup\n\n```lua\n-- main.lua\nlocal LoveScenes = require('love-scenes')\n\nfunction love.load()\n    -- Initialize with default settings\n    LoveScenes.init()\n\n    -- Navigate to the main scene\n    LoveScenes.navigate('/')\nend\n\nfunction love.update(dt)\n    LoveScenes.update(dt)\nend\n\nfunction love.draw()\n    LoveScenes.draw()\nend\n\n-- Forward input events\nfunction love.keypressed(key, scancode, isrepeat)\n    LoveScenes.keypressed(key, scancode, isrepeat)\nend\n```\n\n### 2. Create Your First Scene\n\n```lua\n-- scenes/index.lua (Main menu at route \"/\")\nlocal scene = {}\n\nfunction scene:load(params)\n    self.title = \"My Awesome Game\"\nend\n\nfunction scene:update(dt)\n    -- Scene update logic\nend\n\nfunction scene:draw()\n    love.graphics.printf(self.title, 0, 100, love.graphics.getWidth(), \"center\")\nend\n\nfunction scene:keypressed(key)\n    if key == \"space\" then\n        require('love-scenes').navigate('/game')\n    end\nend\n\nreturn scene\n```\n\n### 3. Add More Scenes\n\n```lua\n-- scenes/game/index.lua (Game scene at route \"/game\")\nlocal scene = {}\n\nfunction scene:load(params)\n    self.player = {x = 100, y = 100}\nend\n\nfunction scene:update(dt)\n    if love.keyboard.isDown(\"left\") then\n        self.player.x = self.player.x - 100 * dt\n    end\n    if love.keyboard.isDown(\"right\") then\n        self.player.x = self.player.x + 100 * dt\n    end\nend\n\nfunction scene:draw()\n    love.graphics.circle(\"fill\", self.player.x, self.player.y, 20)\nend\n\nreturn scene\n```\n\n## File Structure\n\nLove Scenes uses a file-system based routing approach similar to Next.js:\n\n```\nscenes/\n├── index.lua              # Route: /\n├── layout.lua             # Layout for all scenes\n├── game/\n│   ├── index.lua          # Route: /game\n│   └── layout.lua         # Layout for /game/* routes\n├── level/\n│   └── [level].lua        # Route: /level/1-1, /level/forest, etc.\n├── profile/\n│   └── [id].lua           # Route: /profile/123, /profile/abc, etc.\n├── shop/\n│   └── [category].lua     # Route: /shop/weapons, /shop/armor, etc.\n└── settings/\n    ├── index.lua          # Route: /settings\n    └── audio/\n        └── index.lua      # Route: /settings/audio\n```\n\n## Routing Examples\n\n### Static Routes\n\n- `scenes/index.lua` → `/` (root)\n- `scenes/about/index.lua` → `/about`\n- `scenes/game/level/index.lua` → `/game/level`\n\n### Dynamic Routes\n\n- `scenes/user/[id].lua` → `/user/123`, `/user/abc`\n- `scenes/level/[level].lua` → `/level/1-1`, `/level/forest`\n- `scenes/shop/[category].lua` → `/shop/weapons`, `/shop/armor`\n- `scenes/post/[slug]/[id].lua` → `/post/hello-world/123`\n\n```lua\n-- scenes/level/[level].lua\nlocal scene = {}\n\nfunction scene:load(params)\n    self.levelId = params.level  -- Access the dynamic parameter\n    print(\"Loading level:\", self.levelId)\n\n    -- Different logic based on level\n    if self.levelId == \"boss-1\" then\n        self:loadBossLevel()\n    else\n        self:loadNormalLevel()\n    end\nend\n\nreturn scene\n```\n\n## Layouts\n\nLayouts wrap scenes and provide common UI elements:\n\n```lua\n-- scenes/layout.lua (Root layout for all scenes)\nlocal layout = {}\n\nfunction layout:draw(drawScene)\n    -- Draw header\n    love.graphics.setColor(0.2, 0.2, 0.2)\n    love.graphics.rectangle(\"fill\", 0, 0, love.graphics.getWidth(), 60)\n\n    -- Draw scene content\n    love.graphics.push()\n    love.graphics.translate(0, 60)\n    drawScene()  -- This renders the current scene\n    love.graphics.pop()\n\n    -- Draw footer\n    love.graphics.setColor(0.2, 0.2, 0.2)\n    love.graphics.rectangle(\"fill\", 0, love.graphics.getHeight() - 40,\n                          love.graphics.getWidth(), 40)\nend\n\nreturn layout\n```\n\n## Navigation\n\nNavigate between scenes using the `navigate` function:\n\n```lua\nlocal LoveScenes = require('love-scenes')\n\n-- Navigate to different routes\nLoveScenes.navigate('/')\nLoveScenes.navigate('/game')\nLoveScenes.navigate('/profile/123')\nLoveScenes.navigate('/settings/audio')\n\n-- Navigate with additional parameters\nLoveScenes.navigate('/user/123', {tab = \"settings\"})\n```\n\n## Configuration\n\nAll configuration parameters are optional. Love Scenes works out of the box with sensible defaults:\n\n```lua\n-- Minimal setup - all parameters are optional\nLoveScenes.init()\n\n-- With custom configuration\nLoveScenes.init({\n    scenesPath = \"scenes\",      -- Directory containing scenes (default: \"scenes\")\n    autoLoad = true,            -- Automatically load scenes on init (default: true)\n    enableLayouts = true,       -- Enable layout system (default: true)\n    debugMode = false           -- Enable debug logging (default: false)\n})\n```\n\n### Configuration Options\n\n| Parameter       | Type    | Default    | Description                                          |\n| --------------- | ------- | ---------- | ---------------------------------------------------- |\n| `scenesPath`    | string  | `\"scenes\"` | Directory containing your scene files                |\n| `autoLoad`      | boolean | `true`     | Automatically scan and load scenes on initialization |\n| `enableLayouts` | boolean | `true`     | Enable the layout system for wrapping scenes         |\n| `debugMode`     | boolean | `false`    | Enable debug logging to console                      |\n\n## Scene Lifecycle\n\nScenes have several lifecycle methods:\n\n```lua\nlocal scene = {}\n\nfunction scene:load(params)\n    -- Called when scene is created and loaded\n    -- Access route parameters via params\n    -- Initialize scene data, load assets, set up state\nend\n\nfunction scene:onEnter(next)\n    -- Called when navigating to this scene\n    -- next is an optional callback for controlling transition timing\n    if next then\n        -- Perform any animations or async setup\n        -- Call next() when ready for the scene to become active\n        next()\n    end\nend\n\nfunction scene:onLeave(next)\n    -- Called when leaving this scene\n    -- next is an optional callback for controlling transition timing\n    if next then\n        -- Perform cleanup animations\n        -- Call next() when ready to complete the transition\n        next()\n    end\nend\n\nfunction scene:update(dt)\n    -- Called every frame\nend\n\nfunction scene:draw()\n    -- Called every frame for rendering\nend\n\n-- LÖVE 2D callbacks are automatically forwarded\nfunction scene:keypressed(key, scancode, isrepeat)\n    -- Handle input\nend\n\nfunction scene:mousepressed(x, y, button, isTouch, presses)\n    -- Handle mouse input\nend\n\nreturn scene\n```\n\n### Example with Transition Animation\n\n```lua\nlocal scene = {}\nlocal fade_alpha = 1\n\nfunction scene:onEnter(next)\n    if next then\n        -- Start fade-in animation\n        fade_alpha = 0\n        -- Don't block transition, let it proceed immediately\n        next()\n    else\n        fade_alpha = 1\n    end\nend\n\nfunction scene:update(dt)\n    -- Simple fade-in animation\n    if fade_alpha \u003c 1 then\n        fade_alpha = math.min(1, fade_alpha + dt * 2)\n    end\nend\n\nfunction scene:draw()\n    love.graphics.push()\n    love.graphics.setColor(1, 1, 1, fade_alpha)\n\n    -- Draw scene content with fade effect\n    love.graphics.printf(\"Scene Content\", 0, 100, love.graphics.getWidth(), \"center\")\n\n    love.graphics.pop()\nend\n\nreturn scene\n```\n\n## Layout Lifecycle\n\nLayouts also have lifecycle methods:\n\n```lua\nlocal layout = {}\n\nfunction layout:load()\n    -- Called when layout is created\nend\n\nfunction layout:onEnter(scene, next)\n    -- Called when a scene using this layout is entered\n    -- scene: the scene that will be rendered\n    -- next: optional callback for controlling transition timing\n    if next then\n        next()\n    end\nend\n\nfunction layout:onLeave(next)\n    -- Called when leaving this layout\n    -- next: optional callback for controlling transition timing\n    if next then\n        next()\n    end\nend\n    -- Called when leaving this layout\nend\n\nfunction layout:update(dt)\n    -- Called every frame before scene update\nend\n\nfunction layout:draw(drawScene)\n    -- Called every frame for rendering\n    -- drawScene() renders the current scene\nend\n\nreturn layout\n```\n\n## API Reference\n\n### LoveScenes.init(config)\n\nInitialize the library with optional configuration. **All parameters are optional** - the library works with sensible defaults.\n\n**Parameters:**\n\n- `config` (table, optional): Configuration options. If omitted, uses default values.\n  - `scenesPath` (string, optional): Directory containing scenes (default: \"scenes\")\n  - `autoLoad` (boolean, optional): Automatically load scenes on init (default: true)\n  - `enableLayouts` (boolean, optional): Enable layout system (default: true)\n  - `debugMode` (boolean, optional): Enable debug logging (default: false)\n\n**Examples:**\n\n```lua\n-- Minimal setup\nLoveScenes.init()\n\n-- With custom scenes directory\nLoveScenes.init({ scenesPath = \"game-scenes\" })\n\n-- With debug mode\nLoveScenes.init({ debugMode = true })\n```\n\n### LoveScenes.navigate(path, params)\n\nNavigate to a scene.\n\n**Parameters:**\n\n- `path` (string): Route path (e.g., \"/\", \"/game\", \"/user/123\")\n- `params` (table, optional): Additional parameters to pass to the scene\n\n### LoveScenes.getCurrentScene()\n\nGet the current active scene instance.\n\n### LoveScenes.getCurrentLayout()\n\nGet the current active layout instance.\n\n## Complete Examples\n\n### Basic Game Setup\n\nHere's a complete example of a simple game with multiple scenes:\n\n```lua\n-- main.lua\nlocal LoveScenes = require('love-scenes')\n\nfunction love.load()\n    LoveScenes.init({\n        debugMode = true  -- Enable debug logging\n    })\n\n    -- Start at main menu\n    LoveScenes.navigate('/')\nend\n\nfunction love.update(dt)\n    LoveScenes.update(dt)\nend\n\nfunction love.draw()\n    LoveScenes.draw()\nend\n\n-- Forward all LÖVE callbacks\nfunction love.keypressed(key, scancode, isrepeat)\n    LoveScenes.keypressed(key, scancode, isrepeat)\nend\n\nfunction love.mousepressed(x, y, button, isTouch, presses)\n    LoveScenes.mousepressed(x, y, button, isTouch, presses)\nend\n```\n\n```lua\n-- scenes/index.lua (Main Menu)\nlocal LoveScenes = require('love-scenes')\nlocal scene = {}\n\nfunction scene:load()\n    self.title = \"My Awesome Game\"\n    self.menuItems = {\"Start Game\", \"Settings\", \"Quit\"}\n    self.selectedIndex = 1\nend\n\nfunction scene:update(dt)\n    -- Menu logic here\nend\n\nfunction scene:draw()\n    -- Draw title\n    love.graphics.setFont(love.graphics.newFont(32))\n    love.graphics.printf(self.title, 0, 100, love.graphics.getWidth(), \"center\")\n\n    -- Draw menu items\n    love.graphics.setFont(love.graphics.newFont(16))\n    for i, item in ipairs(self.menuItems) do\n        local y = 200 + (i - 1) * 40\n        local color = i == self.selectedIndex and {1, 1, 0} or {1, 1, 1}\n        love.graphics.setColor(color)\n        love.graphics.printf(item, 0, y, love.graphics.getWidth(), \"center\")\n    end\n    love.graphics.setColor(1, 1, 1)  -- Reset color\nend\n\nfunction scene:keypressed(key)\n    if key == \"up\" then\n        self.selectedIndex = math.max(1, self.selectedIndex - 1)\n    elseif key == \"down\" then\n        self.selectedIndex = math.min(#self.menuItems, self.selectedIndex + 1)\n    elseif key == \"return\" then\n        if self.selectedIndex == 1 then\n            LoveScenes.navigate('/game')\n        elseif self.selectedIndex == 2 then\n            LoveScenes.navigate('/settings')\n        elseif self.selectedIndex == 3 then\n            love.event.quit()\n        end\n    end\nend\n\nreturn scene\n```\n\n```lua\n-- scenes/game/index.lua (Game Scene)\nlocal LoveScenes = require('love-scenes')\nlocal scene = {}\n\nfunction scene:load()\n    self.player = {\n        x = 400,\n        y = 300,\n        speed = 200\n    }\n    self.enemies = {}\n    self.score = 0\nend\n\nfunction scene:update(dt)\n    -- Player movement\n    if love.keyboard.isDown(\"left\", \"a\") then\n        self.player.x = self.player.x - self.player.speed * dt\n    end\n    if love.keyboard.isDown(\"right\", \"d\") then\n        self.player.x = self.player.x + self.player.speed * dt\n    end\n    if love.keyboard.isDown(\"up\", \"w\") then\n        self.player.y = self.player.y - self.player.speed * dt\n    end\n    if love.keyboard.isDown(\"down\", \"s\") then\n        self.player.y = self.player.y + self.player.speed * dt\n    end\n\n    -- Keep player on screen\n    self.player.x = math.max(20, math.min(love.graphics.getWidth() - 20, self.player.x))\n    self.player.y = math.max(20, math.min(love.graphics.getHeight() - 20, self.player.y))\nend\n\nfunction scene:draw()\n    -- Draw player\n    love.graphics.setColor(0, 1, 0)  -- Green\n    love.graphics.circle(\"fill\", self.player.x, self.player.y, 20)\n\n    -- Draw UI\n    love.graphics.setColor(1, 1, 1)  -- White\n    love.graphics.print(\"Score: \" .. self.score, 10, 10)\n    love.graphics.print(\"Press ESC to return to menu\", 10, 30)\nend\n\nfunction scene:keypressed(key)\n    if key == \"escape\" then\n        LoveScenes.navigate('/')\n    end\nend\n\nreturn scene\n```\n\n### Dynamic Routes Example\n\n```lua\n-- scenes/profile/[id].lua (Dynamic User Profile)\nlocal scene = {}\n\nfunction scene:load(params)\n    self.userId = params.id\n    self.userInfo = self:loadUserInfo(self.userId)\nend\n\nfunction scene:loadUserInfo(id)\n    -- Simulate loading user data\n    return {\n        name = \"User \" .. id,\n        level = math.random(1, 100),\n        score = math.random(1000, 99999)\n    }\nend\n\nfunction scene:draw()\n    love.graphics.printf(\"User Profile\", 0, 50, love.graphics.getWidth(), \"center\")\n    love.graphics.printf(\"ID: \" .. self.userId, 0, 100, love.graphics.getWidth(), \"center\")\n    love.graphics.printf(\"Name: \" .. self.userInfo.name, 0, 130, love.graphics.getWidth(), \"center\")\n    love.graphics.printf(\"Level: \" .. self.userInfo.level, 0, 160, love.graphics.getWidth(), \"center\")\n    love.graphics.printf(\"Score: \" .. self.userInfo.score, 0, 190, love.graphics.getWidth(), \"center\")\n\n    love.graphics.printf(\"Press ESC to go back\", 0, 250, love.graphics.getWidth(), \"center\")\nend\n\nfunction scene:keypressed(key)\n    if key == \"escape\" then\n        require('love-scenes').navigate('/')\n    end\nend\n\nreturn scene\n```\n\nNow you can navigate to different user profiles:\n\n- `LoveScenes.navigate('/profile/123')`\n- `LoveScenes.navigate('/profile/player1')`\n- `LoveScenes.navigate('/profile/admin')`\n\n## Tips and Best Practices\n\n### 1. Scene Organization\n\n- Keep related scenes in subdirectories (e.g., `scenes/game/`, `scenes/menu/`)\n- Use descriptive names for dynamic routes: `[playerId].lua`, `[levelName].lua`\n\n### 2. State Management\n\n- Initialize all scene data in the `load()` method\n- Use `onEnter()` and `onLeave()` for cleanup and transitions\n- Store global state outside of individual scenes if needed\n\n### 3. Performance\n\n- Preload assets in `load()` method\n- Use `onLeave()` to clean up resources\n- Consider using layouts for shared UI elements\n\n### 4. Navigation\n\n- Use absolute paths for navigation: `/game`, `/settings/audio`\n- Pass additional data via the params parameter\n- Handle navigation errors gracefully\n\n## Examples\n\nCheck out the `scenes/` directory in this repository for complete examples including:\n\n- Main menu with navigation\n- Game scene with player movement\n- Settings scene with configurable options\n- Dynamic profile scenes with URL parameters\n- Layout system with header and footer\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nMIT License - see LICENSE file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzhuravkovigor%2Flove-scenes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzhuravkovigor%2Flove-scenes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzhuravkovigor%2Flove-scenes/lists"}