Tags: brettchalupa/usagi
Tags
v1.1.1 Features: - `usagi font bake` gained five new script blocks, all included in `--scripts all`: `box-drawing` (alias: `box`) for Box Drawing (U+2500–U+257F) and Block Elements (U+2580–U+259F); `arrows` for U+2190–U+21FF; `math` (alias: `math-ops`) for U+2200–U+22FF; `geometric` (alias: `shapes`) for U+25A0–U+25FF; and `symbols` (alias: `misc-symbols`) for U+2600–U+26FF. Box drawing was the motivating gap. Fonts that lack these glyphs are unaffected; the cmap filter drops the codepoints they don't cover. To keep an atlas small, subtract the blocks you don't need, e.g. `--scripts all,-symbols,-math`. See https://codeberg.org/brettchalupa/usagi/issues/16 - Added support for the `?verbose=1` query param to web builds to get diagnostic logging to the console for debugging web build performance. Fixes: - Added `gfx.COLOR_TRUE_WHITE` to meta Lua stubs/comments as the wrong white was being used. - Improved web performance by only copying screen pixels from GPU to CPU for games that call `gfx.get_px`, that way all games don't have to pay that cost. This operation is seemingly slow on web. Showing about a 10% improvement in performance from testing. See https://codeberg.org/brettchalupa/usagi/issues/19
v1.1.0 Features: - New `usagi loveify <src> <dst>` subcommand: one-time port of an Usagi project to a Love2D 11.5 project. Walks the source tree, rewrites compound-assignment operators (`x += 1` → `x = x + (1)`) for LuaJIT compat, copies all assets verbatim, and drops in the Love shim runtime (`usagi_shim.lua` + `conf.lua`) plus the bundled monogram font when the source has no custom `font.png`. Refuses to overwrite an existing destination. Intended as a graduation path when a project needs iOS / Android, more action buttons, more robust APIs, or other Love-only features. See the new "Graduating to Love2D" section in README.md for the workflow and what's intentionally not carried over (pause menu, input remapping, shader hooks, hot reload, FPS overlay). - Linux aarch64 is now a published export target. Released as `usagi-<ver>-linux-aarch64.tar.gz` and produced by `usagi export --target linux-aarch64` (also included in `--target all`). Covers Raspberry Pi, ARM Linux handhelds, ARM SBCs, and ARM Linux VMs. [#270](https://github.com/brettchalupa/usagi/issues/270) - `usagi export --target host` fuses against the running binary regardless of OS or architecture. This lets devs on platforms outside the published-template set (e.g., macOS Intel, FreeBSD) build the engine from source and export for themselves. Output filename is `<slug>-<os>-<arch>.zip`. On unsupported hosts, the default `--target all` also produces this zip alongside the published builds, so the dev doesn't have to know about the flag. See [Building for unsupported platforms](README.md#building-for-unsupported-platforms) in the README. [#273](https://github.com/brettchalupa/usagi/issues/273) - Lua's standard `debug` library is now available in user code. Use `debug.getinfo` / `debug.traceback` for logger line numbers, and embed step-debuggers like debugger.lua. Note that this exposes the full library, including `debug.setlocal` / `debug.setupvalue` / `debug.sethook`. Those can corrupt the VM if misused, so treat them with care. [#267](https://github.com/brettchalupa/usagi/issues/267) - `usagi font bake` gained a `--scripts` flag for picking which Unicode blocks to include. Pass a comma-separated list of names, with `all` (default) and `none` as special values and a `-` prefix for subtraction. Known scripts: `latin`, `latin-ext`, `greek`, `cyrillic`, `punct`, `cjk-punct`, `hiragana`, `katakana`, `hangul` (alias: `korean`), `cjk` (alias: `han`), `halfwidth`. Examples: `--scripts all,-cjk` to drop Han ideographs, or `--scripts latin,korean` for a Korean-only atlas. The `--no-cjk` flag is now deprecated and prints a warning; it stays as an alias for `--scripts all,-cjk` and will be removed in a future major release. [#287](https://github.com/brettchalupa/usagi/issues/287) Fixes: - `meta/usagi.lua` (the LSP type stubs) now declares `gfx.COLOR_TRUE_WHITE`. The constant has always existed at runtime (slot `0`, pure white), but the stub omitted it, so editors flagged `gfx.COLOR_TRUE_WHITE` as undefined. The stub's palette docstring also wrongly claimed slot `0` renders as a magenta sentinel; it resolves to true white, and only indices above the active palette's length render as magenta. - Added missing `util.remap` function to bundled type definitions in `meta/usagi.lua`. - `usagi` CLI output now renders colorized in Windows `cmd.exe`. The colorized `[usagi]` log prefix relies on ANSI escape sequences; modern Windows 10+ conhost understands them but only after a process opts in via `SetConsoleMode`. Windows Terminal and PowerShell inherit the opt-in from the parent process, but bare `cmd.exe` does not, so previously the sequences printed as raw bytes (`[32m[usagi][0m ...`). Usagi now opts in once at startup; on truly pre-Win10 hosts where the opt-in fails, color is suppressed and the plain `[usagi]` prefix path takes over. [#286](https://github.com/brettchalupa/usagi/issues/286) - `usagi font bake` now includes Hangul (Korean) and Latin Extended Additional (Vietnamese precomposed forms) by default. Previously these blocks were excluded, so even a font with full Korean or Vietnamese coverage produced an atlas where every Hangul or Vietnamese codepoint rendered as `?`. The `examples/custom_font` demo now includes a Korean line to verify coverage end to end. [#287](https://github.com/brettchalupa/usagi/issues/287) - Calling `usagi.read_json` or `usagi.read_text` at the top of `main.lua` no longer breaks `usagi tools` and `usagi export`; see [#264](https://github.com/brettchalupa/usagi/issues/264) - `gfx.text_ex` called with 6 args (missing the required `alpha` introduced in v1.0.0) no longer aborts the process on Windows; the error surfaces on the in-game overlay like other malformed calls. As part of the fix, short-arg calls to any wrapped engine API now report the missing argument by index ("bad argument #N to '...' (T expected, got nil)") rather than a confusing "bad argument #1 to 'type'" message. [#259](https://github.com/brettchalupa/usagi/issues/259) - Type annotation for `_config()` table to help with autocomplete; see [#255](https://github.com/brettchalupa/usagi/issues/255) - `sfx.play(name)` called repeatedly no longer cuts off the playing copy. Each sfx now has a pool of 8 voices that overlap; the 9th simultaneous play steals the oldest. [#258](https://github.com/brettchalupa/usagi/issues/258) - `usagi tools` UI now scales when the window is resized. Renders to a 1280x720 canvas and blits scaled (with letterbox bars) to fit any window size. Launches at the largest integer canvas multiple that fits the current monitor. [#269](https://github.com/brettchalupa/usagi/issues/269) - Right clicking in web exports now works properly and does not show the browser's right click menu. See https://codeberg.org/brettchalupa/usagi/issues/13
v1.0.0 Breaking: - Pixel API renamed for consistency with the other shorthand primitives (`rect`, `circ`, `tri`, `spr`, ...). The writer is now `gfx.px(x, y, color)`, the screen-pixel reader is `gfx.get_px(x, y)`, and the sprite-pixel reader is `gfx.get_spr_px(index, x, y)`. The old names, `gfx.pixel`, `gfx.px(x, y)` (two-arg reader), and `gfx.spr_px`, are gone. Find and replace in your code: `gfx.pixel` → `gfx.px`, `gfx.px(x, y)` → `gfx.get_px(x, y)`, `gfx.spr_px` → `gfx.get_spr_px`. [See #215](https://github.com/brettchalupa/usagi/issues/215) - Palette slot `0` now resolves to true white (`COLOR_TRUE_WHITE`) instead of the magenta out-of-range sentinel. Negative indices and indices past the active palette's length still render as magenta, so the "obvious unknown color" indicator survives for the common typo cases. - `gfx.text_ex` gained a required trailing `alpha` (`0..1`) param for parity with `gfx.spr_ex` / `gfx.sspr_ex`. Pass `1.0` to keep the old behavior. The simple `gfx.text` signature is unchanged. Features: - New `usagi.read_json(path)` and `usagi.read_text(path)` for loading arbitrary game data (level layouts, dialog scripts, tuning configs) from a project-level `data/` directory. Paths are forward-slash relative to `data/`, nested subdirs supported (`usagi.read_json("levels/01.json")`). The whole `data/` tree is bundled by `usagi export`, so the same call resolves identically in dev and shipped builds. Hot reload: any save to a data file pokes the same script-reload path as a `.lua` save, so top-level `local levels = usagi.read_json("levels.json")` picks up new bytes without F5. New `examples/level_from_json` and `examples/level_from_csv` (the latter shows the `read_text` + `string.gmatch` pattern for simple CSV grids, since the engine doesn't ship a CSV parser). [See #218](https://github.com/brettchalupa/usagi/issues/218) - New `usagi.to_json(t)` returns a Lua table as a pretty-printed JSON string. Shares the shape validator with `usagi.save`, so keys must be all strings or a dense `1..n` integer array (functions, userdata, NaN, and cycles still error). Useful for devtools, structured stdout logs, and any other place you want JSON without writing to the save file. See `examples/to_json.lua`. - New `gfx.tri(x1, y1, x2, y2, x3, y3, color)` and `gfx.tri_fill(x1, y1, x2, y2, x3, y3, color)` for drawing triangle outlines and filled triangles from three points. Filled-triangle vertex order is auto-corrected so arrows, spaceship nosecones, and pointer shapes draw regardless of how you specified the points. [See #211](https://github.com/brettchalupa/usagi/issues/211) - New `gfx.COLOR_TRUE_WHITE` constant: an off-palette pure `(255, 255, 255)` white. Use it as the identity tint for `gfx.spr_ex` and `gfx.sspr_ex` when you want sprite pixels to pass through unchanged. The Pico-8 `gfx.COLOR_WHITE` is slightly warm (`255, 241, 232`) and tints sprites a touch peachy if used as the identity, which is fine if you want a warm look but undesirable if you're after pure pass-through. Available in every API that takes a palette index (`gfx.text`, `gfx.clear`, `gfx.rect`, etc.) and stays pure white even with a custom `palette.png` loaded. - New examples: - scene_switching: how to define multiple scenes like gameplay, main menu, etc. and switch between them - The credit for the open source code that Usagi depends on is now included in the engine's archive, detailing the various licenses. These are also viewable at https://usagiengine.com/third-parties. - `USAGI_VERBOSE=1` now emits a one-shot startup snapshot at boot (build profile, platform, GC params, resolution, sprite size, pause-menu / palette / font source, script path, Lua heap after `_init`) plus a per-second frame summary line (avg / p50 / p99 / max frame time in ms, count of frames over the 16.7 ms budget, current Lua heap KB). The frame summary catches silent perf regressions of the "still runs but slower" shape; the snapshot pins the env for bug reports. Zero overhead when the env var is not set. - New `examples/diagnostics` to exercise both: short-lived table allocs in `_update` with controls to scale the rate, plus a one-shot burst button to provoke a GC cycle. Run as `USAGI_VERBOSE=1 just example diagnostics` and watch the terminal. - Raylib's own boot chatter and per-frame TEXTURE log are now gated on a separate `USAGI_RAYLIB_VERBOSE=1`, so the diagnostics stream stays readable. Previously both shared `USAGI_VERBOSE=1` and raylib's per-frame log buried the frame summary. Set both env vars when you need everything. Fixes: - GIF recordings now use 255 colors instead of the default Pico-8 color palette, fixing an issue where exported GIF colors were wrong. [See #222](https://github.com/brettchalupa/usagi/issues/222) - macOS no longer prints the GLFW "regular windows do not have icons on macOS" warning at startup. Cocoa never honored the per-window icon anyway, so the call was a known no-op; the `.app` bundle's `AppIcon.icns` is the path that matters there, and it's still generated at `usagi export --target macos` time. - Passing a non-UTF-8 byte sequence (e.g. `string.char(200)`) to engine APIs that take a string (`gfx.text`, `gfx.text_ex`, `usagi.measure_text`, `usagi.menu_item`, `sfx.play`, `music.play`, `gfx.shader_set`, etc.) no longer crashes on Windows. Bytes outside ASCII render as the U+FFFD replacement character instead of erroring at the FFI boundary. - Error overlay text renders cleanly on Windows setups where the bundled font was previously upscaled at a fractional ratio and came out blurred / curly. The overlay now draws at an integer multiple of the bundled monogram's native size. [See #212](https://github.com/brettchalupa/usagi/issues/212) - `usagi.save` now rejects unsupported table shapes with a clear message instead of either a cryptic serde error or silent data loss. Tables with sparse integer keys (`{[6]=1, [7]=2}`), gaps in a 1..n array (`{[1]="x", [3]="z"}`), or mixed string/integer keys now error up front and point at the workaround. JSON only supports maps with string keys or dense `1..n` arrays. [See #220](https://github.com/brettchalupa/usagi/issues/220) - Fixed slow shutdown after long sessions: switched Lua from generational to incremental garbage collection, which keeps the heap bounded during play so closing doesn't stall sweeping accumulated dead objects on exit. [See #232](https://github.com/brettchalupa/usagi/issues/232) - Pause menu now always uses the engine's color palette (Pico-8) so that custom color palettes don't inadvertently lead to illegible pause menu colors.
v0.8.0 Breaking: - Color slot indices are now **1-based** instead of 0-based, matching `gfx.spr` and Lua's array convention. The `gfx.COLOR_*` constants shift up by one. `gfx.COLOR_BLACK` is now `1` (was `0`), `gfx.COLOR_PEACH` is `16` (was `15`). Code that uses the named constants is unaffected; code that passes literal integers (`gfx.clear(0)`, `gfx.rect_fill(..., 7)`) needs to bump each literal by 1 or switch to the named constants. Slot `0` and any index above the active palette's length now render as the magenta out-of-range sentinel. - `gfx.spr_ex` and `gfx.sspr_ex` gained three required trailing params: `rotation` (radians), `tint` (palette color), and `alpha` (`0..1`). Use `0, gfx.COLOR_WHITE, 1.0` for the identity values to preserve the old behavior. The simple `gfx.spr` / `gfx.sspr` signatures are unchanged. See the README's "Scaling sprites" subsection for wrapper recipes if you find the verbose call sites painful. Features: - Custom color palettes via `palette.png`. Drop a PNG at your project root and Usagi swaps the default Pico-8 palette for yours. Pixels read in row-major order (left-to-right, top-to-bottom) so any rectangular shape works. 16x1 strips, 16x2 grids, 4x4 grids, etc. Color count = `width × height`. lospec.com's "1px cells" exports are the canonical source. Hot-reloads like `sprites.png`, ships in `usagi export` bundles. The `gfx.COLOR_*` constants stay as slot indices, so they keep resolving through the same slots, but the RGB at each slot is whatever you painted. Slots beyond your palette's range render as magenta. The ColorPalette tool reflects the active palette. New `examples/palette_swap` ships a sweetie16 palette; delete its `palette.png` to see the same Lua in Pico-8 colors. - New `sfx.play_ex(name, volume, pitch, pan)`, `music.play_ex(name, volume, pitch, pan, loop)`, and `music.mutate(volume, pitch, pan)` for programmatic audio control. `play_ex` is fire-and-forget per-call params (use it for random-pitch footsteps, panned UI cues, attenuated dialogue beeps). `music.mutate` modulates the currently-playing track in place with replace semantics. This is useful for ducking music under dialog, pitch-warping during hitstun, and fade-outs. Pan is `-1..1` (left to right), volume `0..1`, pitch a raw multiplier (`1.0` = identity). The `examples/sound` demo gets a random-pitch jump on BTN3; `examples/music` ducks the track while LEFT is held. - New `gfx.text_ex(text, x, y, scale, rotation, color)` for scaled and rotated text. Scale is a font-size multiplier (use integers for crisp pixel-art text; fractional values blur). Rotation is in radians (use `math.rad(deg)` for literal degrees) and pivots around the text's center. New `examples/text` shows a big scaled title, a sin-wave wiggling subtitle, and a static tilted label. - New `gfx.rect_ex(x, y, w, h, thickness, color)`, `gfx.circ_ex(x, y, r, thickness, color)`, and `gfx.line_ex(x1, y1, x2, y2, thickness, color)` for thick-stroke shape outlines. `circ_ex` strokes are centered on the nominal radius so concentric rings at adjacent radii sit flush instead of leaving rounding gaps. The `examples/shapes` demo now includes a small concentric-rings showcase. - `gfx.spr_ex` / `gfx.sspr_ex` now support rotation, tint, and alpha. Rotation is in radians (use `math.rad(deg)` for literal-degree values) and pivots around the sprite's center. Tint is a palette color multiplied over the sprite (`gfx.COLOR_WHITE` is the identity; other colors recolor for hit flashes etc.). Alpha is `0..1` for fade-in/out. The `examples/spr` demo now exercises all three (spinning bunny, tint-flashing ship via BTN1, pulsing-alpha bullet). - New `input.mouse_scroll()` returns the per-frame vertical scroll delta (positive up, negative down, `0` when no scroll). Works the same on a mouse wheel or a trackpad two-finger swipe. The mouse example now uses it to cycle the spark color. - Upgraded the embedded Lua runtime from 5.4 to 5.5. No game-code changes are expected for typical Usagi scripts; the bundled `.luarc.json` (shipped via `usagi init` / `usagi refresh`) now pins `runtime.version` to `Lua 5.5` so the LSP matches. - TilePicker: LMB on a tile copies its `spr` index (existing behavior). RMB click-and-drag now selects a tile-aligned rectangle on the sheet and copies `sx,sy,sw,sh` ready for `sspr`. The current selection stays visible: a highlight box on the sheet plus a readout in the header showing both the `spr` index (for single tiles) and the `sspr` source rect. A live preview rect tracks the drag. - `sprites.png` is now explicitly loaded with POINT (nearest-neighbor) texture filtering, matching the bundled font, so the pixel-art intent is pinned in the engine rather than relying on a default. - TilePicker: hold middle mouse and drag, or hold space and drag with the left mouse, to pan the sheet. Use the scroll wheel to zoom (anchored on the cursor so the pixel under the mouse stays put). The header also shows the sheet pixel coords under the cursor. - New `usagi.dump(v)` helper: pretty-prints any Lua value to a string, recursing into tables with sorted keys and cycle detection. Pair with `print` for terminal debugging, or feed into `gfx.text` to draw on screen. - New `gfx.px(x, y)` reads a pixel from the most recently rendered frame and returns `(r, g, b, palette_index)` as multiple values. The palette index is the 1-based slot for an exact RGB match, or `nil` for off-palette colors. All four returns are `nil` for off-screen coordinates or on the very first frame before any drawing has happened. Reads reflect the previous frame's finished image, so they don't see in-progress draws inside the current `_draw`. Useful for collision-by-color, fog-of-war reveals, palette-swap effects, and water reflections. - New `gfx.spr_px(index, x, y)` reads a pixel from `sprites.png`. `index` is a 1-based sprite slot (same shape as `gfx.spr`); `(x, y)` is the offset inside that cell. Returns the same `(r, g, b, palette_index)` shape as `gfx.px`. All four returns are `nil` for an out-of-range index, out-of-cell coordinates, a project with no `sprites.png`, or a fully transparent source pixel (so the ergonomic `if r then ...` check covers both "no sheet" and "alpha hole" cases). Useful for pixel-perfect sprite collision and for data-baked levels where you paint the layout into the sheet and scan it at startup. - New `examples/px` cart demonstrates both reads side-by-side: a small maze where movement consults `gfx.px` for collision-by-color, plus a `gfx.spr_px` scan that re-renders sprite 1 pixel-by-pixel next to its `gfx.spr` original. - Bundled font upgraded from monogram (95 ASCII glyphs) to monogram-extended (504 glyphs covering full Basic Latin, Latin-1 Supplement, Latin Extended-A, partial Greek and Cyrillic). Text like `café naïve jalapeño`, `Здравствуй, мир!`, and `Καλημέρα κόσμε` now render. Same look, same line height, just more codepoints. Updated `examples/text` shows the new chars. - Custom font support: drop `font.png` at your project root and Usagi uses it for `gfx.text` / `gfx.text_ex` / `usagi.measure_text`. Engine UI (FPS overlay, pause menu, error text) keeps the bundled font so layout stays predictable regardless of what you ship. The font's natural line height drives `font.base_size()` at runtime, so larger or smaller fonts render at their design size with no scaling. - New `usagi font bake <font.ttf> <size>` subcommand bakes a TTF/OTF into the custom-font format (a single PNG with glyph metadata embedded as a zTXt chunk). Defaults to writing `font.png` in the current directory, so the output is immediately a project drop-in. Includes the CJK Unified Ideographs block by default for fonts that cover it (kanji/hanzi/hanja); pass `--no-cjk` to skip. Pass the font's natural design size for crispest output (e.g., `15` for monogram-style 5×7 fonts, `18` for Silver, `8` for Misaki Gothic). New `examples/custom_font` ships a Silver-baked demo with multi-script text. - Per-game gamepad remapping for BTN1/BTN2/BTN3 alongside the existing keyboard remapping. New "Configure Gamepad" entry under the pause menu's Input sub-menu, mirroring the Pico-8-style sequential capture flow used by "Configure Keys": face buttons and shoulder/trigger positions are bindable; dpad, Select/Start/Home, and stick clicks are reserved. Bksp or Select undoes the previous capture; Del or Start resets every override. Overrides persist per game as `pad_map.json` next to `keymap.json`. Pause titles renamed to `KEYBOARD CONFIG` and `GAMEPAD CONFIG`. - Pause menu reorganized: a new Settings sub-menu now holds Music, SFX, Fullscreen, and Input, so the Top stays focused on Continue plus the destructive actions (Clear Save Data, Reset Game, Quit). The Input sub-menu is reached via Top > Settings > Input. - Pause menu lays out more reasonably at low resolutions. Item left-margin and the Input Tester's binding columns scale with the game's resolution instead of fixed pixel offsets, so games running at non-default sizes (vertical orientations, 128x128 prototypes) no longer push columns past the right edge. The volume meter's `xx%` readout is omitted when it wouldn't fit. - New `usagi.menu_item(label, callback)` registers up to 3 custom rows on the pause menu's Top view, between Continue and Settings. Use cases: jumping back to a title screen, bumping a level counter, granting resources for testing. The callback fires on selection; the menu closes by default but stays open if the callback returns Lua `true` (handy for repeatable in-game tweaks). Items auto-clear before each `_init` re-run so fresh registrations always land on a clean slate. `usagi.clear_menu_items()` wipes them manually, which is what `examples/menu_item.lua` uses to swap the registered set when transitioning between its title and gameplay scenes. - New `usagi.toggle_fullscreen()` flips fullscreen state from Lua and returns the new state as a bool. Paired with `usagi.is_fullscreen()` for reading current state without flipping. Both persist to `settings.json` the same way the pause-menu Fullscreen row and the Alt+Enter shortcut do, so the three toggle paths stay in sync. Intended for games that ship a custom pause menu or settings screen and need to drive fullscreen from script. - New `usagi.PLATFORM` string reports the build target the binary was compiled for: `"web"`, `"macos"`, `"linux"`, `"windows"`, or `"unknown"` (for builds on uncovered targets like BSDs). Lets games gate code paths by platform without parsing user-agent strings or shelling out, e.g. `if usagi.PLATFORM ~= "web" then ... end` for desktop-only features. - New `usagi.quit()` terminates the main loop the same way the pause-menu Quit row and Shift+Esc do, intended for custom in-game pause / title menus. On web the call still flips the internal flag but the emscripten main loop owns lifetime, so the canvas freezes on the last frame rather than tearing down the page. Gate with `usagi.PLATFORM` if your custom menu shouldn't expose a quit option on web. - GIF recordings and PNG screenshots now land in the user's Downloads directory (e.g. `~/Downloads/<game>-YYYYMMDD-HHMMSS.gif`) instead of a project-local `captures/` folder. Shipped binaries write somewhere players can actually find regardless of where the exe was launched from. Falls back to `<cwd>/captures` if the OS doesn't expose a Downloads dir. - GIF recorder reworked into a rolling buffer. The engine now keeps the last ~5 seconds of gameplay in memory at all times; pressing F9 / Ctrl+G / Cmd+G writes that buffer out as a `.gif`. No more start / stop toggle: trigger the save after the cool moment, not before. Per-frame timing reflects real frame dt with a 30fps floor, so a game that stutters no longer produces a sped-up GIF. The expensive work (LZW encode + disk write) happens only on save, not every frame, which fixes the chug that recording caused in heavier games. The always-on REC indicator is gone (recording is permanent now). - New `_config().pause_menu = false` disables the built-in pause overlay so games can roll their own menu system. Esc / P / Enter / gamepad Start flow through to user code instead of opening the engine's menu. What you keep: raw keyboard reads (`input.key_*`), the abstract direction and BTN actions, and the standalone APIs `usagi.toggle_fullscreen`, `usagi.is_fullscreen`, and `usagi.quit`. What you lose: the built-in pause overlay (which means `usagi.menu_item` registrations no longer render anywhere), the Configure Keys / Configure Gamepad screens, the Input Tester, keyboard remap UI, and gamepad-driven menu nav. Suitable for keyboard-driven prototypes; gamepad-heavy games that want full control should keep the default or fork. New `examples/custom_menu.lua` ships a minimal hand-rolled menu (Resume, Toggle Fullscreen, Quit) wired up to the new APIs. - The `usagi tools` window adopts a clean dark theme of its own, independent of the engine's Pico-8 palette. Less competing color for the eye when you're picking sprites, inspecting saves, or playing back music. The ColorPalette tool still displays the project's actual palette (the whole point of that tool); only the surrounding chrome changed. - First-boot music and sfx volumes default to `1.0` (full) instead of `0.8`. Players with an existing `settings.json` keep whatever they previously set; this only affects fresh installs and new game ids. The same `1.0` is also the Shift+M unmute target now. Fixes: - `gfx.rect` no longer drops the top-right corner pixel on some desktop environment + GPU configurations. Visual output matches the old path on every platform that already rendered correctly.
v0.7.2 Fixes: - `input.mapping_for` now properly returns the key string instead of `"?"` for custom mappings. - VSync enabled for the engine to fix screen tearing. See [#132](https://github.com/brettchalupa/usagi/issues/132)
v0.7.1 Features: - Usagi version is logged on game start. - Raylib log level is set to warning to reduce noise. Set env var `USAGI_VERBOSE=1` to get full Raylib logs. Fixes: - Web games don't crash when loading due to fullscreen error. See [#154](https://github.com/brettchalupa/usagi/issues/154) - Web games now load with default shell. They no longer display a black screen. Fixes CSS error introduced in v0.7.0.
v0.7.0 Features: - `MOUSE_MIDDLE` (a.k.a. scroll wheel click) support for moues input checks. - `effect.stop()` to end all currently running effects. Fixes: - `input.pressed` and `input.released` now edge-detect the analog stick the same way they do the d-pad, so menus (including the engine pause menu) can be navigated with the left stick. - Closing and opening the menu swallows input so that it doesn't accidentally trigger presses. See [#130](https://github.com/brettchalupa/usagi/issues/130) - Live reload with `usagi dev main.lua` or `usagi dev game.lua` now works again. Nested paths or `usagi dev` worked as expected in v0.6.1 but passing a filename within the current directory would break live reload. See [#136](https://github.com/brettchalupa/usagi/issues/136) - <kbd>Enter</kbd> no longer closes the Pause menu when it's open but instead is used to confirm the selection. It was awkward as a toggle. - Toggle fullscreen now works on web. - "Quit" Pause menu option is not hidden on web since it didn't do anything. - Up and down page navigation keyboard keys no longer scroll the page for web builds. Before this up and down, etc. were scroll page, not registering as game input. See [#112](https://github.com/brettchalupa/usagi/issues/112) - Effects reset when the game resets. Before they'd keep running, shaking the screen or hitstopping when they shouldn't.
v0.6.1 Fixes: - No longer crash on Windows when `require`d Lua files have a syntax error. See [#105](https://github.com/brettchalupa/usagi/issues/105). - No longer crash on Windows when invalid args are passed. See [#103](https://github.com/brettchalupa/usagi/issues/103).
v0.6.0
Features:
- Experimental `usagi update` command to update the binary in place when new
versions are released. Won't be useful and fully testable until the next
release comes out.
- `usagi refresh` command to update the ancillary engine files when a new
version is released. Currently updates `meta/usagi.lua`, `.luarc.json`, and
`USAGI.md`. Does **not** update `main.lua`. Use this after `usagi update` to
get the docs and LSP integration for the `usagi -V` you're using.
- New `effect.*` Lua module for engine-level juice. Four primitives, all decay
automatically once per frame:
- `effect.hitstop(time)` freezes `_update` for `time` seconds.
- `effect.screen_shake(time, intensity)` shakes the blit, magnitude in game
pixels, decays linearly.
- `effect.flash(time, color)` full-screen palette-color overlay that fades
from opaque to transparent.
- `effect.slow_mo(time, scale)` scales the `dt` passed to `_update`;
`scale=0.5` is half-speed, `scale=0` freezes (use `hitstop` for that).
Stacking rule across all four: longer duration wins, latest magnitude wins;
spam-calling is safe. See `examples/effect.lua` for a runnable demo. The
`notetris` example now uses `effect.screen_shake` in place of its bespoke
shake.
- New `usagi.SPRITE_SIZE` constant (default `16`) for tile-grid math without
hardcoding the cell size. Same value the engine uses internally for `gfx.spr`
indexing, the tilepicker tool, and the window-icon slicer. Override the
default by setting `_config().sprite_size`; the new value flows through every
consumer (Lua draws, icon slice on session and `usagi export --target macos`,
tilepicker grid in `usagi tools`).
- `_config()` can override the game's render resolution via `game_width` and
`game_height` (defaults 320 and 180). The internal RT is sized to those dims;
`usagi.GAME_W` / `GAME_H` reflect the active values. Tested band is roughly
320x180 to 640x360; pause-menu and tools UI are pixel-fixed and may overflow
at very small sizes or look sparse at very large ones. The web export
templates the canvas backing-store and aspect ratio from the configured
resolution, so non-16:9 / non-default games ship correctly with the default
shell (no `--web-shell` needed) and embed cleanly in itch at any iframe size.
Sprite size and bundled font are still fixed at 16 and 5x7.
```lua
function _config()
return { game_width = 480, game_height = 270 }
end
```
Fixes:
- A persistent error in `_update` or `_draw` no longer spams stderr 60x/sec.
`record_err` now logs only when the message changes; the on-screen overlay
still updates every frame, so users see live changes when they edit and save.
v0.5.0 Breaking: - `settings.json` replaces the single `volume` key with `music_volume` and `sfx_volume`. Existing settings are auto-migrated on load: the old `volume` value is copied into both new fields the first time the engine reads them. No action required. - `input.down(action)` is renamed to `input.held(action)` and `input.mouse_down(button)` to `input.mouse_held(button)`. The old names collided with directional input names (`input.down(input.DOWN)` was ambiguous) and "held" reads more naturally as the level-state pair to the edge-state `pressed` / `released`. Update calls; behavior is unchanged. Features: - Pause Menu is now navigable. Up/Down moves between items, Left/Right adjusts values, BTN1 (<kbd>Z</kbd>/gamepad-A) confirms, BTN2 (<kbd>X</kbd>/gamepad-B) goes back. Items: Continue, Music volume, SFX volume, Fullscreen, Input, Clear Save Data (with a confirm dialog), Reset Game, Quit. - Music and SFX have separate persisted volume levels, each rendered as a 5-bar meter (steps of 20%). Default is 80% for both. - <kbd>Shift</kbd>+<kbd>M</kbd> now mutes both channels at once and a second press restores both to their defaults. - Pause Menu's Input view shows a live tester (D-pad + button rects light up while pressed) and a "Configure Keys >" entry that opens a Pico-8-style keyconfig flow: highlights one action at a time, captures one key, advances. <kbd>Esc</kbd> cancels, <kbd>Delete</kbd> resets all overrides. Keyboard-only for now; gamepad bindings stay fixed. The new mappings persist per-game in `keymap.json` next to `settings.json` (web: localStorage `usagi.keymap.<game_id>`). Override semantics are "replace": once you map LEFT to W, the default arrow Left no longer fires LEFT. - Switch face buttons follow Nintendo convention: BTN1 fires from A (east) and BTN2 from B (south), so "A confirms, B cancels" feels native on Switch. Xbox and PlayStation are unchanged (BTN1=south, BTN2=east). Triggers (LB/RB) and BTN3 stay put across all families. - New Lua API for source-aware control glyphs: `input.mapping_for(action)` returns the label of the active source's primary binding (e.g. `"Z"` while the player is on keyboard; `"A"` on Xbox, `"Cross"` on PlayStation, `"B"` on Switch when the active source is gamepad). Gamepad family is auto-detected via `GetGamepadName` and falls back to Xbox for unknown / generic / Steam Deck pads. The engine tracks the most recent source automatically, switching only when a _bound_ input fires so stray keys don't flip it. `input.last_source()` returns `"keyboard"` or `"gamepad"`; matching constants are `input.SOURCE_KEYBOARD` and `input.SOURCE_GAMEPAD`. Examples that previously hardcoded `BTN1`/`BTN2`/`BTN3` in their on-screen prompts (sound, music, save, shader, rng, snake, dialog, operators, mouse) now use `input.mapping_for` so the prompts adapt to the active device. - New input functions: `input.released(action)` and `input.mouse_released(button)` fire the frame the input transitions from held to up. Mirrors `pressed` for the release edge; useful for charge-and-release mechanics (jump-on-release, slingshot pull-back). - New `util` global with drop-in math/geometry helpers: `util.clamp`, `util.sign`, `util.round`, `util.approach`, `util.lerp`, `util.wrap`, `util.flash`, `util.vec_normalize`, `util.vec_dist`, `util.vec_dist_sq`, `util.vec_from_angle`, `util.point_in_rect`, `util.point_in_circ`, `util.rect_overlap`, `util.circ_overlap`, `util.circ_rect_overlap`. Pure Lua, available without `require`. Source is at `runtime/util.lua` for forkability. Open it to read the implementations or override individual functions in your own `_init`. Functions that take shaped tables check the shape and raise an error pointing at your call site (e.g. `util.rect_overlap: arg 1 table missing or non-numeric field 'h'`) instead of failing deep inside the helper. `util.min` / `util.max` aren't included since Lua's `math.min` / `math.max` already do the job. - Direct keyboard reads via `input.key_pressed(key)`, `input.key_held(key)`, and `input.key_released(key)`, paired with `input.KEY_*` constants for letters, digits, F1–F12, arrows, modifiers, common punctuation, and a few specials (Space, Enter, Escape, Tab, Backspace, Delete). Documented as an escape hatch — these bypass the keymap override and gamepad bindings, so they're intended for dev hotkeys (e.g. <kbd>F1</kbd> to toggle a debug overlay) and keyboard-and-mouse-only games. Anything a player should be able to remap or reach with a controller still belongs on the abstract `input.held` / `input.pressed` / `input.released` actions. Raw gamepad reads remain intentionally unexposed. - CLI output is now color-coded: success and reload messages render in green, warnings (graceful fallbacks like a missing keymap or skipped export target) in yellow, and errors in red. The `[usagi]` prefix is dimmed so the message itself reads as the foreground content. Reload messages no longer look like errors at a glance. Color is auto-disabled when stdout isn't a terminal (piping to a file, CI logs) or when `NO_COLOR` is set, per <https://no-color.org>. Fixes: - <kbd>Alt</kbd>+<kbd>Enter</kbd> no longer opens the Pause Menu while toggling fullscreen. Pause Menu only opens on <kbd>Enter</kbd> when Alt isn't held. - `usagi` commands now correctly log the output on Windows and the exported game window does not show them, see #79.
PreviousNext