At the company I work for, we recently launched a pixel-y status board called "Display" that immediately caught my eye:
It runs a custom app on the Tidbyt platform and was a successful Kickstarter in 2021. While it has a lot going for it (dimmable display, mobile app configuration, SDK and 3rd party apps, beautiful wood case), at $180 USD, it's still more than I want to spend.
A tweet I saw about it featured an animated character on the display which got me interested immediately. I dove right into a spritesheet animation tutorial on Adafruit. The basic idea is to use a single bitmap image that has all the frames on it, then use the displayio.TileGrid class to conveniently cycle through the frames. The finished demo:
Here's some notes on how I did it:
TileGridrepresents a grid of images taken from a source bitmap. The bitmap is partitioned into equally sized "tiles", and any of these sub-regions can be shown as an image in the
- To choose a tile from the bitmap to be shown on a
TileGrid, and to reference the bitmap's tiles are indexed in row-major order. For example, suppose we have a bitmap that is 256 x 256 pixels, and we define the tile size to be 64 x 64 pixels. Each of the bitmap tile regions would be numbered from 0 to 15.
- The tutorial encourages you to use a vertical spritesheet, because it's easy to create and simplifies indexing. But a horizontal one works just as well. For example, my spritesheet lives in a bitmap of 160 x 32 pixels, so each one of the five tiles is 32 x 32 pixels. Since we only need to display one frame at a time, we make the
TileGridhave a width and height of 1:
bitmap = displayio.OnDiskBitmap(filename) sprite = displayio.TileGrid( bitmap, pixel_shader=bitmap.pixel_shader, width=1, height=1, tile_width=32, tile_height=32 ) frame_count = int(bitmap.width / 32)
- To make the sprite move back and forth across the screen, I update the sprite's
xmember, keeping track of the current direction and when it's moved off-screen. In addition, I mirror the character when it is walking in the opposite direction by toggling the sprite's
flip_xattribute: this eliminates the need to have a second, flipped version of the spritesheet.
def advance_frame(): global current_frame current_frame = current_frame + 1 if current_frame >= frame_count: current_frame = 0 sprite_group = current_frame if sprite_group.x < -32 or sprite_group.x > 64: sprite_group.flip_x = not sprite_group.flip_x direction = -1 if not sprite_group.flip_x else 1 sprite_group.x += direction