import pygame from pygame.locals import * from random import * # Geschwindigkeit des Spiels bzw. genauer # Frequenz, mit der das Spielfeld neu gezeichnet wird. FRAMES_PER_SECOND_AM_ANFANG = 7 VERGROESSERUNG_PRO_APFEL = 0.5 # 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 = 24 MAXY = 15 # Festlegung von Breite und Höhe einer Box in Pixel. # Meist sind Breite und Höhe gleich, was quadratische Boxen liefert. SEITENLAENGE_BOX = 40 # Es geht auch mit Bildern (Hintergrund, Apfel, Schlangenkopf) # und Sound (Crash bzw. Game over). # Suche dafür geeignete Dateien und speichere diese am besten # in demselben Verzeichnis wie dieses Programm. # In der Funktion ''lade_bilder_und_sound'' musst du # dann die Namen deiner Dateien (evtl. inklusive Pfad) angeben. BOXEN_STATT_BILDER = True EINFARBIGER_HINTERGRUND = True MIT_SOUND = False # Ab hier Konstanten, die von den obigen abhängen. 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 = 5 * 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 '(' + str(self.x) + ',' + str(self.y) + ')' def __repr__(self): return '(' + str(self.x) + ',' + str(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 __hash__(self): return hash(str(self)) # # Ende der Definition der Klasse Punkt. # 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_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) def schreibe_transparent(x, y, text, groesse): schrift = pygame.font.SysFont('freemono', round(groesse * SEITENLAENGE_BOX)) formatierter_text = schrift.render(text, True, SCHWARZ) rechteck = formatierter_text.get_rect() rechteck.center = (round((x + 0.5) * SEITENLAENGE_BOX), round((y + 0.5) * SEITENLAENGE_BOX)) if rechteck.x >= 0 and rechteck.y >= 0: text_flaeche = pygame.Surface((rechteck.x, rechteck.y)) text_flaeche.fill(WEISS) text_flaeche.blit(formatierter_text, rechteck) text_flaeche.set_alpha(50) leinwand.blit(formatierter_text, rechteck) def neue_apfelposition(): a = Punkt(randrange(0, MAXX + 1), randrange(0, MAXY + 1)) while a in schlange: a = Punkt(randrange(0, MAXX + 1), randrange(0, MAXY + 1)) return a def lade_bilder_und_sound(): global hintergrundbild, bild_kopf_der_schlange, bild_apfel, apfel_ess_sound, crash_sound if not EINFARBIGER_HINTERGRUND: hintergrundbild = pygame.image.load('landschaft.jpg') hintergrundbild = pygame.transform.scale(hintergrundbild, (FENSTER_BREITE, FENSTER_HOEHE)) if not BOXEN_STATT_BILDER: bild_apfel = pygame.image.load('apple.svg') bild_apfel = pygame.transform.scale(bild_apfel, (SEITENLAENGE_BOX, SEITENLAENGE_BOX)) bild_kopf_der_schlange = pygame.image.load('Excited-Smiley-Face.svg') bild_kopf_der_schlange = pygame.transform.scale(bild_kopf_der_schlange, (SEITENLAENGE_BOX, SEITENLAENGE_BOX)) if MIT_SOUND: crash_sound = pygame.mixer.Sound('mixkit-funny-game-lose-tone-2877.wav') apfel_ess_sound = pygame.mixer.Sound('mixkit-video-game-retro-click-237.wav') 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) pygame.init() uhr = pygame.time.Clock() leinwand = pygame.display.set_mode((FENSTER_BREITE + 1, FENSTER_HOEHE + 1 + PLATZ_FUER_TEXT)) pygame.display.set_caption('Snake game') lade_bilder_und_sound() # Initialisiere die Liste, deren Einträge die Koordinaten der Quadrate der Schlange sind. # Der 0-te Eintrag enhält die Position des Kopfes der Schlange. laenge = 4 #I schlange = [Punkt(4, 7), Punkt(3, 7), Punkt(2, 7), Punkt(1, 7)] # Oder besser, da abhängig von der Variablen "länge" schlange = [] for i in range(laenge): schlange.insert(0, Punkt(1 + i, MAXY // 2)) # Und noch besser bzw. kürzer geht das so: # schlange = [Punkt(laenge - i, MAXY // 2) for i in range(laenge)] farbe_schlange = GRUEN # Änderung der Spielerposition pro Spielzyklus: bewegungsrichtung = Punkt(0, 0) # Hilfsvariablen, anfangs am besten ignorieren. neue_richtung = bewegungsrichtung richtung_vor_stopp = Punkt(0, 0) # Initialisiere Apfelposition (nicht auf Schlange!) und Farbe. apfel = neue_apfelposition() farbe_apfel = ROT # Initialisierung der "Spielvariablen" frames_per_second = FRAMES_PER_SECOND_AM_ANFANG spiel_aktiv = True crash = False gefressene_aepfel = 0 # Hier startet die "game loop". while spiel_aktiv: # Graphische Darstellung des Spielgeschehens: # Hintergrund if EINFARBIGER_HINTERGRUND: leinwand.fill(HINTERGRUND_FARBE) else: leinwand.fill(HINTERGRUND_FARBE) leinwand.blit(hintergrundbild, (0, 0)) # Ausgabe diverser Zahlen unter dem Spielfeld schreibe(MAXX // 4, MAXY + 1, 'apple count: ' + str(gefressene_aepfel), 0.8) schreibe(3 * MAXX // 4, MAXY + 1, 'length: ' + str(laenge), 0.8) schreibe(MAXX // 4, MAXY + 2, 'apple: ' + str(apfel), 0.8) schreibe(3 * MAXX // 4, MAXY + 2, 'current length: ' + str(len(set(schlange))), 0.8) schreibe(MAXX // 4, MAXY + 3, 'head of snake: ' + str(schlange[0]), 0.8) schreibe(3 * MAXX // 4, MAXY + 3, 'frames per second: ' + str(frames_per_second), 0.8) text = str(schlange) schreibe(MAXX // 2, MAXY + 4, text, 0.6) schreibe(MAXX // 2, MAXY + 5, 'Space key: pause game', 0.8) # Raster zeichnen zeichne_gitter() # Apfel zeichnen if BOXEN_STATT_BILDER: zeichne_box(apfel, farbe_apfel) schreibe_transparent(apfel.x, apfel.y , str(apfel), 0.25) else: zeige_bild(bild_apfel, apfel) schreibe_transparent(apfel.x, apfel.y , str(apfel), 0.25) # Schlange zeichnen for element in schlange: zeichne_box(element, farbe_schlange) schreibe_transparent(element.x, element.y , str(element), 0.25) if not BOXEN_STATT_BILDER: zeige_bild(bild_kopf_der_schlange, schlange[0]) schreibe_transparent(schlange[0].x, schlange[0].y , str(schlange[0]), 0.25) # Alles bis jetzt "versteckt" Gezeichnete sichtbar machen. pygame.display.update() uhr.tick(frames_per_second) # Verarbeitung von Tastatureingaben: for ereignis in pygame.event.get(): if ereignis.type == QUIT: print("Button zum Schliessen des Pygame-Fensters angeklickt.") spiel_aktiv = False elif ereignis.type == KEYDOWN: if ereignis.key == K_ESCAPE: print("Escape-Taste gedrückt.") spiel_aktiv = False elif ereignis.key == K_q: print("Taste 'q' gedrückt.") spiel_aktiv = False elif ereignis.key == K_SPACE: print("Leer-Taste gedrückt.") if bewegungsrichtung != Punkt(0, 0): richtung_vor_stopp = bewegungsrichtung bewegungsrichtung = Punkt(0, 0) else: bewegungsrichtung =richtung_vor_stopp else: if ereignis.key == K_r: print("Pfeiltaste links gedrückt.") neue_richtung = Punkt(-1, -1) elif ereignis.key == K_f: print("Pfeiltaste rechts gedrückt.") neue_richtung = Punkt(-1, 1) elif ereignis.key == K_t: print("Pfeiltaste hoch gedrückt.") neue_richtung = Punkt(1, -1) elif ereignis.key == K_g: print("Pfeiltaste runter gedrückt.") neue_richtung = Punkt(1, 1) # Die folgende if-Bedingung verhindert, dass die # Snake die Richtung umkehren kann (und mit sich selbst kollidiert). if schlange[1] != schlange[0] + neue_richtung: bewegungsrichtung = neue_richtung # Bewegen der Schlange: if bewegungsrichtung != Punkt(0, 0): # Falls die Schlange die gewünschte Länge hat: # Beseitige das letzte Element aus der Liste "schlange". if len(schlange) == laenge: schlange.pop() # Neues Feld, auf das sich die Schlange bewegt: neue_position = schlange[0] + bewegungsrichtung # Verhalten am Spielfeldrand: if neue_position.x < 0: neue_position.x = MAXX if neue_position.x > MAXX: neue_position.x = 0 if neue_position.y < 0: neue_position.y = MAXY if neue_position.y > MAXY: neue_position.y = 0 # Selbstkollision: if neue_position in schlange: crash = True spiel_aktiv = False if MIT_SOUND: pygame.mixer.Sound.play(crash_sound) else: # Hänge die neue Position vorne an die Liste "schlange". schlange.insert(0, neue_position) # Apfel erreicht? if neue_position == apfel: gefressene_aepfel = 1 if MIT_SOUND: pygame.mixer.Sound.play(apfel_ess_sound) laenge = laenge * 2 frames_per_second = frames_per_second + VERGROESSERUNG_PRO_APFEL apfel = neue_apfelposition() # Ende der game loop. # Nach Abbruch oder Crash: if crash: 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) for ereignis in pygame.event.get(): pygame.time.delay(100) while pygame.event.get() == []: pygame.time.delay(100) pygame.quit() exit()