Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| lehrkraefte:snr:informatik:glf22:python:snake:ballgame-fehlerhaftes-programm [2023/01/19 20:44] – [Ball game program] Olaf Schnürer | lehrkraefte:snr:informatik:glf22:python:snake:ballgame-fehlerhaftes-programm [2023/01/26 22:45] (current) – Olaf Schnürer | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== Ball game program ====== | ||
| + | |||
| + | <code python ball-game-zu-verbessern.py> | ||
| + | import pygame | ||
| + | from pygame.locals import * | ||
| + | from random import * | ||
| + | |||
| + | # Spielgeschwindigkeit bzw. genauer Frequenz der Spielzyklen am Anfang. | ||
| + | FRAMES_PER_SECOND_AM_ANFANG = 40 | ||
| + | |||
| + | # Das Spielfeld besteht aus kleinen rechteckigen Boxen mit Koordinaten | ||
| + | # 0 bis MAXX (jeweils einschliesslich) in x-Richtung und | ||
| + | # 0 bis MAXY (jeweils einschliesslich) in y-Richtung. | ||
| + | # Achtung: y-Achse zeigt nach unten (wie Zeilennummern) | ||
| + | MAXX = 30 | ||
| + | MAXY = 20 | ||
| + | |||
| + | # Festlegung von Breite und Höhe einer Box in Pixel. | ||
| + | # Meist sind Breite und Höhe gleich, was quadratische Boxen liefert. | ||
| + | SEITENLAENGE_BOX = 25 | ||
| + | |||
| + | # Radius des Balles am Anfang. | ||
| + | RADIUS_BALL_AM_ANFANG = 0.7 | ||
| + | |||
| + | # Anzahl Leben am Anfang. | ||
| + | LEBEN_AM_ANFANG = 10 | ||
| + | |||
| + | # Einige von den obigen Werten abhängige Konstanten. | ||
| + | ANZAHL_BOXEN_X = MAXX + 1 | ||
| + | ANZAHL_BOXEN_Y = MAXY + 1 | ||
| + | |||
| + | FENSTER_BREITE = ANZAHL_BOXEN_X * SEITENLAENGE_BOX | ||
| + | FENSTER_HOEHE = ANZAHL_BOXEN_Y * SEITENLAENGE_BOX | ||
| + | PLATZ_FUER_TEXT = 3 * SEITENLAENGE_BOX | ||
| + | |||
| + | # Farben per Rot-, Grün- und Blauwert | ||
| + | # (jeweils auf Skala von 0 bis 255). | ||
| + | # Wer will, kann hier zusätzliche eigene Farben festlegen. | ||
| + | # | ||
| + | ROT | ||
| + | GRUEN | ||
| + | BLAU = ( 0, 0, 255) | ||
| + | GELB = (255, 255, 0) | ||
| + | MAGENTA = (255, 0, 255) | ||
| + | CYAN = ( 0, 255, 255) | ||
| + | WEISS | ||
| + | SCHWARZ = ( 0, 0, 0) | ||
| + | HELLGRAU = | ||
| + | DUNKELGRAU = (160, 160, 160) | ||
| + | HINTERGRUND_FARBE = SCHWARZ | ||
| + | # HINTERGRUND_FARBE = HELLGRAU | ||
| + | |||
| + | # | ||
| + | # Definition der Klasse Punkt. Bitte einfach akzeptieren. | ||
| + | # | ||
| + | class Punkt: | ||
| + | def __init__(self, | ||
| + | self.x = x | ||
| + | self.y = y | ||
| + | |||
| + | def __str__(self): | ||
| + | return f' | ||
| + | | ||
| + | def __repr__(self): | ||
| + | return f' | ||
| + | | ||
| + | def __eq__(self, | ||
| + | return self.x == other.x and self.y == other.y | ||
| + | |||
| + | def __add__(self, | ||
| + | return Punkt(self.x + other.x, self.y + other.y) | ||
| + | |||
| + | def __sub__(self, | ||
| + | return Punkt(self.x - other.x, self.y - other.y) | ||
| + | |||
| + | def laenge(self): | ||
| + | return (self.x * self.x + self.y * self.y) ** 0.5 | ||
| + | |||
| + | # | ||
| + | # Ende der Definition der Klasse Punkt. | ||
| + | # | ||
| + | |||
| + | # | ||
| + | # Einige Funktionsdefinitionen | ||
| + | # | ||
| + | |||
| + | def zeichne_box(p, | ||
| + | rechteck = pygame.Rect(p.x * SEITENLAENGE_BOX + 1, p.y * SEITENLAENGE_BOX + 1, SEITENLAENGE_BOX - 1, SEITENLAENGE_BOX - 1) | ||
| + | pygame.draw.rect(leinwand, | ||
| + | |||
| + | def zeichne_kreis(p, | ||
| + | pygame.draw.circle(leinwand, | ||
| + | |||
| + | def zeichne_gitter(): | ||
| + | for x in range(ANZAHL_BOXEN_X + 1): | ||
| + | pygame.draw.line(leinwand, | ||
| + | for y in range(ANZAHL_BOXEN_Y + 1): | ||
| + | pygame.draw.line(leinwand, | ||
| + | |||
| + | def schreibe(x, y, text, groesse): | ||
| + | schrift = pygame.font.SysFont(' | ||
| + | formatierter_text = schrift.render(text, | ||
| + | rechteck = formatierter_text.get_rect() | ||
| + | rechteck.center = (round((x + 0.5) * SEITENLAENGE_BOX), | ||
| + | leinwand.blit(formatierter_text, | ||
| + | |||
| + | # Nicht verwendet, aber man könnte damit statt der farbigen Boxen Bilder anzeigen. | ||
| + | def zeige_bild(bild, | ||
| + | rechteck = pygame.Rect(p.x * SEITENLAENGE_BOX + 1, p.y * SEITENLAENGE_BOX + 1, SEITENLAENGE_BOX - 1, SEITENLAENGE_BOX - 1) | ||
| + | leinwand.blit(bild, | ||
| + | |||
| + | def abstand(p, q): | ||
| + | return (p-q).laenge() | ||
| + | # | ||
| + | # Ende des Abschnitts mit den Funktionsdefinitionen. | ||
| + | # | ||
| + | |||
| + | # | ||
| + | # Hier geht das eigentliche Programm los. | ||
| + | # | ||
| + | |||
| + | # Initialisierung des Spielfelds | ||
| + | pygame.init() | ||
| + | uhr = pygame.time.Clock() | ||
| + | leinwand = pygame.display.set_mode((FENSTER_BREITE + 1, FENSTER_HOEHE + 1 + PLATZ_FUER_TEXT)) | ||
| + | pygame.display.set_caption(' | ||
| + | |||
| + | # Initialisierung der Daten des Spielers, insbesondere Startposition | ||
| + | spieler = Punkt(MAXX // 2, MAXY // 2) | ||
| + | farbe_spieler = GRUEN | ||
| + | |||
| + | # Initialisieren der Daten des Balls | ||
| + | radius_ball = RADIUS_BALL_AM_ANFANG | ||
| + | ball = Punkt(radius_ball, | ||
| + | bewegungsrichtung_ball = Punkt(0.1 + 0.4 * random(), randrange(-1, | ||
| + | farbe_ball = MAGENTA | ||
| + | |||
| + | frames_per_second = FRAMES_PER_SECOND_AM_ANFANG | ||
| + | spiel_aktiv = True | ||
| + | leben = LEBEN_AM_ANFANG | ||
| + | |||
| + | # Hier startet die sogenannte "game loop". | ||
| + | while spiel_aktiv: | ||
| + | leinwand.fill(HINTERGRUND_FARBE) | ||
| + | zeichne_gitter() | ||
| + | zeichne_kreis(ball, | ||
| + | # zeichne_box(ball, | ||
| + | zeichne_box(spieler, | ||
| + | schreibe(MAXX // 4, MAXY + 1, f' | ||
| + | schreibe(3 * MAXX // 4, MAXY + 1, f' | ||
| + | schreibe(MAXX // 4, MAXY + 2, f' | ||
| + | schreibe(3 * MAXX // 4, MAXY + 2, f'Ball position: ({ball.x: | ||
| + | schreibe(MAXX // 4, MAXY + 3, f' | ||
| + | schreibe(3 * MAXX // 4, MAXY + 3, f'Ball direction: ({bewegungsrichtung_ball.x: | ||
| + | pygame.display.update() | ||
| + | uhr.tick(frames_per_second) | ||
| + | |||
| + | if leben >= 20 or leben <= 0: | ||
| + | spiel_aktiv = False | ||
| + | ende = ' | ||
| + | |||
| + | if abstand(spieler, | ||
| + | leben = leben + 1 | ||
| + | radius_ball = max(0.3, 0.9 * radius_ball) | ||
| + | # Spieler bekommt neue Position. | ||
| + | spieler.x = 2 * MAXX // 3 | ||
| + | spieler.y = randrange(MAXY + 1) | ||
| + | |||
| + | # Ball bekommt neue Position und Richtung. | ||
| + | ball = Punkt(radius_ball, | ||
| + | bewegungsrichtung_ball = Punkt(0.1 + 0.4 * random(), randrange(-1, | ||
| + | | ||
| + | |||
| + | # Spielerposition bei Tastendruck verändern. | ||
| + | # | ||
| + | # Teilaufgabe 1: Passe die folgenden 21 Zeilen so an, | ||
| + | # dass die Pfeiltasten das grüne Quadrat wie gewünscht steuern. | ||
| + | # | ||
| + | for ereignis in pygame.event.get(): | ||
| + | if ereignis.type == KEYDOWN: | ||
| + | if ereignis.key == K_LEFT: | ||
| + | print(" | ||
| + | spieler.y = spieler.y + 1 | ||
| + | if spieler.y >= MAXY: | ||
| + | spieler.y = 1 | ||
| + | elif ereignis.key == K_RIGHT: | ||
| + | print(" | ||
| + | spieler.x = spieler.x - 1 | ||
| + | if spieler.x < 0: | ||
| + | spieler.x = 3 | ||
| + | elif ereignis.key == K_UP: | ||
| + | print(" | ||
| + | spieler.x = spieler.x + 2 | ||
| + | elif ereignis.key == K_DOWN: | ||
| + | print(" | ||
| + | spieler.y = spieler.y - 1 | ||
| + | if spieler.y < 0: | ||
| + | spieler.y = spieler.x | ||
| + | spieler.x = 0 | ||
| + | elif ereignis.key == K_ESCAPE: | ||
| + | print(" | ||
| + | spiel_aktiv = False | ||
| + | ende = ' | ||
| + | elif ereignis.key == K_RETURN: | ||
| + | print(" | ||
| + | spieler.x = MAXX // 2 | ||
| + | spieler.y = MAXY // 2 | ||
| + | elif ereignis.key == K_q: | ||
| + | print(" | ||
| + | spiel_aktiv = False | ||
| + | ende = ' | ||
| + | elif ereignis.type == QUIT: | ||
| + | print(" | ||
| + | spiel_aktiv = False | ||
| + | ende = ' | ||
| + | |||
| + | # Position des Balls verändern. | ||
| + | ball = ball + bewegungsrichtung_ball | ||
| + | # | ||
| + | # Teilaufgabe 2: Passe die folgenden 10 Zeilen so an, | ||
| + | # dass der Ball rechts, oben und unten normal reflektiert wird. | ||
| + | # | ||
| + | if ball.y - radius_ball < 0: | ||
| + | bewegungsrichtung_ball.y = - bewegungsrichtung_ball.y | ||
| + | bewegungsrichtung_ball.x = - bewegungsrichtung_ball.y | ||
| + | if ball.y + radius_ball > MAXY + 1: | ||
| + | bewegungsrichtung_ball.y = - 0.1 | ||
| + | bewegungsrichtung_ball.x = 0.4 | ||
| + | if ball.x + radius_ball > MAXX + 1: | ||
| + | bewegungsrichtung_ball.x = - bewegungsrichtung_ball.x | ||
| + | # Die folgende Zeile nicht verändern. | ||
| + | leben = leben - 2 | ||
| + | |||
| + | # Ab hier nichts mehr verändern! | ||
| + | if ball.x - radius_ball < 0: | ||
| + | bewegungsrichtung_ball.x = 0.1 + 0.4 * random() | ||
| + | if bewegungsrichtung_ball.y > 0: | ||
| + | bewegungsrichtung_ball.y = 0.1 + 0.4 * random() | ||
| + | else: | ||
| + | bewegungsrichtung_ball.y = -(0.1 + 0.4 * random()) | ||
| + | |||
| + | if ende == ' | ||
| + | if leben >= 20: | ||
| + | schreibe(MAXX // 2, MAXY // 2, ' | ||
| + | else: | ||
| + | schreibe(MAXX // 2, MAXY // 2, 'GAME OVER', 2) | ||
| + | schreibe(MAXX // 2, MAXY // 2 + 3, 'press any key', 1) | ||
| + | pygame.display.update() | ||
| + | pygame.time.delay(500) | ||
| + | pygame.event.clear() | ||
| + | pygame.event.wait() | ||
| + | pygame.quit() | ||
| + | exit() | ||
| + | </ | ||
| + | |||
| + | ===== Link zur Snake-Seite ===== | ||
| + | |||
| + | * [[lehrkraefte: | ||
| + | |||