Files
Adolfo Reyna e6e4eca188 Add Lua 5.4 scripting integration for dynamic game loading
- Integrated Lua 5.4 engine (32-bit mode for embedded ARM)
- Created LuaGame wrapper class implementing Game interface
- Added C++ bindings exposing renderer, game state, and input to Lua
- Implemented SD card loader for automatic .lua game discovery
- Updated GameLauncher to support std::function for lambda captures
- Made Game class members public for Lua bindings access
- Added example Lua games: counter, snake, bouncing ball
- Included comprehensive API documentation

Games can now be written as .lua text files on SD card and loaded
without recompilation. Build size: 747KB UF2, Lua VM uses ~50-80KB RAM.
2026-02-07 11:56:03 -05:00

234 lines
5.8 KiB
Markdown

# Lua Game API Documentation
This document describes the API available to Lua game scripts on the Pico game console.
## Script Structure
Every Lua game must define three functions:
```lua
function init()
-- Initialize game state
end
function update(event)
-- Process input and update game logic
-- Return true if redraw needed, false otherwise
return needs_redraw
end
function draw()
-- Render the game to screen
end
```
## Metadata Comments
Add metadata at the top of your script:
```lua
-- NAME: My Awesome Game
-- DESC: A fun game about squares
```
## Global Tables
### `game` - Game State and Info
- `game.width()` - Get display width in pixels
- `game.height()` - Get display height in pixels
- `game.exit()` - Request exit to game launcher
- `game.vars` - Table for persistent state variables
Example:
```lua
function init()
game.vars.score = 0
game.vars.player_x = 100
game.vars.state = 0
end
```
### `renderer` - Drawing Functions
All coordinates are in pixels. Color is boolean: `true` = black/on, `false` = white/off.
#### Basic Shapes
- `renderer.clear(white)` - Clear screen to white (true) or black (false)
- `renderer.pixel(x, y, on)` - Set single pixel
- `renderer.line(x0, y0, x1, y1, on, width)` - Draw line (width optional, default 1)
- `renderer.rect(x, y, w, h, on, filled)` - Draw rectangle (filled optional, default false)
- `renderer.circle(x, y, radius, on, filled)` - Draw circle (filled optional, default false)
- `renderer.triangle(x0, y0, x1, y1, x2, y2, on, filled)` - Draw triangle
#### Text
- `renderer.text(x, y, text, on)` - Draw text string
Example:
```lua
function draw()
renderer.clear(false) -- Clear to black
renderer.rect(50, 50, 100, 80, true, true) -- Filled white rectangle
renderer.circle(100, 100, 30, true, false) -- White circle outline
renderer.text(10, 10, "Score: " .. tostring(game.vars.score), true)
end
```
### `INPUT` - Input Event Types
Constants for checking `event.type`:
- `INPUT.NONE` - No input
- `INPUT.TOUCH_DOWN` - Touch/click started
- `INPUT.TOUCH_MOVE` - Touch/drag in progress
- `INPUT.TOUCH_UP` - Touch/click released
- `INPUT.BUTTON_0` - Physical button 0 pressed
- `INPUT.BUTTON_1` - Physical button 1 pressed
- `INPUT.GESTURE` - Gesture detected
## Input Event Structure
The `event` parameter passed to `update()` has these fields:
- `event.type` - Event type (see INPUT constants)
- `event.x` - X coordinate (for touch events)
- `event.y` - Y coordinate (for touch events)
- `event.button_id` - Button identifier (for button events)
- `event.valid` - Boolean, true if event is valid
Example:
```lua
function update(event)
if event.type == INPUT.TOUCH_DOWN then
print("Touch at: " .. event.x .. ", " .. event.y)
game.vars.last_touch_x = event.x
game.vars.last_touch_y = event.y
return true -- Request redraw
end
return false
end
```
## State Machine Pattern
Use `game.vars` to implement state machines:
```lua
local STATE_MENU = 0
local STATE_PLAYING = 1
local STATE_GAME_OVER = 2
function init()
game.vars.state = STATE_MENU
end
function update(event)
if game.vars.state == STATE_MENU then
-- Handle menu input
if event.type == INPUT.TOUCH_DOWN then
game.vars.state = STATE_PLAYING
return true
end
elseif game.vars.state == STATE_PLAYING then
-- Handle gameplay input
elseif game.vars.state == STATE_GAME_OVER then
-- Handle game over screen
end
return false
end
function draw()
if game.vars.state == STATE_MENU then
-- Draw menu
elseif game.vars.state == STATE_PLAYING then
-- Draw game
elseif game.vars.state == STATE_GAME_OVER then
-- Draw game over screen
end
end
```
## Standard Lua Libraries
Available libraries:
- `math` - Math functions (`math.sin`, `math.random`, etc.)
- `string` - String manipulation
- `table` - Table operations (`table.insert`, `table.remove`, etc.)
- `coroutine` - Coroutines for advanced flow control
**Not available** (embedded environment):
- `io` - File I/O (use SD card APIs in C++ if needed)
- `os` - Operating system functions
- `debug` - Debugging functions
## Tips and Best Practices
1. **Keep it Simple** - Lua is slower than C++. Keep game logic simple for smooth performance.
2. **Use Local Variables** - Local variables are faster than globals:
```lua
local function move_player() -- Local function
local speed = 5 -- Local variable
game.vars.player_x = game.vars.player_x + speed
end
```
3. **Minimize Allocations** - Reuse tables instead of creating new ones:
```lua
-- Good: Reuse existing table
game.vars.snake[1].x = new_x
-- Bad: Creates garbage
game.vars.snake[1] = {x = new_x, y = new_y}
```
4. **Efficient Drawing** - Only redraw when needed. Return `false` from `update()` if nothing changed.
5. **Frame Limiting** - For animation, use frame counters:
```lua
function update(event)
game.vars.frame = (game.vars.frame or 0) + 1
if game.vars.frame % 5 == 0 then -- Every 5 frames
-- Update animation
return true
end
return false
end
```
## Example: Complete Minimal Game
```lua
-- NAME: Click Counter
-- DESC: Count your clicks
function init()
game.vars.count = 0
end
function update(event)
if event.type == INPUT.TOUCH_DOWN then
game.vars.count = game.vars.count + 1
return true
end
return false
end
function draw()
renderer.clear(false)
local text = "Clicks: " .. tostring(game.vars.count)
renderer.text(20, 20, text, true)
end
```
## Deployment
1. Save your `.lua` file to SD card in `/games/` directory
2. Eject SD card and insert into Pico console
3. Power on - your game will appear in the launcher menu
4. Select and play!
No recompilation needed - edit scripts directly on SD card for rapid iteration.