7. Budgets and real hardware

The console has on the order of 8 MB of RAM shared between MicroPython’s heap, the image strips and the interpreter, and a hard deadline every column. The budgets exist so you find out at build() rather than mid-game.

Resource

Budget

Notes

Layers

8

Most games use two or three

Sprites

100

Pools count in full, up front

Tilemaps

16

A label counts as one

Image strips

100

Per asset pack

Read them at runtime from vs2.limits — see vs2.limits.sprites and its neighbours in the reference.

Reading the errors

Every one of these is raised the first time the scene is entered, never later:

ResourceLimitError: sprite 101/100 in Vixeous (world: 62, hud: 39);
  reduce the sprite budget

The census names every layer holding sprites, so the oversized one is visible without counting by hand.

ResourceLimitError: tilemap 17/16 in Game (world: 14, hud: 3);
  reduce the tilemap budget

Remember labels land here, not in the sprite budget.

AssetLimitError: myname.mygame defines 103 images; this target supports 100

Too many strips in __images__.yaml. Combine related art into one strip with more frames.

Staying inside them

Text belongs in labels. Three lines of text as one sprite per character cost 54 of 100 slots. As three labels they cost three tilemap records, and the cost does not grow with the text.

Pools, not lists. A sprite_pool(count=16) states its cost in one number that shows up in the census. Sixteen individually created sprites do not.

Cell data, not sprites. Anything on a grid — terrain, a starfield, a tile-based background — is one tilemap record however many cells it has.

Keep update() free of allocation

Creating a tuple, dict or formatted string every tick adds up to garbage that stays on the console’s small heap until the next scene change. These operations reuse existing render objects. That does not guarantee that the surrounding Python expressions allocate nothing; the reasons are in why VS2 works this way.

sprite.x += 0.5
sprite.frame = 3
pool.spawn(x, y)
pool.despawn(enemy)
tilemap.view_y = depth % tilemap.tile_height
tilemap.cells[row * tilemap.columns + col] = WINDOWS
label.set_number(score, width=5)
joy1.held(LEFT)

The convenient tilemap[col, row] syntax creates a tuple when the indices are computed at runtime. For frequent writes, use the direct cells buffer as above; validate the row, column and tile value yourself, since it bypasses checked indexing. See direct cell writes. Sealing limits render-object creation, not all Python allocation. Measure heap stability on the target.

Moving to the console

The emulator and the console run the same renderer semantics, so a game that looks right in the emulator generally looks right on the disc. Three things only the real thing tells you:

Timing. The emulator does not enforce the per-column deadline. A scene near the tilemap budget with several large maps is worth watching on hardware.

Legibility. The 256 angular steps are spread around a large circumference at the rim and crammed into almost nothing at the centre, so the same glyph is crisp near the rim and unreadable near the middle. Text belongs at low Y on a HUD layer — the in-tree games put their scoreboards at y=0 or y=1. Nothing warns you about this; you have to look at it:

The same score at y = 1, crisp at the bottom rim, and at y = 44, a tiny unreadable smudge near the centre

The same label at y = 1 and at y = 44.

Backgrounds, colours and intensity. Dark or black backgrounds work better on the real Ventilastation than bright ones. Leave empty space empty — EMPTY_TILE cells and transparent pixels draw nothing — and keep any backdrop you do draw dark. A tilemap that lights the whole disc in bright colours is the exception, which is why the trench tiles in chapter 5 are near-black. How a colour looks on a monitor is not how it looks on the LEDs, and the emulator cannot judge their intensity for you, so always test your colours and intensities on the real hardware and adjust them there. Do it before you share a game, not after.

In the game

Add up Trench Run’s budget. The numbers are in vs2.limits:

Resource

Used by the game scene

Budget

Layers

2 (world, hud)

8

Sprites

1 ship + 16 enemies = 17

100

Tilemaps

1 trench wall + 1 score label = 2

16

Image strips

5 (ship, enemy, trench, numerals, steel8x8)

100

Everything is created in build(), so the whole table is checked the first time the scene is entered. The complete game is at the end of chapter 6.

Run it in the emulator, then run it on the real Ventilastation. The trench, the score and the title should all look the way they did on screen, but only the disc will tell you whether the colours are comfortable and the text is crisp.

Packaging

Follow assets, menu and sharing to package your game or submit its source folder as a pull request.

Where to look next

  • The API reference for the full surface, and the glossary for any term you have forgotten.

  • Going further for features this tutorial skipped.

  • Real games in the tree: games/demos/input_demo shows every control, and games/alecu/vixeous uses pools, a scrolling terrain map and labels together.

  • games/demos/povstress is the stress case, deliberately near the budgets.