kopia lustrzana https://github.com/russhughes/st7789py_mpy
209 wiersze
6.1 KiB
Python
209 wiersze
6.1 KiB
Python
"""
|
|
tiny_toasters.py
|
|
================
|
|
|
|
.. figure:: ../_static/tiny_toasters.jpg
|
|
:align: center
|
|
|
|
Test sprites_converter.
|
|
|
|
Tiny Flying Toasters for smaller displays using a converted BMP spritesheet module using:
|
|
|
|
.. code-block:: console
|
|
|
|
`sprites_converter.py ttoasters.bmp 32 32 4 > ttoast_bitmaps.py`
|
|
|
|
.. rubric:: The tiny_toasters.py example uses the spritesheet from CircuitPython_Flying_Toasters pendant
|
|
project https://learn.adafruit.com/circuitpython-sprite-animation-pendant-mario-clouds-flying-toasters
|
|
|
|
|
|
.. note:: This example requires the following modules:
|
|
|
|
.. hlist::
|
|
:columns: 3
|
|
|
|
- `st7789py`
|
|
- `tft_config`
|
|
- `tiny_toasters`
|
|
|
|
"""
|
|
|
|
import gc
|
|
import time
|
|
import random
|
|
import st7789py as st7789
|
|
import tft_config
|
|
import tiny_toasters_bitmaps as toast_bitmaps
|
|
|
|
TOASTER_FRAMES = [0, 1, 2, 3]
|
|
TOAST_FRAMES = [4]
|
|
|
|
|
|
def collide(a_col, a_row, a_width, a_height, b_col, b_row, b_width, b_height):
|
|
"""return true if two rectangles overlap"""
|
|
return (
|
|
a_col + a_width >= b_col
|
|
and a_col <= b_col + b_width
|
|
and a_row + a_height >= b_row
|
|
and a_row <= b_row + b_height
|
|
)
|
|
|
|
|
|
def random_start(tft, sprites, bitmaps, num):
|
|
"""
|
|
Return a random location along the top or right of the screen, if that location would overlaps
|
|
with another sprite return (0,0). This allows the other sprites to keep moving giving the next
|
|
random_start a better chance to avoid a collision.
|
|
|
|
"""
|
|
# 50/50 chance to try along the top/right half or along the right/top half of the screen
|
|
if random.getrandbits(1):
|
|
row = 1
|
|
col = random.randint(bitmaps.WIDTH // 2, tft.width - bitmaps.WIDTH)
|
|
else:
|
|
col = tft.width - bitmaps.WIDTH
|
|
row = random.randint(1, tft.height // 2)
|
|
|
|
if any(
|
|
collide(
|
|
col,
|
|
row,
|
|
bitmaps.WIDTH,
|
|
bitmaps.HEIGHT,
|
|
sprite.col,
|
|
sprite.row,
|
|
sprite.width,
|
|
sprite.height,
|
|
)
|
|
for sprite in sprites
|
|
if num != sprite.num
|
|
):
|
|
col = 0
|
|
row = 0
|
|
|
|
return (col, row)
|
|
|
|
|
|
def main():
|
|
class Toast:
|
|
"""
|
|
Toast class to keep track of toaster and toast sprites
|
|
"""
|
|
|
|
def __init__(self, sprites, bitmaps, frames):
|
|
"""create new sprite in random location that does not overlap other sprites"""
|
|
self.num = len(sprites)
|
|
self.bitmaps = bitmaps
|
|
self.frames = frames
|
|
self.steps = len(frames)
|
|
self.col, self.row = random_start(tft, sprites, bitmaps, self.num)
|
|
self.width = bitmaps.WIDTH
|
|
self.height = bitmaps.HEIGHT
|
|
self.last_col = self.col
|
|
self.last_row = self.row
|
|
self.step = random.randint(0, self.steps)
|
|
self.dir_col = -random.randint(2, 5)
|
|
self.dir_row = 2
|
|
self.prev_dir_col = self.dir_col
|
|
self.prev_dir_row = self.dir_row
|
|
self.iceberg = 0
|
|
|
|
def clear(self):
|
|
"""clear above and behind sprite"""
|
|
tft.fill_rect(
|
|
self.col, self.row - 1, self.width, self.dir_row + 1, st7789.BLACK
|
|
)
|
|
|
|
tft.fill_rect(
|
|
self.col + self.width + self.dir_col,
|
|
self.row,
|
|
-self.dir_col,
|
|
self.height,
|
|
st7789.BLACK,
|
|
)
|
|
|
|
def erase(self):
|
|
"""erase last position of sprite"""
|
|
tft.fill_rect(
|
|
self.last_col, self.last_row, self.width, self.height, st7789.BLACK
|
|
)
|
|
|
|
def move(self, sprites):
|
|
"""step frame and move sprite"""
|
|
|
|
if self.steps:
|
|
self.step = (self.step + 1) % self.steps
|
|
|
|
self.last_col = self.col
|
|
self.last_row = self.row
|
|
new_col = self.col + self.dir_col
|
|
new_row = self.row + self.dir_row
|
|
|
|
# if new location collides with another sprite, change direction for 32 frames
|
|
|
|
for sprite in sprites:
|
|
if (
|
|
self.num != sprite.num
|
|
and collide(
|
|
new_col,
|
|
new_row,
|
|
self.width,
|
|
self.height,
|
|
sprite.col,
|
|
sprite.row,
|
|
sprite.width,
|
|
sprite.height,
|
|
)
|
|
and (self.col > sprite.col)
|
|
):
|
|
self.iceberg = 32
|
|
self.dir_col = -1
|
|
self.dir_row = 3
|
|
new_col = self.col + self.dir_col
|
|
new_row = self.row + self.dir_row
|
|
|
|
self.col = new_col
|
|
self.row = new_row
|
|
|
|
# if new location touches edge of screen, erase then set new start location
|
|
if self.col <= 0 or self.row > tft.height - self.height:
|
|
self.erase()
|
|
self.dir_col = -random.randint(2, 5)
|
|
self.dir_row = 2
|
|
self.col, self.row = random_start(tft, sprites, self.bitmaps, self.num)
|
|
|
|
# Track post collision direction change
|
|
if self.iceberg:
|
|
self.iceberg -= 1
|
|
if self.iceberg == 1:
|
|
self.dir_col = self.prev_dir_col
|
|
self.dir_row = self.prev_dir_row
|
|
|
|
def draw(self):
|
|
"""if the location is not 0,0 draw current frame of sprite at it's location"""
|
|
if self.col and self.row:
|
|
tft.bitmap(self.bitmaps, self.col, self.row, self.frames[self.step])
|
|
|
|
# configure display driver
|
|
tft = tft_config.config(tft_config.WIDE)
|
|
|
|
# create toast spites and set animation frames
|
|
sprites = []
|
|
sprites.append(Toast(sprites, toast_bitmaps, TOAST_FRAMES))
|
|
sprites.append(Toast(sprites, toast_bitmaps, TOASTER_FRAMES))
|
|
sprites.append(Toast(sprites, toast_bitmaps, TOASTER_FRAMES))
|
|
|
|
# move and draw sprites
|
|
|
|
while True:
|
|
for sprite in sprites:
|
|
sprite.clear()
|
|
sprite.move(sprites)
|
|
sprite.draw()
|
|
|
|
gc.collect()
|
|
time.sleep(0.05)
|
|
|
|
|
|
main()
|