Building scenes
These four modules load baked scenes, attach properties to tiles, create simple bitmap art in code, and reuse a fixed set of sprites. See /reference/ for the signatures.
picogame_scene
Section titled “picogame_scene”The loader turns a baked SCENE dictionary into a pg.Scene and a set of named handles. Use it for a level produced by the editor or scene_build.py. The same data and loader run on hardware and in the simulator. See /scene-format/ for the input format.
load(pg, scene, display=None, strip_h=None, font=None, bank=None) returns a View. It resolves the display in the same way as picogame_game.setup(), constructs the scene, and adds each tilemap, sprite, group, particle system, and HUD label. On an SPI display it allocates two width * strip_h * 2 render buffers, available as view.bufA and view.bufB. On a framebuffer target both are None. Pass a font such as terminalio.FONT if the data contains a HUD label. See /hardware/ for display backends and /memory/ for buffer costs.
load_bank(pg, bank) builds shared bitmaps/sounds/anims ONCE; pass the result as load(..., bank=...) for each level so unchanged art is not rebuilt per level.
The returned View is your handle to everything:
view.scene- the livepg.Scene. Callview.scene.refresh()each frame andview.scene.set_view(ox, oy)to scroll.view.named[name]- dict of name -> sprite / particles / HUD label for any layer given aname.view.group(tag)- list of sprites for a group layer (returns[]if the tag is unknown, so it is safe to iterate).view.tick(dt)- advance all auto-animated sprites; call once per frame withdtin seconds.view.tile_xy(px, py)- world pixel ->(tx, ty)cell of the primary (first) tilemap.view.is_solid(tx, ty)- shorthand fortile_has(tx, ty, "solid").view.tile_has(tx, ty, prop)- True if the primary tilemap’s tile at that cell has the named property (from the bakedtileprops).view.point(name)-(x, y)for a named point, or None.view.in_zone(x, y, tag=None)- first zone(tag, x, y, w, h)containing the point (optionally filtered by tag), else None.view.play(sound_id)- play a baked sfx by id (no-op if audio/sample is missing).view.tilemap/view.camera/view.zones/view.points/view.anims- the primary tilemap object, the camera tuple, and the raw collections.
import board, terminalioimport picogame as pgimport picogame_scene as pgsimport world1_scene
view = pgs.load(pg, world1_scene.SCENE, font=terminalio.FONT)player = view.named["player"]enemies = view.group("enemies")while True: view.tick(1 / 30) # advance auto-animations tx, ty = view.tile_xy(player.x, player.y) if not view.is_solid(tx, ty): player.move(player.x + 2, player.y) view.scene.refresh()picogame_tiles
Section titled “picogame_tiles”TileFlags stores one metadata bitfield for each tile index, so every cell using that tile shares the same properties. Use it with a hand-built pg.Tilemap. A scene loaded through picogame_scene already exposes the higher-level view.is_solid() and view.tile_has() methods.
Eight named bits and their masks: B_SOLID, B_HAZARD, B_LADDER, B_PLATFORM, B_WATER, B_COIN, B_EXIT, B_CUSTOM are bit indices 0..7; SOLID, HAZARD, LADDER, PLATFORM, WATER, COIN, EXIT, CUSTOM are the matching 1 << bit masks. Use the masks when building the table, the B_* indices when querying.
TileFlags(flags=None, tile_px=8) builds the table. flags is either a {tile_index: bitfield} dict or a list/bytes indexed by tile index; tile_px is the tile size used by the pixel helper.
tf.get(tile, bit=None)- the full bitfield of a tile, or one bool flag ifbit(aB_*index) is given.tf.set(tile, bit, value=True)- flag (or clear) a bit on a tile at runtime.tf.at(tilemap, tx, ty, bit)- flagbitof the tile at cell(tx, ty).tf.at_px(tilemap, px, py, bit)- flagbitof the tile under MAP-LOCAL pixel(px, py); the common collision probe.
import picogame as pgimport picogame_tiles as tiles
TILE = 8tf = tiles.TileFlags({1: tiles.SOLID, 2: tiles.COIN, 3: tiles.EXIT}, tile_px=TILE)
def blocked(level, tx, ty): # level is a pg.Tilemap return tf.at(level, tx, ty, tiles.B_SOLID)
if tf.at_px(level, px, py, tiles.B_SOLID): # is the tile under pixel (px, py) solid? stop()picogame_shapes
Section titled “picogame_shapes”These functions build single-colour PAL8 Bitmap objects in code. They suit prototypes and geometric art such as balls, bricks, or ships. Unlike Canvas, they return a reusable bitmap for a Sprite or Tilemap. Palette index 0 is transparent and index 1 contains the requested colour.
rect(w, h, color)- a filledw x hrectangle.circle(d, color)- a filled disc of diameterd.ring(d, color, thickness=2)- a circle outline of diameterd.from_mask(mask, color)- a bitmap from a list of strings;#,X, or1sets a pixel. Sized to the mask.atlas(frames_data, w, h, color)- pack a list ofw*h0/1 buffers into one horizontal multi-frame bitmap (one colour). The general “frame sheet” builder.color_frames(w, h, colors)- a multi-frame bitmap where frameiis a solid fill ofcolors[i]; frame 0 is already a colour. Index 0 transparent.tileset_colors(w, h, colors)- a tileset where frame 0 is EMPTY (transparent) and frameiis a solid fill ofcolors[i-1]. So a Tilemap reads tile value 0 as empty and 1..N as coloured tiles.poly_frames(size, points, nframes, color, fill=True)- bakenframesrotations of a polygon (points around centre, +y down) into asize x sizeatlas. The engine also rotates at runtime (Sprite.angle); baked frames trade a little RAM for a cheaper per-frame blit and pixel-stable art, so pick them for many always-rotating objects (asteroids), andanglefor one-off or continuous rotation. Setfill=Falsefor an outline.
import picogame as pgimport picogame_shapes as shp
ball = shp.circle(4, pg.rgb565(255, 255, 120))bricks = shp.tileset_colors(16, 8, [pg.rgb565(220, 70, 70), pg.rgb565(80, 140, 240)]) # value 0 empty, 1..2 colouredship = shp.poly_frames(16, [(0, -8), (6, 7), (0, 4), (-6, 7)], 16, pg.rgb565(200, 220, 255)) # 16 pre-rotated framessprite = pg.Sprite(ball, 100, 60)picogame_pool
Section titled “picogame_pool”Pool pre-allocates a fixed number of sprites for short-lived objects such as bullets, enemies, or pickups. sprite.visible marks an occupied slot and sprite.data can hold entity state. Spawning and freeing slots allocate no new sprites. See /memory/ for why stable allocation matters on the device.
Pool(scene, bitmap, capacity, anchor=None, fixed=False) pre-allocates capacity hidden sprites sharing bitmap, sets each anchor (if given) and data = None, and adds them all to scene (fixed= passes through to scene.add).
pool.items- the underlying list of sprites; iterate it directly for zero-alloc updates.pool.spawn()- make the first free (hidden) sprite visible and return it, or None if the pool is full.pool.free(s)- hide sprites(return it to the pool).pool.free_all()- hide every sprite (use on level reset).pool.count()- count of live (visible) sprites; cheap, but iterateitemsfor the sprites themselves.
import picogame as pgimport picogame_pool
bullets = picogame_pool.Pool(scene, bullet_bm, 6, anchor=(0.5, 0.5))
b = bullets.spawn() # a now-visible sprite, or None if fullif b: b.data = {"vx": 6} b.move(x, y)
for s in bullets.items: # zero-alloc iteration if not s.visible: continue s.move(s.x + s.data["vx"], s.y) if off_screen(s): bullets.free(s)