lehrkraefte:snr:informatik:glf22:python:snake:ballgame-fehlerhaftes-programm

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
lehrkraefte:snr:informatik:glf22:python:snake:ballgame-fehlerhaftes-programm [2023/01/20 11:05] Olaf Schnürerlehrkraefte: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  grün  blau
 +ROT        (255,    0,    0)
 +GRUEN      (  0,  255,    0)
 +BLAU    =    (  0,    0,  255)
 +GELB    =    (255,  255,    0)
 +MAGENTA =    (255,    0,  255)
 +CYAN    =    (  0,  255,  255)
 +WEISS      (255,  255,  255)
 +SCHWARZ =    (  0,    0,    0)
 +HELLGRAU =   (80,  80,  80)
 +DUNKELGRAU = (160,  160,  160)
 +HINTERGRUND_FARBE = SCHWARZ
 +# HINTERGRUND_FARBE = HELLGRAU
 +
 +#
 +# Definition der Klasse Punkt. Bitte einfach akzeptieren.
 +#
 +class Punkt:
 +    def __init__(self, x, y):
 +        self.x = x
 +        self.y = y
 +
 +    def __str__(self):
 +        return f'({self.x}, {self.y})'
 +    
 +    def __repr__(self):
 +        return f'({self.x}, {self.y})'
 +    
 +    def __eq__(self, other):
 +        return self.x == other.x and self.y == other.y
 +
 +    def __add__(self, other):
 +        return Punkt(self.x + other.x, self.y + other.y)
 +
 +    def __sub__(self, other):
 +        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, farbe):
 +    rechteck = pygame.Rect(p.x * SEITENLAENGE_BOX + 1, p.y * SEITENLAENGE_BOX + 1, SEITENLAENGE_BOX - 1, SEITENLAENGE_BOX - 1)
 +    pygame.draw.rect(leinwand, farbe, rechteck)
 +
 +def zeichne_kreis(p, r, farbe):    
 +    pygame.draw.circle(leinwand, farbe, ((p.x + 0.0) * SEITENLAENGE_BOX + 1, (p.y + 0.0) * SEITENLAENGE_BOX + 1), r * SEITENLAENGE_BOX, width=0)
 +
 +def zeichne_gitter():
 +    for x in range(ANZAHL_BOXEN_X + 1):
 +        pygame.draw.line(leinwand, HELLGRAU, (x * SEITENLAENGE_BOX, 0), (x * SEITENLAENGE_BOX, FENSTER_HOEHE))
 +    for y in range(ANZAHL_BOXEN_Y + 1):
 +        pygame.draw.line(leinwand, HELLGRAU, (0, y * SEITENLAENGE_BOX), (FENSTER_BREITE, y * SEITENLAENGE_BOX))
 +
 +def schreibe(x, y, text, groesse):
 +    schrift = pygame.font.SysFont('freemono', round(groesse * SEITENLAENGE_BOX))
 +    formatierter_text = schrift.render(text, True, WEISS, SCHWARZ)
 +    rechteck = formatierter_text.get_rect()
 +    rechteck.center = (round((x + 0.5) * SEITENLAENGE_BOX), round((y + 0.5) * SEITENLAENGE_BOX))
 +    leinwand.blit(formatierter_text, rechteck)
 +
 +# Nicht verwendet, aber man könnte damit statt der farbigen Boxen Bilder anzeigen.
 +def zeige_bild(bild, p):
 +    rechteck = pygame.Rect(p.x * SEITENLAENGE_BOX + 1, p.y * SEITENLAENGE_BOX + 1, SEITENLAENGE_BOX - 1, SEITENLAENGE_BOX - 1)
 +    leinwand.blit(bild, rechteck)
 +
 +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('My favorite game')
 +
 +# 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, randrange(MAXY // 4, 3 * MAXY // 4))
 +bewegungsrichtung_ball = Punkt(0.1 + 0.4 * random(), randrange(-1, 2, 2) * 0.1 + 0.4 * random())
 +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, radius_ball, farbe_ball)
 +    # zeichne_box(ball, farbe_ball)
 +    zeichne_box(spieler, farbe_spieler)
 +    schreibe(MAXX // 4, MAXY + 1, f'Lives: {leben}', 0.7)
 +    schreibe(3 * MAXX // 4, MAXY + 1, f'Update frequenqy: {frames_per_second}', 0.7)
 +    schreibe(MAXX // 4, MAXY + 2, f'Player position: {spieler}', 0.7)
 +    schreibe(3 * MAXX // 4, MAXY + 2, f'Ball position: ({ball.x:.1f}, {ball.y:.1f})',0.7)
 +    schreibe(MAXX // 4, MAXY + 3, f'Enter key moves player to center', 0.7)
 +    schreibe(3 * MAXX // 4, MAXY + 3, f'Ball direction: ({bewegungsrichtung_ball.x:.1f}, {bewegungsrichtung_ball.y:.1f})',0.7)
 +    pygame.display.update()
 +    uhr.tick(frames_per_second)
 +
 +    if leben >= 20 or leben <= 0:
 +        spiel_aktiv = False
 +        ende = 'gewonnen oder verloren'
 +
 +    if abstand(spieler, ball) < radius_ball + 0.9:
 +        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, randrange(MAXY // 4, 3 * MAXY // 4))
 +        bewegungsrichtung_ball = Punkt(0.1 + 0.4 * random(), randrange(-1, 2, 2) * 0.1 + 0.4 * random())
 +        
 +
 +    # 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("Pfeiltaste ... gedrückt.")
 +                spieler.y = spieler.y + 1
 +                if spieler.y >= MAXY:
 +                    spieler.y = 1
 +            elif ereignis.key == K_RIGHT:
 +                print("Pfeiltaste ... gedrückt.")
 +                spieler.x = spieler.x - 1
 +                if spieler.x < 0:
 +                    spieler.x = 3
 +            elif ereignis.key == K_UP:
 +                print("Pfeiltaste hoch gedrückt.")
 +                spieler.x = spieler.x + 2
 +            elif ereignis.key == K_DOWN:
 +                print("Pfeiltaste runter gedrückt.")
 +                spieler.y = spieler.y - 1
 +                if spieler.y < 0:
 +                    spieler.y = spieler.x
 +                    spieler.x = 0
 +            elif ereignis.key == K_ESCAPE:
 +                print("Escape-Taste gedrückt.")
 +                spiel_aktiv = False
 +                ende = 'Abbruch'
 +            elif ereignis.key == K_RETURN:
 +                print("Enter/Return-Taste gedrückt.")
 +                spieler.x = MAXX // 2
 +                spieler.y = MAXY // 2
 +            elif ereignis.key == K_q:
 +                print("Taste 'q' gedrückt.")
 +                spiel_aktiv = False
 +                ende = 'Abbruch'
 +        elif ereignis.type == QUIT:
 +            print("Button zum Schliessen des Pygame-Fensters angeklickt.")
 +            spiel_aktiv = False
 +            ende = 'Abbruch'
 +
 +    # 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 == 'gewonnen oder verloren':
 +    if leben >= 20:
 +        schreibe(MAXX // 2, MAXY // 2, 'Congratulations!', 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()
 +</code>
 +
 +===== Link zur Snake-Seite =====
 +
 +  * [[lehrkraefte:snr:informatik:glf22:python:snake|Snake]]
 +