Add RLCD animations and screensavers
This commit is contained in:
@@ -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()
|
||||
Reference in New Issue
Block a user