Skip to content

Commit 6b3ec85

Browse files
authored
Merge pull request #58 from fatfingers23/feature/SpriteExample
MicroPython Example of loading sprites
2 parents 88c1f3b + 88e3ec5 commit 6b3ec85

File tree

6 files changed

+283
-1
lines changed

6 files changed

+283
-1
lines changed

boards/presto/manifest.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
*.af
22
*.png
3+
*.16bpp
34
main.py
45
attitude_indicator.py
6+
awesome_game.py
57
cheerlights_bulb.py
68
cubes.py
79
image_gallery.py

examples/awesome_game.py

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
# ICON [[(-6.09, 20.0), (-7.14, 19.96), (-8.63, 19.78), (-10.29, 19.37), (-11.02, 19.11), (-12.48, 18.43), (-14.22, 17.27), (-15.52, 16.12), (-16.4, 15.14), (-17.1, 14.22), (-18.16, 12.38), (-18.65, 11.2), (-19.11, 9.61), (-19.36, 7.97), (-19.42, 6.62), (-19.38, 5.51), (-19.18, 3.92), (-18.88, 2.66), (-18.41, 1.33), (-17.67, -0.14), (-16.69, -1.58), (-15.84, -2.55), (-14.4, -3.87), (-13.45, -4.56), (-12.16, -5.31), (-10.14, -6.11), (-9.01, -6.39), (-7.41, -6.62), (-5.64, -6.67), (-4.29, -8.95), (-3.72, -9.57), (-3.13, -9.89), (-1.99, -10.1), (-0.79, -9.76), (0.58, -8.98), (1.09, -9.81), (2.19, -10.97), (3.05, -11.48), (4.52, -11.93), (5.79, -12.0), (6.89, -11.81), (7.63, -11.53), (9.64, -10.4), (7.87, -7.33), (6.31, -8.22), (5.76, -8.42), (4.89, -8.34), (4.09, -7.86), (3.64, -7.2), (5.42, -6.18), (6.24, -5.44), (6.6, -4.67), (6.71, -3.51), (6.42, -2.62), (5.2, -0.44), (5.84, 0.67), (6.61, 2.64), (6.97, 4.01), (7.21, 5.68), (7.24, 6.93), (7.07, 8.92), (6.58, 10.95), (6.05, 12.29), (5.46, 13.42), (4.79, 14.43), (3.52, 15.94), (1.73, 17.51), (0.37, 18.39), (-1.78, 19.33), (-3.76, 19.82), (-6.04, 20.0)], [(-6.09, 16.44), (-4.82, 16.37), (-3.91, 16.22), (-2.71, 15.87), (-1.8, 15.49), (-0.58, 14.77), (0.12, 14.22), (1.31, 13.05), (1.98, 12.2), (2.87, 10.64), (3.33, 9.36), (3.66, 7.41), (3.63, 5.64), (3.46, 4.6), (3.06, 3.23), (2.2, 1.46), (1.02, -0.4), (2.89, -3.6), (-1.73, -6.27), (-3.6, -3.07), (-5.56, -3.07), (-6.96, -2.99), (-8.83, -2.61), (-10.73, -1.84), (-12.3, -0.84), (-13.31, 0.04), (-14.04, 0.85), (-14.84, 2.07), (-15.34, 3.18), (-15.77, 4.78), (-15.9, 6.13), (-15.8, 8.14), (-15.36, 9.93), (-14.76, 11.29), (-14.07, 12.38), (-13.1, 13.52), (-12.35, 14.21), (-10.96, 15.18), (-9.77, 15.76), (-8.63, 16.13), (-7.05, 16.4), (-6.13, 16.44)], [(14.09, -4.89), (14.09, -8.44), (19.42, -8.44), (19.42, -4.89), (14.09, -4.89)], [(4.31, -14.67), (4.31, -20.0), (7.87, -20.0), (7.87, -14.67), (4.31, -14.67)], [(12.98, -11.07), (10.49, -13.56), (14.27, -17.33), (16.76, -14.84), (12.98, -11.07)]]
2+
# NAME Awesome Game
3+
# DESC tufty2040 port of awesome_game.py
4+
"""
5+
This is a port of awesome_game.py from the tufty2040 examples
6+
https://github.com/pimoroni/pimoroni-pico/blob/main/micropython/examples/tufty2040/awesome_game.py
7+
"""
8+
9+
import math
10+
from time import sleep
11+
from presto import Presto
12+
import random
13+
import time
14+
15+
# Setup for the Presto display
16+
presto = Presto()
17+
display = presto.display
18+
WIDTH, HEIGHT = display.get_bounds()
19+
20+
# We'll need this for the touch element of the screen
21+
touch = presto.touch
22+
23+
# Load the spreadsheets so we can flip between them
24+
tilemap = bytearray(32_768)
25+
open("s4m_ur4i-pirate-tilemap.16bpp", "rb").readinto(tilemap)
26+
27+
character = bytearray(32_768)
28+
open("s4m_ur4i-pirate-characters.16bpp", "rb").readinto(character)
29+
30+
display.set_spritesheet(character)
31+
32+
class Player():
33+
def __init__(self):
34+
self.reset()
35+
36+
def reset(self):
37+
self.x = 150
38+
self.y = 180
39+
self.w = 15
40+
self.h = 30
41+
self.speed = 5
42+
self.is_alive = True
43+
self.lives = 3
44+
self.score = 0
45+
self.moving = 0
46+
47+
def move(self, x, y):
48+
if self.x + x > 0 - self.w and self.x + x < WIDTH - self.w:
49+
self.x += x
50+
self.y += y
51+
52+
def sprite(self):
53+
display.set_spritesheet(character)
54+
display.sprite(1, 1 if self.moving else 0, self.x, self.y, 4, 0)
55+
56+
57+
class Treasure():
58+
def __init__(self):
59+
self.w = 16
60+
self.h = 16
61+
self.randomize()
62+
63+
def sprite(self):
64+
if not self.enabled:
65+
return
66+
display.set_spritesheet(tilemap)
67+
display.sprite(4, 2, self.x, self.y, 3, 0)
68+
69+
def randomize(self):
70+
self.enabled = True
71+
self.x = random.randint(15, WIDTH - 60)
72+
self.y = HEIGHT - 50
73+
74+
75+
class Block():
76+
def __init__(self):
77+
self.w = 16
78+
self.h = 16
79+
self.is_alive = True
80+
self.randomize()
81+
82+
def move(self):
83+
self.y += self.speed
84+
85+
def sprite(self):
86+
display.set_spritesheet(character)
87+
display.sprite(10, 8, self.x, self.y, 4, 0)
88+
89+
def randomize(self):
90+
self.last_update = time.time()
91+
self.x = random.randint(10, WIDTH - self.w - 10)
92+
self.y = -self.h
93+
# was originally 4-12, but 12 felt too fast
94+
# self.speed = random.randint(4, 12)
95+
self.speed = random.randint(2, 4)
96+
97+
98+
99+
class Game():
100+
def __init__(self, FPS_COUNTER=False):
101+
self.player = Player()
102+
self.block = []
103+
self.last_new_block = 0
104+
105+
self.treasure = Treasure()
106+
self.last_treasure = 0
107+
108+
self.SKY = display.create_pen(72, 180, 224)
109+
#FPS setup
110+
self.show_fps = FPS_COUNTER
111+
self.fps_start_time = time.time()
112+
self.fps_counter = 0
113+
self.current_fps = 0
114+
115+
for _i in range(5):
116+
self.block.append(Block())
117+
118+
def reset(self):
119+
for i in range(7):
120+
presto.set_led_rgb(i, 0, 0, 0)
121+
for block in self.block:
122+
block.randomize()
123+
124+
self.treasure.randomize()
125+
self.player.reset()
126+
if self.show_fps:
127+
self.current_fps = 0
128+
self.fps_counter = 0
129+
self.fps_start_time = time.time()
130+
131+
def get_input(self):
132+
x = touch.x
133+
if x > 120:
134+
self.player.move(self.player.speed, 0)
135+
self.player.moving = 0
136+
if x < 120:
137+
self.player.move(-self.player.speed, 0)
138+
self.player.moving = 1
139+
140+
def background(self):
141+
display.set_spritesheet(tilemap)
142+
display.set_pen(self.SKY)
143+
display.clear()
144+
145+
for i in range(WIDTH / 32):
146+
display.sprite(1, 2, i * 32, 210, 4, 0)
147+
148+
def draw(self):
149+
self.background()
150+
for block in self.block:
151+
block.sprite()
152+
display.set_pen(0xFFFF)
153+
display.text("Score: " + str(self.player.score), 10, 10, 320, 2)
154+
self.treasure.sprite()
155+
display.set_pen(0)
156+
self.player.sprite()
157+
if self.show_fps:
158+
#1 for every second
159+
self.fps_counter += 1
160+
if (time.time() - self.fps_start_time) > 1:
161+
self.current_fps = self.fps_counter / (time.time() - self.fps_start_time)
162+
self.fps_counter = 0
163+
self.fps_start_time = time.time()
164+
display.set_pen(0xFFFF)
165+
display.text("FPS: " + str(math.floor(self.current_fps)), 175, 10, 320, 2)
166+
display.update()
167+
168+
def check_collision(self, a, b):
169+
return a.x + a.w >= b.x and a.x <= b.x + b.w and a.y + a.h >= b.y and a.y <= b.y + b.h
170+
171+
def update(self):
172+
for block in self.block:
173+
block.move()
174+
if block.y > HEIGHT:
175+
block.randomize()
176+
177+
if block.y + block.h >= self.player.y and self.check_collision(self.player, block):
178+
block.randomize()
179+
self.player.is_alive = False
180+
181+
if self.treasure.enabled:
182+
if self.check_collision(self.player, self.treasure):
183+
self.player.score += 1
184+
if 0 < self.player.score < 8:
185+
for i in range(self.player.score):
186+
# turn a light gold
187+
presto.set_led_rgb(i, 255, 215, 0)
188+
self.treasure.enabled = False
189+
self.last_treasure = time.time()
190+
191+
if time.time() - self.last_treasure > 2:
192+
if not self.treasure.enabled:
193+
self.treasure.randomize()
194+
195+
if self.player.lives == 0:
196+
self.player.is_alive = False
197+
198+
199+
200+
game = Game(FPS_COUNTER=True)
201+
while True:
202+
203+
touch.poll()
204+
game.background()
205+
display.set_pen(0xFFFF)
206+
display.text("ARGH!", 40, 35, 200, 5)
207+
display.text("Touch screen to Start", 80, 150, 180, 1)
208+
display.update()
209+
210+
while not touch.state:
211+
presto.update()
212+
213+
while game.player.is_alive:
214+
if touch.state:
215+
game.get_input()
216+
game.update()
217+
game.draw()
218+
presto.update()
219+
220+
game.background()
221+
display.set_pen(0xFFFF)
222+
display.text("OOPS!", 40, 35, 200, 5)
223+
display.text("Your score: " + str(game.player.score), 50, 150, 180, 2)
224+
display.update()
225+
presto.update()
226+
#Added a because if you are still touching the screen may breeze pass the scoring screen
227+
sleep(.5)
228+
# See if the display is still being touched after the sleep
229+
touch.poll()
230+
while not touch.state:
231+
presto.update()
232+
233+
game.reset()
234+
presto.update()
32 KB
Binary file not shown.
32 KB
Binary file not shown.

modules/py_frozen/presto.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,4 @@ def partial_update(self, x, y, w, h):
9292

9393
def clear(self):
9494
self.display.clear()
95-
self.presto.update(self.display)
95+
self.presto.update(self.display)

tools/convert-image-rgb565.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/bin/env python3
2+
from PIL import Image
3+
import numpy
4+
import sys
5+
import pathlib
6+
7+
# This scripts takes and converts an image to a format you can load with the PicoGraphics sprite methods
8+
# https://github.com/pimoroni/pimoroni-pico/blob/main/micropython/modules/picographics/README.md#sprites
9+
# The sprite sheets displays and loads best if your image is 128x128 with each sprite being 8x8
10+
# An example can be seen in the assets of 32blint-games repo
11+
# https://github.com/mikerr/32blit-games/tree/37daeaacdb661e2f9a47e3682859234b9b849226/sprite-browser/assets
12+
13+
# Run with `./filename.py source-image.jpg`
14+
IMAGE_PATH = pathlib.Path(sys.argv[1])
15+
OUTPUT_PATH = IMAGE_PATH.with_suffix(".16bpp")
16+
17+
18+
def image_to_data(image):
19+
"""Generator function to convert a PIL image to 16-bit 565 RGB bytes."""
20+
# Convert the image to RGB (ignoring alpha if present)
21+
image = image.convert('RGB')
22+
23+
# Convert the image to a NumPy array
24+
pb = numpy.array(image).astype('uint16')
25+
26+
# Extract the RGB channels
27+
r = (pb[:, :, 0] >> 3) & 0x1F # 5 bits for red
28+
g = (pb[:, :, 1] >> 2) & 0x3F # 6 bits for green
29+
b = (pb[:, :, 2] >> 3) & 0x1F # 5 bits for blue
30+
31+
# Pack the RGB channels into a 16-bit 565 format
32+
color = (r << 11) | (g << 5) | b
33+
34+
# Flatten the array and convert to bytes
35+
return color.byteswap().tobytes()
36+
37+
img = Image.open(IMAGE_PATH)
38+
w, h = img.size
39+
data = image_to_data(img)
40+
41+
print(f"Converted: {w}x{h} {len(data)} bytes")
42+
43+
with open(OUTPUT_PATH, "wb") as f:
44+
f.write(data)
45+
46+
print(f"Written to: {OUTPUT_PATH}")

0 commit comments

Comments
 (0)