Spritesheets - Some progress on Tufty2040

Hi folks!

So I invested some time learning how to make my own custom sprite sheets. I didn’t see a more in depth tutorial on this, so I decided to just make this post. The huge advantage of spritesheets is that it is greate for animating vs opening individual images. It also has alpha transparency, and you can elect a color to turn transparent.

Creating a sprite sheet
My workflow is simple for making sprite sheets using Photoshop. This can be done through GIMP as well. Here is the process

  1. Open Photoshop (editing software) and create a blank canvas that is 128x128 pixels.
  2. Go to Edit>Preferences (Ctrl+K)>Guides,Grid & Slices.
  3. Change “Gridline Every” to “8 Pixels” with Subdivision 1.
  4. View>Show>Grid (Ctrl+') to see the grids.
  5. Micropython functions allow us to display each 8x8 pixel box (more on that later). You have 256 boxes available on a 128x128 canvas (16 boxes x 16 boxes). For animations, add your image sequences within the boxes. Keep it regular like my example below has 4x7 boxes as the character standard. The character is in idle animation that I scaled to make it fit in my grid. {Character animation file credit: pzUH}
  6. Save the file as JPEG or PNG (Does not matter). E.g. Robot.jpeg
  7. Download the spritesheet to RGB332 conversion file from Pimoronis Github here. and save it to a folder of your choice. E.g. SpritesheetsConversions
  8. Go to your command-line of choice, navigate to the folder >SpritesheetsConversions where you downloaded the .py file. Make sure your image file that you want to convert to the spritesheet format is in the same folder as the .py file. Enter the following command based on your file name. Using the examples I’ve given above:
spritesheet-to-rgb332.py Robot.jpeg

The new file automatically saved in the folder of the .py program. The new file should be something like Robot.rgb332
9. Go to Thonny, and upload the Robot.rgb332 file.

You are good to go for the next part. Writing the code to display your sprites and animation.

NOTE: You can remove the background color in sprites. In your editing software, keep a background color that is unique. Note down its RGB value so you can remove it in the code. For me, I kept it to simply BLACK which is 255,255,255.

Displaying a sprite
We’re going to start with something basic. Displaying just the first character. the 4x7 box robot. The most important function is this one:

display.sprite (BOX_NUMBER_X, BOX_NUMBER_Y, BOX_LOC_X, BOX_LOC_Y, SCALE_FACTOR, ALPHA_TRANSPARENT_COLOR)

You can elect a color to make it transparent. For the code below I selected BLACK = create_pen(0,0,0) because the background of my character is BLACK.

Full code to make the above work is here:

#IMPORT STATEMENTS
from picographics import PicoGraphics, DISPLAY_TUFTY_2040,PEN_RGB332

#SET DISPLAY
display = PicoGraphics(display=DISPLAY_TUFTY_2040, pen_type=PEN_RGB332)

#CREATE A BYTEARRAY TO READ THE SPRITESHEET INTO
robot = bytearray(128 * 128)
open("Robot.rgb332", "rb").readinto(robot)

#SET THE ACTIVE SPRITESHEET
display.set_spritesheet(robot)

#CREATE SOME USEFUL PENS
BLACK = display.create_pen(0,0,0)
WHITE = display.create_pen(255,255,255)

#SETTING THE SIZE OF ONE OF YOUR BLOCKS (8px BY 8px) and scaling choice of your character
SPRITE_SIZE = 8
SCALE = 2

while True:
    
    # The i represents number of boxes on the x-axis containing your character and j represents the y axis.
    for i in range(4):
        for j in range(7):
            
            # Calculate the x and y position on the LCD screen. Each consecutive block is placed 8px after the start of the previous one.
            lcd_x = i * SPRITE_SIZE
            lcd_y = j * SPRITE_SIZE
            
            # display.sprite (BOX_NUMBER_X, BOX_NUMBER_Y, BOX_LOC_X, BOX_LOC_Y, SCALE_FACTOR, ALPHA_TRANSPARENT_COLOR)
            # The SCALE multiplier offsets where each box will be placed correctly
            display.sprite(i, j, lcd_x*SCALE, lcd_y*SCALE, SCALE, BLACK)
    
    #UPDATE FUNCTION
    display.update()
    
    #CLEAR SCREEN
    display.set_pen(WHITE)
    display.clear()

This code places the character on the top left. To move your character, simply add another variable in your display.sprite() function. E.g:
display.sprite(i, j, (lcd_x*SCALE)+OFFSET_X, lcd_y*SCALE+OFFSET_Y, SCALE, BLACK)

Result:

Animating sprites
I am aware that different people have different ways to animate sprites. I am open to suggestions on optimizing this, but this is my method at the moment.

Changes made to animate:

  • Added math and time import statement
  • Added a way to calculate framerate and get the frame number
  • Added a way to set where the sprites will be read from on every frame
  • Added an offset in display.sprite() to read where the sprite can be shown from
#IMPORT STATEMENTS
from picographics import PicoGraphics, DISPLAY_TUFTY_2040,PEN_RGB332
import math, time

#SET DISPLAY
display = PicoGraphics(display=DISPLAY_TUFTY_2040, pen_type=PEN_RGB332)

#CREATE A BYTEARRAY TO READ THE SPRITESHEET INTO
robot = bytearray(128 * 128)
open("Robot.rgb332", "rb").readinto(robot)

#SET THE ACTIVE SPRITESHEET
display.set_spritesheet(robot)

#CREATE SOME USEFUL PENS
BLACK = display.create_pen(0,0,0)
WHITE = display.create_pen(255,255,255)

#SETTING THE SIZE OF ONE OF YOUR BLOCKS (8px BY 8px) and scaling choice of your character
SPRITE_SIZE = 8
SCALE = 2
OFFSET_X = 100
OFFSET_Y = 50


while True:
    
    #DIVIDING MS BY 125 GIVES 8 FRAMES PER SECOND. % 10 gives a way to keep count of that and get a t value from 0 to 9
    t = math.floor(time.ticks_ms() / 125) % 10
    
    #DEPENDING ON WHICH t VALUE WE GET, WE WANT TO SELECT WHICH BLOCK WE WANT TO START WITH TO DISPLAY OUR GRID OF SPRITES
    if t<1:
        START_X = 0
        START_Y = 0
    elif t<2:
        START_X = 4
        START_Y = 0
    elif t<3:
        START_X = 8
        START_Y = 0
    elif t<4:
        START_X = 12
        START_Y = 0
    elif t<5:
        START_X = 0
        START_Y = 7
    elif t<6:
        START_X = 4
        START_Y = 7
    elif t<7:
        START_X = 8
        START_Y = 7
    elif t<8:
        START_X = 12
        START_Y = 7
    
    # The i represents number of boxes on the x-axis containing your character and j represents the y axis.
    for i in range(4):
        for j in range(7):
            
            # Calculate the x and y position on the LCD screen. Each consecutive block is placed 8px after the start of the previous one.
            lcd_x = i * SPRITE_SIZE
            lcd_y = j * SPRITE_SIZE
            
            # WE'VE ADDED START_X and START_Y OFFSET TO OUR i and j value.
            # display.sprite (BOX_NUMBER_X, BOX_NUMBER_Y, BOX_LOC_X, BOX_LOC_Y, SCALE_FACTOR, ALPHA_TRANSPARENT_COLOR)
            # The SCALE multiplier offsets where each box will be placed correctly
            display.sprite(i+START_X, j+START_Y, (lcd_x*SCALE)+OFFSET_X, (lcd_y*SCALE)+OFFSET_Y, SCALE, BLACK)
    
    #UPDATE FUNCTION
    display.update()
    
    #CLEAR SCREEN
    display.set_pen(WHITE)
    display.clear()

Some final notes. This code is a lean and straightforward version of animating something simple. It is best to take most of the code in its own def functions and not keep everything in main.

I love contributing to this channel. I think Tufty2040 is soo under-rated and we can build such a big community and pool in cool resources to help make our badges shine :)

If anyone knows of any good pixel sprite animation creating softwares or websites then please drop them below. Thank you!

Special thanks to @OllieTheFoxy for inspiring me. I wanted to make custom spritesheets and their post helped kick that off.

2 Likes

I am using CircuitPython on the Tufty and CircuitPythons display abstraction has support for sprites builtin.

Can you link me the documentation for it?

This is the basic tutorial: Introduction | CircuitPython Display Support Using displayio | Adafruit Learning System In the examples section they also cover sprite-sheets.

Congratulations on your Tufty2040 endeavours! I’m so glad you gave me a shoutout as well ^^ Let’s continue to push this hardware to its limits and make some graphics on it people might have thought impossible. :D

I also want to thank you for this!