Skip to content

Tile Maps (TileSheet / TileMap)

A library for easily writing tile-based map rendering. Suitable for RPG-style grid worlds, tiled backgrounds, stage-based puzzles, and more.

Class Role
TileSheet Wraps a tile image (a single BMP with tiles arranged in a grid). Can stamp individual tiles
TileMap Loads an fmrb_map JSON file and draws tiles layer by layer using TileSheet#stamp

Both classes are built into picoruby-fmrb-app and require no additional require.

TileSheet

Handles BMP tile sheet images (images with consecutive tiles arranged in a grid).

Constructor

TileSheet.new(gfx, path, cols:, rows: nil, tile_size: 16)
Argument Purpose
gfx FmrbGfx instance
path Path to a BMP already transferred to the graphics side (use @gfx.transfer_file to send it)
cols: Number of columns in the sheet (in tiles)
rows: Number of rows. If nil, automatically estimated from the BMP size (assumes 8-bit BMP)
tile_size: Pixel width and height of a single tile (default: 16)

Internally creates a SpriteImage with transparent color 0 and calls load_bmp.

Methods

Method Purpose
stamp(index, dst_x:, dst_y:) Stamp tile number index (row-major: row * cols + col) onto the canvas. nil or negative values are no-ops (for empty map cells)
destroy Release the internal SpriteImage
Attribute Description
image Internal SpriteImage
cols / rows Tile counts
tile_size Pixel size

Example

@gfx.transfer_file("/usr/share/sprites/test/tilesheet.bmp",
                   "/usr/share/sprites/test/tilesheet.bmp")
sheet = TileSheet.new(@gfx,
                       "/usr/share/sprites/test/tilesheet.bmp",
                       cols: 4, tile_size: 16)
sheet.stamp(0, dst_x: 0,  dst_y: 0)   # Stamp tile 0 at the top-left
sheet.stamp(5, dst_x: 16, dst_y: 0)   # Stamp tile 5 to the right

TileMap

Loads a map file and lays out tiles for each layer.

Constructor

TileMap.new(path)

path is a path on the core side (internal flash). Note that this is not on the graphics side. Two formats are accepted, and which one it is decided by the first four bytes:

Format
.map.bin The packed form. One byte per tile, read straight out of the file
.json The fmrb_map v1 JSON below

Prefer the packed form on the device. Loading a map used to mean parsing JSON, which for a large one took the best part of a minute; the packed form is read rather than parsed, and the same map arrives in milliseconds. The JSON stays readable and diffable, so it is the form to keep in a repository — export the packed one beside it.

Methods

Method Purpose
render(sheet, origin_x:, origin_y:, max_cols: nil, max_rows: nil) Draw all layers. origin_* is the drawing position for the top-left tile. max_cols/rows clips the display range (for viewporting when the map is larger than the screen)
render_view(sheet, origin_x:, origin_y:, view_x:, view_y:, view_w:, view_h:) Draw only the tiles the viewport touches, with sub-tile offsets. origin_* is where the viewport's top-left corner lands on the canvas; view_x / view_y are that corner in full-map pixels. This is what a scrolling world uses
tile_at(x, y, layer: 0) The tile id at those tile coordinates, or nil when it is out of range or empty
walkable?(x, y) Whether the tile there is in walkable_tiles. Out of range is false
event_at(x, y) Return the event Hash at tile coordinates (x, y). Returns nil if none
Attribute Description
width / height Map width and height (in tiles)
tile_size Pixel size of a single tile
tilesheet_path Path of the tile sheet to use
tilesheet_cols Number of columns in the tile sheet
layers Layer array
events Event array

Example

map = TileMap.new("/app/game/rpg_demo/world.map.json")
# Build the sheet using map.tilesheet_path / map.tilesheet_cols
sheet = TileSheet.new(@gfx, map.tilesheet_path, cols: map.tilesheet_cols)

# Render into an 11x11 tile viewport
map.render(sheet, origin_x: 16, origin_y: 16, max_cols: 11, max_rows: 11)
@gfx.present

fmrb_map JSON Format (v1)

{
  "format": "fmrb_map",
  "version": 1,
  "width": 20,
  "height": 15,
  "tile_size": 16,
  "tilesheet": "/usr/share/sprites/test/tilesheet.bmp",
  "tilesheet_cols": 4,
  "layers": [
    { "name": "ground", "data": [[0, 0, 1, ...], [...], ...] },
    { "name": "objects", "data": [[null, null, 5, ...], [...], ...] }
  ],
  "events": [
    { "x": 4, "y": 6, "kind": "talk", "text": "こんにちは" }
  ]
}
Key Description
format Fixed as "fmrb_map"
version 1
width / height Map size (in tiles)
tile_size Pixel size of a single tile
tilesheet graphics-side path of the sheet to use
tilesheet_cols Number of columns in the sheet
layers[] Each layer. data is a 2D array ([row][col] -> tile number, null for empty)
events[] Arbitrary events. Retrieved via event_at(x, y)

Map Creation Tools

Tile sheets and maps can be created with web tools (bundled in fmruby-core/tool/web/):

  • Sprite Editor: Create a BMP with 16x16 RGB332 tiles arranged in a grid
  • Map Editor: Place tiles + edit events -> export as a packed .map.bin or as fmrb_map JSON. It reads both

Start with ruby web_server.rb and access http://localhost:8080 (same server as the Console).

Example: Simple RPG

A complete RPG demo is available in /app/game/rpg_demo/. In fullscreen:

  • Left 176x176 (11x11 tiles): Field drawn with TileMap + TileSheet
  • Right 128x240: Panel displaying player coordinates / last event
  • Player is a separate BMP (16x16) controlled via SpriteInstance with arrow keys / D-pad

This directory includes .app.rb / .app.toml along with world.bmp, world.map.json, and player.bmp. The launcher performs a 3-level scan so it also picks up /app/<category>/<bundle>/*.app.toml, allowing you to keep assets alongside the app.