Add RLCD animations and screensavers

This commit is contained in:
aeroreyna
2026-06-21 21:19:51 -04:00
parent edf3da1a30
commit 8dbc5f4c7b
24 changed files with 3097 additions and 0 deletions
+223
View File
@@ -0,0 +1,223 @@
# Standalone MicroPython Screensaver: Paddle Bounce
# Inspired by paddle-ball arcade classic. Exposes run(text="", frames=None).
import time
import math
import random
try:
import board_config
display = getattr(board_config, 'display_instance', None)
except Exception:
board_config = None
display = None
def sleep_ms(ms):
try:
time.sleep_ms(ms)
except AttributeError:
time.sleep(ms / 1000.0)
def check_touch(d, bc):
"""Robust check for screen touch to stop the screensaver."""
if bc is not None:
touch_inst = getattr(bc, 'touch_instance', None)
if touch_inst is not None:
if hasattr(touch_inst, 'is_touched'):
try:
if touch_inst.is_touched():
return True
except Exception:
pass
for meth in ('get_touch', 'read_touch'):
if hasattr(touch_inst, meth):
try:
res = getattr(touch_inst, meth)()
if res is not None and res is not False and res != 0:
return True
except Exception:
pass
touch_val = getattr(bc, 'touch', None)
if touch_val is not None:
if callable(touch_val):
try:
res = touch_val()
if res is not None and res is not False and res != 0:
return True
except Exception:
pass
else:
if hasattr(touch_val, 'is_touched'):
try:
if touch_val.is_touched():
return True
except Exception:
pass
for meth in ('get_touch', 'read_touch'):
if hasattr(touch_val, meth):
try:
res = getattr(touch_val, meth)()
if res is not None and res is not False and res != 0:
return True
except Exception:
pass
if d is not None:
for meth in ('get_touch', 'touch'):
if hasattr(d, meth):
try:
res = getattr(d, meth)()
if res is not None and res is not False and res != 0:
return True
except Exception:
pass
return False
def run(text="", frames=None):
if not display:
print("board_config.display_instance is not available. Skipping screensaver.")
return
width = getattr(display, 'width', 400)
height = getattr(display, 'height', 300)
# Paddle parameters
pad_w = 6
pad_h = 36
left_x = 15
right_x = width - 15 - pad_w
# Initial states
ly = (height - pad_h) // 2
ry = (height - pad_h) // 2
bx, by = width // 2, height // 2
vx, vy = 3.5, 1.8
score_l = 0
score_r = 0
paddle_speed = 3.0
# Simulated tracking error offset
target_offset_l = 0
target_offset_r = 0
def reset_ball(serve_to_left):
nonlocal bx, by, vx, vy, target_offset_l, target_offset_r
bx = width // 2
by = height // 2
vx = -3.5 if serve_to_left else 3.5
vy = random.choice([-2.0, -1.0, 1.0, 2.0])
target_offset_l = random.randint(-12, 12)
target_offset_r = random.randint(-12, 12)
frame = 0
serve_delay = 0
try:
while True:
if frames is not None and frames > 0 and frame >= frames:
break
if check_touch(display, board_config):
print("Screensaver stopped by screen touch.")
break
if serve_delay > 0:
serve_delay -= 1
else:
# Move ball
bx += vx
by += vy
# Ceiling/floor bounce
if by <= 32:
by = 32
vy = -vy
elif by >= height - 8:
by = height - 8
vy = -vy
# AI tracking
if vx < 0: # Ball heading Left
# Track ball center with error offset
target_y = by + 3 - (pad_h // 2) + target_offset_l
if ly < target_y:
ly = min(height - pad_h - 6, ly + paddle_speed)
elif ly > target_y:
ly = max(32, ly - paddle_speed)
else: # Ball heading Right
target_y = by + 3 - (pad_h // 2) + target_offset_r
if ry < target_y:
ry = min(height - pad_h - 6, ry + paddle_speed)
elif ry > target_y:
ry = max(32, ry - paddle_speed)
# Check Left boundary
if bx <= left_x + pad_w:
if left_x <= bx:
# Ball within x-range of paddle, check y-collision
if ly - 3 <= by <= ly + pad_h + 3:
bx = left_x + pad_w
# Bounce & increase speed
vx = -vx * 1.05
# Cap horizontal speed to prevent teleporting
if abs(vx) > 8.0:
vx = 8.0 if vx > 0 else -8.0
vy = vy * 1.05 + random.uniform(-0.6, 0.6)
# Assign new tracking error for right paddle
target_offset_r = random.randint(-15, 15)
else:
# Miss! Point for Right
score_r += 1
reset_ball(serve_to_left=False)
serve_delay = 40
# Check Right boundary
if bx + 6 >= right_x:
if bx <= right_x + pad_w:
# Ball within x-range of paddle, check y-collision
if ry - 3 <= by <= ry + pad_h + 3:
bx = right_x - 6
# Bounce & increase speed
vx = -vx * 1.05
if abs(vx) > 8.0:
vx = 8.0 if vx > 0 else -8.0
vy = vy * 1.05 + random.uniform(-0.6, 0.6)
# Assign new tracking error for left paddle
target_offset_l = random.randint(-15, 15)
else:
# Miss! Point for Left
score_l += 1
reset_ball(serve_to_left=True)
serve_delay = 40
# Draw frame
display.clear(0)
# Header score line
display.text(text if text else "PADDLE BOUNCE", 10, 10, 1)
display.text(f"{score_l:02d} {score_r:02d}", width // 2 - 24, 10, 1)
display.line(0, 26, width, 26, 1)
# Draw center dotted line
for y in range(30, height, 16):
display.fill_rect(width // 2 - 1, y, 2, 8, 1)
# Draw paddles
display.fill_rect(left_x, int(ly), pad_w, pad_h, 1)
display.fill_rect(right_x, int(ry), pad_w, pad_h, 1)
# Draw ball (6x6 square)
if serve_delay == 0 or (serve_delay // 4) % 2 == 0:
display.fill_rect(int(bx), int(by), 6, 6, 1)
display.show()
frame += 1
sleep_ms(30)
except KeyboardInterrupt:
print("Screensaver stopped by user interrupt.")
if __name__ in ('__main__', '__name__'):
run()