kopia lustrzana https://github.com/russhughes/st7789py_mpy
178 wiersze
5.8 KiB
Python
178 wiersze
5.8 KiB
Python
'''
|
|
toasters.py - Flying Toasters(ish) an ESP-32 and ST7789 240x320 display.
|
|
|
|
Uses spritesheet from CircuitPython_Flying_Toasters pendant project
|
|
https://learn.adafruit.com/circuitpython-sprite-animation-pendant-mario-clouds-flying-toasters
|
|
|
|
Convert spritesheet bmp to tft.bitmap() method compatible python module using:
|
|
python3 ./sprites2bitmap.py toasters.bmp 64 64 4 > toast_bitmaps.py
|
|
|
|
'''
|
|
|
|
import gc
|
|
import time
|
|
import random
|
|
from machine import Pin, SPI
|
|
import st7789
|
|
import 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 postion 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 spi interface
|
|
spi = SPI(1, baudrate=31250000, sck=Pin(18), mosi=Pin(19))
|
|
|
|
# configure display
|
|
tft = st7789.ST7789(
|
|
spi,
|
|
240,
|
|
320,
|
|
reset=Pin(4, Pin.OUT),
|
|
cs=Pin(13, Pin.OUT),
|
|
dc=Pin(12, Pin.OUT),
|
|
backlight=Pin(15, Pin.OUT),
|
|
rotation=1,
|
|
buffer_size=64*62*2)
|
|
|
|
# init and clear screen
|
|
tft.init()
|
|
tft.fill(st7789.BLACK)
|
|
|
|
# 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.01)
|
|
|
|
main()
|