Skip to content

Spritesheets and Tilemaps

In this tutorial, you’ll learn how to:

  • Convert an image into a PythonExtra-compatible spritesheet
  • Load it and draw individual tiles
  • Render a full tilemap made of tile indices

This is perfect for platformers, top-down maps, menus, or puzzle games!


Create a tileset using any image editor like Aseprite, Piskel, or even Photoshop.
All tiles must be the same size — for example, 16×16 pixels.

Here’s an example of a classic Mario tileset:

Tiles from the classic Mario

Make sure the image is in .png or .jpg format, and that it’s tightly packed — no spacing between tiles.


You’ll need the fxconv tool included in the PythonExtra dev tools.

Run this command in your terminal:

Terminal window
python tools/fxconv-main.py --bopti-image super-tiles.jpg \
-o smario_tiles.py \
--cg profile:p4_rgb565 name:image --py

This will generate a file called smario_tiles.py, which contains the image as a gint.image() object.

You can download a pre-converted version [here](](/wiki/img/python/examples/smario_tiles.py)

OptionWhat it does
--bopti-imageConverts an image file
-o smario_tiles.pyOutput file name (Python module)
--cg profile:p4_rgb565Compression profile for ClassPad (fast & compact)
name:imageVariable name that will hold the image object
--pyExport as Python code instead of C

You’ll end up with something like this inside the file:

smario_tiles.py
from gint import image
image = image(...) # auto-created with width, height, etc.

A tilemap is just a 2D list of tile indices:

level = [
[0, 1, 2],
[0, 3, 4],
[0, 0, 0]
]

Each number tells which tile from the spritesheet to use.

Tiles mapping to indexes

Tile 0 is the top-left tile, tile 1 is the one next to it, and so on, left-to-right, row-by-row.


Here’s the function we’ll use:

def draw_tilemap(tilemap, tileset_img, x0=0, y0=0):
tiles_per_row = tileset_img.width // TILE_SIZE
for y, row in enumerate(tilemap):
for x, tile_index in enumerate(row):
tile_x = (tile_index % tiles_per_row) * TILE_SIZE
tile_y = (tile_index // tiles_per_row) * TILE_SIZE
dsubimage(x0 + x * TILE_SIZE, y0 + y * TILE_SIZE, tileset_img,
tile_x, tile_y, TILE_SIZE, TILE_SIZE)
  • tiles_per_row is how many tiles fit horizontally in the image
  • tile_index is the number from the map
  • We compute its position in the image and use dsubimage() to blit it

Screenshot of the final result

from gint import *
from smario_tiles import image as tileset_img
TILE_SIZE = 16
SPRITE_WIDTH = tileset_img.width
def draw_tilemap(tilemap, tileset_img, x0=0, y0=0):
tiles_per_row = SPRITE_WIDTH // TILE_SIZE
for y, row in enumerate(tilemap):
for x, tile_index in enumerate(row):
tile_x = (tile_index % tiles_per_row) * TILE_SIZE
tile_y = (tile_index // tiles_per_row) * TILE_SIZE
dsubimage(x0 + x * TILE_SIZE, y0 + y * TILE_SIZE, tileset_img,
tile_x, tile_y, TILE_SIZE, TILE_SIZE)
# Sample tilemap
level = [
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 1, 2, 3, 0, 0, 0, 1, 2, 3, 0 ],
[ 0, 5, 6, 7, 0, 0, 0, 5, 6, 7, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 14, 13, 14, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 14, 14, 14, 14, 14, 0, 0, 0, 15 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15 ],
[ 35, 36, 37, 0, 0, 0, 0, 0, 15, 15, 15 ],
[ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39 ]
]
# Run
dclear(C_WHITE)
draw_tilemap(level, tileset_img)
dupdate()
getkey()

Need help converting images or slicing sprite animations?
Ask the 👨‍💻 PythonExtra ChatGPT!