Making Sprite Sheets

The main key is to save your files as indexed bitmap image files. Then they can be loaded using the CircuitPython Image Load library. Exactly how you do this will depend on what software you are using. The sprite sheets in this guide were created using GIMP. The general process went something like:

  • Create new image width x height pixels
  • Use 1 pixel pencil tool to draw sprites
  • Save as .xcf file for future edits
  • When ready to export:
    • Image -> Mode -> Indexed
    • File -> Export As...
    • specify filename with .bmp extension
A similar export method can be used for the background images.

Flake Fall Speed

So how were different flake fall speeds implemented? Well, pretty simply. The speed is based on the flake index. It comes down to this one line of code (from the fancy version):

flake_pos[i] += 1 - flake[0] / NUM_SPRITES

The first flake falls the fastest. The last flake falls the slowest. That's why the various flake examples arranged the flakes from smallest to largest.

Since the y location is an integer value, a separate float value is used to allow for floating point math. That is what gets stored in flake_pos. This is simply changed to an integer when it comes time to set the flake position:

flake.y = int(flake_pos[i])

A fancier way to do this might be to allow for setting custom fall speeds for each flake. But that would require an additional storage mechanism and even more work to manually set up for each flake sprite sheet.

Another idea might be to somehow "weigh" the flake sprite, like how many non-transparent pixels it contains. And then base fall speed on that.

Animated Flakes?

Sure. Why not? That would be pretty cool. This could be done. Maybe in a future version.

Snow Accumulation

It's pretty easy to know when a flake hits the ground. But then what? How do we "add" snow to the currently accumulated snow. The simplest would be to just blindly add a few pixels around where the flake fell. However, this can lead to a "spikey" profile to the snow, which doesn't look very natural.

The current version of the code tries to deal with this, but in a pretty simple way. It does a local steepness check based on the surrounding snow and will only add pixels if it is currently not too steep. This works for the most part, but does lead to somewhat unnatural looking edge effects.

It'd be nice if this were fancier. Another maybe-in-the-future mod. Maybe an avalanche simulator?

Layering in displayio

This project is a good example of how multiple TileGrids and Groups can be used to create layered effects and animation. The background image and the snow on the ground are just a single TileGrid that fills the entire display. The flakes are more interesting. Each flake is a TileGrid and is only as big as the flake itself. These are all added to the same Group. All three of these elements, background (TileGrid), snow (TileGrid), and flakes (Group of TileGrids), are then added to the main Group which is shown on the display. As such, the ordering matters in these lines of code:

# Add everything to display
splash = displayio.Group()
splash.append(background)
splash.append(flakes)
splash.append(snow)
display.show(splash)

This guide was first published on Nov 02, 2019. It was last updated on Apr 17, 2024.

This page (More Details) was last updated on Mar 08, 2024.

Text editor powered by tinymce.