Bevor es losgehen kann brauchen Sie einige Dateien. Es sind dies die Bibliotheken für die CircuitPython-Version (10.x), die beiden
Hintergrundbilder (320x240 Pixel) und die Textdatei für den Score. Laden Sie einfach die zip-Datei
von hier herunter,
entpacken sie und kopieren die Ordner 'images', 'lib' und 'txt' ins Hauptverzeichnis des Laufwerks 'CIRCUITPY'.
Los gehts:
Im Unterschied zum oben gezeigten 'Tetris' bietet es sich beim 'Flipper'-Spiel an, das Display im Hochformat zu bezutzen, also so:
Dadurch verändert sich die Bezeichnung (nicht die GPIO's) der Button. Aus 'rechts' wird 'up', aus 'up' wird 'left' u.s.w.
Ich zeige dazu den Quellcode zu Definition der Module (1 -21), Initialisierung des Displays (29 - 41) und der Button (43 - 56):
# SPDX-FileCopyrightText : 2026 Detlef Gebhardt, written for CircuitPython 10.0.3
# SPDX-FileCopyrightText : Copyright (c) 2026 Detlef Gebhardt
# SPDX-Filename : Flipper-Spiel
# SPDX-License-Identifier: https://dgebhardt.de
import time
import board
import busio
import displayio
import terminalio
import digitalio
import fourwire
import bitmaptools
from adafruit_display_text import label
from adafruit_display_shapes.circle import Circle
from adafruit_display_shapes.roundrect import RoundRect
from adafruit_st7789 import ST7789
import adafruit_imageload
import random
import math
import pwmio
import microcontroller
# Anzahl Kugeln
kugeln = 6
# speaker aktivieren
speak = pwmio.PWMOut(board.GP15, frequency=4000, variable_frequency=True)
# 2 Zoll 320x240 Waveshare Display
cs=board.GP21
dc=board.GP17
reset=board.GP20
bl=board.GP16
# Release any resources currently in use for the displays
displayio.release_displays()
spi = busio.SPI(board.GP18, board.GP19)
display_bus = fourwire.FourWire(spi, command=dc, chip_select=cs, reset=reset)
display = ST7789(display_bus, rotation=0, width=240, height=320, backlight_pin=bl, rowstart=0, colstart=0)
display.brightness = 1
main = displayio.Group()
display.root_group = main
key_A = digitalio.DigitalInOut(board.GP7)
key_A.switch_to_input(pull=digitalio.Pull.UP)
key_B = digitalio.DigitalInOut(board.GP6)
key_B.switch_to_input(pull=digitalio.Pull.UP)
keyUp = digitalio.DigitalInOut(board.GP2)
keyUp.switch_to_input(pull=digitalio.Pull.UP)
keyLeft = digitalio.DigitalInOut(board.GP4)
keyLeft.switch_to_input(pull=digitalio.Pull.UP)
keyRight = digitalio.DigitalInOut(board.GP5)
keyRight.switch_to_input(pull=digitalio.Pull.UP)
key_X = digitalio.DigitalInOut(board.GP9)
key_X.switch_to_input(pull=digitalio.Pull.UP)
key_Y = digitalio.DigitalInOut(board.GP8)
key_Y.switch_to_input(pull=digitalio.Pull.UP)
Hinweisen möchte ich auch auf Zeile 24. Hier wird die Variable für die Anzahl der zu spielenden Kugeln festgelegt. In Zeile
27 wird der Lautsprecher aktiviert (GPIO 15). Im Spiel gibt es später die Möglichkeit, diesen über die X- oder Y-Taste
ein- bzw. auszuschalten.
Als nächstes werden der in einer Textdatei gespeicherte 'Highscore' gelesen, der Hintergrund, die beweglichen Flipper (als 'Sprites')
und die Hindernisse (obstacles) dargestellt.
Die Zeilen 63 bis 77 bilden ein 'Intro' zur Anzeige des Highscore und die 'while'-Schleife wird erst abgebrochen,
wenn eine der Tasten A, B, X oder Y gedrückt wurde. Um den belegten Speicher zu 'entlasten', wird in Zeile 75 und 76
das angezeigte Label und das Hintergrundbild aus dem Speicher gelöscht und erst danach die 'weiteren Teile' geladen.
Danach werden noch Label für entsprechende Anzeigen im Spiel definiert und alle 'Komponenten' für das Spiel dem
Speicher zugewiesen.
Es folgen Funktionen für den Abschuss einer Kugel, Initialisierung der Sprites, das Abspielen eines Tons, das Registrieren einer Kollision
und Spielende, sowie die Festlegung von Variablen. Darauf wird hier jetzt nicht im Detail eingegangen.
Den 'Rest' erledigt die 'while'-Schleife ab Zeile 287. Klicken Sie auf 'Code kopieren', um den vollständigen Quellcode
in die Thonny-IDE zu übertragen. Die Auslassungen hier sind nur dem großen Umfang und der Übersicht geschuldet.
Einen Hinweis möchte ich noch einmal geben. Wenn der Ton eingeschaltet ist, werden vor Spielstart, bei jeder Kollision und bei
Spielende Töne abgespielt. Das kann auf Dauer etwas nervig sein. Deshalb kann mit der X-Taste der Ton abgestellt werden. Um
dennoch eine gewisse 'Anspannung' zu schaffen, wird bei abgeschaltetem Ton die Bildschirmhelligkeit in sehr schneller Folge
verändert. Es handelt sich also nicht um einen 'Wackelkontakt' am Display (siehe z.B. Zeilen 288 bis 296).
while True:
# Effekt vor Spielbeginn
if kugeln == 6 and (time.monotonic() - last_tone) >= 4 and not sound:
last_tone = time.monotonic()
for i in range (5):
display.brightness = 1
time.sleep(0.05)
display.brightness = 0.05
time.sleep(0.05)
display.brightness = 1
# Ton vor Spielbeginn
if kugeln == 6 and (time.monotonic() - last_tone) >= 4 and sound:
last_tone = time.monotonic()
tone = 0.25
for f in (3300, 3490, 3920, 4400, 4940):
i = random.randint(1,256)
speak.frequency = f
speak.duty_cycle = 10000
time.sleep(tone)
tone -= 0.04
speak.duty_cycle = 0
time.sleep(0.03)
speak.duty_cycle = 0
# Sound ausschalten
if key_X.value == False:
sound = False
# Sound einschalten
if key_Y.value == False:
sound = True
# rechten Flipper bewegen
if keyRight.value == False and not debounce:
debounce = True
hit = time.monotonic()
flipperr[0] = 6
if y_ball >= 222 and y_ball <= 233 and x_ball >= 125 and x_ball <= 145:
rolldown = False
hitback = True
ballspeed = -2
i = random.randint(-1,1)
if i == 1:
direction[0] = 1
else:
direction[0] = -1
hit = time.monotonic()
# linken Flipper bewegen
if keyLeft.value == False and not debounce:
debounce = True
hit = time.monotonic()
flipperl[0] = 3
if y_ball >= 228 and y_ball <= 240 and x_ball >= 85 and x_ball <= 105:
rolldown = False
hitback = True
ballspeed = -2
i = random.randint(-1,1)
if i == -1:
direction[0] = -1
else:
direction[0] = 1
hit = time.monotonic()
# 'UP' druecken zum Abschuss
if keyUp.value == False and kugeln > 0 and launch == True:
label.text = ""
debounce = True
rolldown = True
# Anzahl Restkugeln verringern
kugeln -= 1
text_balls.text = str(kugeln)
updating_score.text = "{:06}".format(points)
hit = time.monotonic()
launch = ball_launch(launch, ball)
x_ball = ball[0]
y_ball = ball[1]
rolldown = True
#
# bonus_hole
#
if (x_ball-120)*(x_ball-120) + (y_ball-210)*(y_ball-210) <= 200 and hitback ==False:
bonus_hole.fill = 0xffffff
circle_ball.fill = None
label.text = "Bonus Ball"
time.sleep(1)
bonus_hole.fill = 0xaf3232
label.text = ""
x_ball = 120
y_ball = 270
circle_ball.x = 200
circle_ball.y = 260
ball[0] = 205
ball[1] = 270
circle_ball.fill = 0xffff00
kugeln += 1
text_balls.text = str(kugeln)
launch = True
rolldown = False
ballspeed = 0
#
# Ball rollt nach unten
#
if rolldown == True and (time.monotonic() - roll) > (0.1 + ballspeed):
roll = time.monotonic()
y_ball += 5
ballspeed -= 0.002
circle_ball.y = y_ball
points = collision(x_ball, y_ball, points)
if x_ball >= 20 and x_ball <= 100:
y1 = 0.575 * x_ball + 180
if y_ball >= y1:
x_ball += 5
y_ball = int(0.575 * x_ball + 180)
circle_ball.x = x_ball
circle_ball.y = y_ball
if x_ball >= 130 and x_ball <= 216:
y2 = -0.535 * x_ball + 300
if y_ball >= y2:
x_ball -= 5
y_ball = int(-0.535 * x_ball + 300)
circle_ball.x = x_ball
circle_ball.y = y_ball
# Kugel rollt unten durch
if y_ball >= 270:
circle_ball.y = 340
if sound:
for f in range(4000, 2900, -30):
speak.frequency = f
speak.duty_cycle = 5000
time.sleep(0.01)
time.sleep(0.2)
speak.duty_cycle = 0
# kurze Pause vor nächstem Schuss
time.sleep(0.7)
# Game over
if kugeln == 0:
game_over(key_A, points, score)
circle_ball.x = 200
circle_ball.y = 260
ball[0] = 205
ball[1] = 270
launch = True
rolldown = False
ballspeed = 0
#
# Ball wird vom Flipper zurueckgeschossen
#
if hitback == True and (time.monotonic() - flip) > (ballspeed):
flip = time.monotonic()
ballspeed += 0.02
x_ball -= 5 * direction[0]
y_ball -= 4
if x_ball >= 190 and y_ball > 104:
direction[0] = 1
if x_ball <= 40 and y_ball > 104:
direction[0] = -1
if y_ball <= int(104 - math.sqrt(9604 - (x_ball-112)*(x_ball-112))):
hitback = False
rolldown = True
roll = time.monotonic()
ballspeed = 0
points = collision(x_ball, y_ball, points)
circle_ball.x = x_ball
circle_ball.y = y_ball
# Flipper zurücksetzen
if time.monotonic() - hit > 0.1:
debounce = False
flipperl[0] = 1
flipperr[0] = 4
287 while True:
288 # Effekt vor Spielbeginn
289 if kugeln == 6 and (time.monotonic() - last_tone) >= 4 and not sound:
290 last_tone = time.monotonic()
291 for i in range (5):
292 display.brightness = 1
293 time.sleep(0.05)
294 display.brightness = 0.05
295 time.sleep(0.05)
296 display.brightness = 1
.....
318 # rechten Flipper bewegen
319 if keyRight.value == False and not debounce:
320 debounce = True
321 hit = time.monotonic()
322 flipperr[0] = 6
323 if y_ball >= 222 and y_ball <= 233 and x_ball >= 125 and x_ball <= 145:
324 rolldown = False
325 hitback = True
326 ballspeed = -2
327 i = random.randint(-1,1)
328 if i == 1:
329 direction[0] = 1
330 else:
331 direction[0] = -1
332 hit = time.monotonic()
.....
452 # Flipper zurücksetzen
453 if time.monotonic() - hit > 0.1:
454 debounce = False
455 flipperl[0] = 1
456 flipperr[0] = 4