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 with 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!