OK, let's get fancy with those flakes. Allowing for custom flakes is a perfect use of creating a "sprite sheet" to hold the flake shapes, as discussed here.
This comes at the cost of requiring you to actually create this sprite sheet. You'll also need to modify a few lines of code to provide some info about the sprite sheet layout. But with this feature, along with a settable background image, you can really have fun customizing your snow globe. We'll provide a few examples to get you started.
Here's the fancy version of the snow globe code:
# SPDX-FileCopyrightText: 2019 Carter Nelson for Adafruit Industries # # SPDX-License-Identifier: MIT from random import randrange import board import busio import displayio from adafruit_gizmo import tft_gizmo import adafruit_imageload import adafruit_lis3dh #---| User Config |--------------- BACKGROUND = "/blinka_dark.bmp" # specify color or background BMP file NUM_FLAKES = 50 # total number of snowflakes FLAKE_SHEET = "/flakes_sheet.bmp" # flake sprite sheet FLAKE_WIDTH = 4 # sprite width FLAKE_HEIGHT = 4 # sprite height FLAKE_TRAN_COLOR = 0x000000 # transparency color SNOW_COLOR = 0xFFFFFF # snow color SHAKE_THRESHOLD = 27 # shake sensitivity, lower=more sensitive #---| User Config |--------------- # Accelerometer setup accelo_i2c = busio.I2C(board.ACCELEROMETER_SCL, board.ACCELEROMETER_SDA) accelo = adafruit_lis3dh.LIS3DH_I2C(accelo_i2c, address=0x19) # Create the TFT Gizmo display display = tft_gizmo.TFT_Gizmo() # Load background image try: bg_bitmap, bg_palette = adafruit_imageload.load(BACKGROUND, bitmap=displayio.Bitmap, palette=displayio.Palette) # Or just use solid color except (OSError, TypeError, AttributeError): BACKGROUND = BACKGROUND if isinstance(BACKGROUND, int) else 0x000000 bg_bitmap = displayio.Bitmap(display.width, display.height, 1) bg_palette = displayio.Palette(1) bg_palette[0] = BACKGROUND background = displayio.TileGrid(bg_bitmap, pixel_shader=bg_palette) # Snowflake setup flake_bitmap, flake_palette = adafruit_imageload.load(FLAKE_SHEET, bitmap=displayio.Bitmap, palette=displayio.Palette) if FLAKE_TRAN_COLOR is not None: for i, color in enumerate(flake_palette): if color == FLAKE_TRAN_COLOR: flake_palette.make_transparent(i) break NUM_SPRITES = flake_bitmap.width // FLAKE_WIDTH * flake_bitmap.height // FLAKE_HEIGHT flake_pos = [0.0] * NUM_FLAKES flakes = displayio.Group() for _ in range(NUM_FLAKES): flakes.append(displayio.TileGrid(flake_bitmap, pixel_shader=flake_palette, width = 1, height = 1, tile_width = FLAKE_WIDTH, tile_height = FLAKE_HEIGHT, x = randrange(0, display.width), default_tile=randrange(0, NUM_SPRITES))) # Snowfield setup snow_depth = [display.height] * display.width snow_palette = displayio.Palette(2) snow_palette[0] = 0xADAF00 # transparent color snow_palette[1] = SNOW_COLOR # snow color snow_palette.make_transparent(0) snow_bitmap = displayio.Bitmap(display.width, display.height, len(snow_palette)) snow = displayio.TileGrid(snow_bitmap, pixel_shader=snow_palette) # Add everything to display splash = displayio.Group() splash.append(background) splash.append(flakes) splash.append(snow) display.root_group = splash def clear_the_snow(): #pylint: disable=global-statement, redefined-outer-name global flakes, flake_pos, snow_depth display.auto_refresh = False for flake in flakes: # set to a random sprite flake[0] = randrange(0, NUM_SPRITES) # set to a random x location flake.x = randrange(0, display.width) # set random y locations, off screen to start flake_pos = [-1.0*randrange(0, display.height) for _ in range(NUM_FLAKES)] # reset snow level snow_depth = [display.height] * display.width # and snow bitmap for i in range(display.width*display.height): snow_bitmap[i] = 0 display.auto_refresh = True def add_snow(index, amount, steepness=2): location = [] # local steepness check for x in range(index - amount, index + amount): add = False if x == 0: # check depth to right if snow_depth[x+1] - snow_depth[x] < steepness: add = True elif x == display.width - 1: # check depth to left if snow_depth[x-1] - snow_depth[x] < steepness: add = True elif 0 < x < display.width - 1: # check depth to left AND right if snow_depth[x-1] - snow_depth[x] < steepness and \ snow_depth[x+1] - snow_depth[x] < steepness: add = True if add: location.append(x) # add where snow is not too steep for x in location: new_level = snow_depth[x] - 1 if new_level >= 0: snow_depth[x] = new_level snow_bitmap[x, new_level] = 1 while True: clear_the_snow() # loop until globe is full of snow while snow_depth.count(0) < display.width: # check for shake if accelo.shake(SHAKE_THRESHOLD, 5, 0): break # update snowflakes for i, flake in enumerate(flakes): # speed based on sprite index flake_pos[i] += 1 - flake[0] / NUM_SPRITES # check if snowflake has hit the ground if flake_pos[i] >= snow_depth[flake.x]: # add snow where it fell add_snow(flake.x, FLAKE_WIDTH) # reset flake to top flake_pos[i] = 0 # at a new x location flake.x = randrange(0, display.width) flake.y = int(flake_pos[i]) display.refresh()
As you can see, there are more items in the customization section:
#---| User Config |--------------- BACKGROUND = "/blinka_dark.bmp" # specify color or background BMP file NUM_FLAKES = 50 # total number of snowflakes FLAKE_SHEET = "/flakes_sheet.bmp" # flake sprite sheet FLAKE_WIDTH = 4 # sprite width FLAKE_HEIGHT = 4 # sprite height FLAKE_TRAN_COLOR = 0x000000 # transparency color SNOW_COLOR = 0xFFFFFF # snow color SHAKE_THRESHOLD = 27 # shake sensitivity, lower=more sensitive #---| User Config |---------------
Most of these are the same as the simple version in the previous section. The new ones are for specifying a flake sprite sheet. They are:
-
FLAKE_SHEET
- the BMP file containing the flake sprites -
FLAKE_WIDTH
- the width of each sprite -
FLAKE_HEIGHT
- the height of each sprite -
FLAKE_TRAN_COLOR
- the color to use as transparency
For FLAKE_TRAN_COLOR
, it's best to just pick something simple when creating your sprite sheet bitmap. Don't use one of the colors in the flake itself, since then it wouldn't show up.
The two that are probably the most confusing are FLAKE_WIDTH
and FLAKE_HEIGHT
. You can think of these as the width and height of each of your flakes. These should be the same for each flake. It's not the total width and height of your flake sheet - just the sprites themselves. For a more in depth discussion see here and here.
Background Image
To start out, the code is reusing the background image from the previous section of the guide. Download it from the link there. Be sure to have the file blinka_dark.bmp in your CIRCUITPY folder.
Simple Snowglobe Redux
Let's start by simply recreating the same flakes from the previous section, but this time by using a sprite sheet. Here's the file:
Download that and copy it to your CIRCUITPY folder.
If you open it in an image viewer, you won't see much since it's so small. But zoomed in, it looks something like this:
The border and grids have been added for reference. They aren't part of the actual file. You can see how there are 3 different 4x4 bitmaps. We need to tell the code this, which is what these lines do:
FLAKE_WIDTH = 4 # sprite width FLAKE_HEIGHT = 4 # sprite height
With the background (blinka_dark.bmp) and flake sprite sheet (flake_sheet.bmp) files copied to your CIRCUITPY folder, you can try running the code above. You should get the same as before - a Blinka background with simple white snow flakes.
Now let's try something different.
Who Watches The...Squids?
This example better shows how you can customize the snow globe. Hmmm. What else can we make fall from the sky. How about squids? Sure. And with a background that matches the reference. Grab these two files:
and save to your CIRCUITPY folder.
Then, change the customization settings to this:
#---| User Config |--------------- BACKGROUND = "/watchmen_bg.bmp" # specify color or background BMP file NUM_FLAKES = 20 # total number of snowflakes FLAKE_SHEET = "/squid_sheet_16.bmp" # flake sprite sheet FLAKE_WIDTH = 16 # sprite width FLAKE_HEIGHT = 16 # sprite height FLAKE_TRAN_COLOR = 0x000000 # transparency color SNOW_COLOR = 0x279ED5 # snow color SHAKE_THRESHOLD = 27 # shake sensitivity, lower=more sensitive #---| User Config |---------------
The background image is just another BMP file. Other than that, nothing new.
The sprite sheet is pretty different though. The flakes are larger - 16 x 16. They also contain multiple colors. Zoomed in, it looks something like this:
The total number of flakes is reduced a bit, since these are larger flakes. And to make the snow match the squids, it is set to a blue-ish color.
With everything copied over and the other changes in place, run the code again and you should get:
Falling squids!
Have Fun!
Have fun with this flake customization feature. Like maybe have cat and dog flakes? Or maybe frogs? Falling hearts might be nice. Or just fancier snow flakes. Up to you.
What else can fall from the sky? Here's one more for you. But you'll have to load these up to see...
#---| User Config |--------------- BACKGROUND = "/wg_bg.bmp" # specify color or background BMP file NUM_FLAKES = 20 # total number of snowflakes FLAKE_SHEET = "/wg_sheet.bmp" # flake sprite sheet FLAKE_WIDTH = 20 # sprite width FLAKE_HEIGHT = 20 # sprite height FLAKE_TRAN_COLOR = 0x000000 # transparency color SNOW_COLOR = 0xFF00FF # snow color SHAKE_THRESHOLD = 27 # shake sensitivity, lower=more sensitive #---| User Config |---------------
Page last edited January 21, 2025
Text editor powered by tinymce.