import pygame from random import randint from datetime import datetime OBJ_OVER = "GameOver" OBJ_BALL = "Ball" OBJ_PADDLE = "Paddle" OBJ_COUNTER = "Counter" OBJ_HIGHSCORE = "Highscore" ############################################################################### class GameObject(): """ Oberklasse für alle Objekte im Spiel Jedes Objekt sollte sich selbst aktualisieren und darstellen können """ def update(self, game_objects): pass def draw(self, window): pass def signal_is_over(self, game_objects): pass ############################################################################### class Window(GameObject): """ Das eigentliche Fenster für das Spiel Stellt den schwarzen Hintergrund dar und wird daher als erstes gezeichnet Steuert auch die Spielgeschwindigkeit """ def __init__(self): self.width = 640 self.height = 480 self.title = "Pong" pygame.init() pygame.display.set_caption(self.title) self.size = (self.width, self.height) self.screen = pygame.display.set_mode(self.size) self.clock = pygame.time.Clock() def update(self, game_objects): self.clock.tick(50) def draw(self, window=None): black = (0, 0, 0) self.screen.fill(black) ############################################################################### class GameOverLabel(GameObject): """ Boolsches "Objekt", das festhält, ob das Spiel verloren wurde Stellt in diesem Fall die Game Over Schrift bereit """ def __init__(self, window): font = pygame.font.Font(None, 36) self.image = font.render('Game Over', 1, (255, 255, 255)) self.rect = self.image.get_rect(centerx=window.width // 2, centery=window.height // 2) self.is_over = False def signal_is_over(self, game_objects): self.is_over = True def draw(self, window): if self.is_over: window.screen.blit(self.image, self.rect) ############################################################################### class Counter(GameObject): """ Zähler """ def __init__(self, window): self.window = window self.counter = 0 def increment(self): self.counter += 10 def draw(self, window): font = pygame.font.Font(None, 36) self.image = font.render(f'{self.counter}', 1, (255, 255, 255)) self.rect = self.image.get_rect(right=window.width-10 , top=10) window.screen.blit(self.image, self.rect) ############################################################################### class Highscore(GameObject): """ Verwaltung der Highscore-Liste """ def __init__(self, window): self.window = window self.visible = False self.font = pygame.font.Font(None, 24) self.scores = {} self.last_score = None ### TODO: Hier müssen die Highscores eingelesen werden def signal_is_over(self, game_objects): self.last_score = game_objects[OBJ_COUNTER].counter self.scores[self.last_score] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") ### TODO: Hier müssen die Highscores gesichert werden self.visible = True def draw(self, window): if self.visible: sorted_scores = sorted(self.scores.keys()) sorted_scores.reverse() line_no = 1 for index, key in enumerate(sorted_scores): if index < 4 or key == self.last_score: color = (255, 0, 0) if key == self.last_score else (255, 255, 255) image = self.font.render(f'{key} Punkte am {self.scores[key]}', 1, color) rect = image.get_rect(centerx=window.width // 2, top=window.height // 2 + line_no * 30) line_no += 1 window.screen.blit(image, rect) ############################################################################### class BouncingBall(GameObject): """ Das wandernde Ohm-Zeichen Abprallen wird im Rahmen der Aktualisierung berechnet Darstellung nur, wenn nicht gerade "Game Over angezeigt wird" """ def __init__(self, window): self.window = window self.image = pygame.image.load('A9/ohm.png') self.rect = self.image.get_rect() self.rect.left = window.width // 2 - self.rect.width // 2 self.rect.top = window.height // 2 - self.rect.height // 2 maxspeed = 10 self.dx = randint(2, maxspeed) self.dy = randint(2, maxspeed) def update(self, game_objects): self.is_visible = not game_objects[OBJ_OVER].is_over if self.is_visible: self.rect.left += self.dx self.rect.top += self.dy self.bounce(game_objects) def bounce(self, game_objects): if self.rect.left < 0 or self.rect.right > self.window.width: self.dx *= -1 has_hit = self.hit(game_objects[OBJ_PADDLE]) if has_hit: game_objects[OBJ_COUNTER].increment() if self.rect.top < 0 or has_hit: self.dy *= -1 if self.rect.bottom > self.window.height: if not game_objects[OBJ_OVER].is_over: for obj in game_objects.values(): obj.signal_is_over(game_objects) def hit(self, paddle): if self.dy < 0: return False return paddle.rect.colliderect(self.rect) def draw(self, window): if self.is_visible: window.screen.blit(self.image, self.rect) ############################################################################### class Paddle(GameObject): """ Der Schläger Darstellung nur, wenn nicht gerade "Game Over angezeigt wird" """ def __init__(self, window): self.width = 100 self.height = 10 x = window.width // 2 - self.width // 2 y = window.height - 2 * self.height self.rect = pygame.Rect(x, y, self.width, self.height) self.color = (255, 0, 0), # rot self.speed = 10 self.dx = 0 self.dy = 0 def update(self, game_objects): self.is_visible = not game_objects[OBJ_OVER].is_over if self.is_visible: self.rect.left += self.dx self.rect.top += self.dy def draw(self, window): if self.is_visible: pygame.draw.rect(window.screen, self.color, self.rect) ############################################################################### def main(): """ Main-Funktion mit Game-Loop Hostet die beiden zentralen Variablen "window" und "game_objects" """ window = Window() game_objects = dict() init(window, game_objects) running = True while running: running = user_input(window, game_objects) update(window, game_objects) draw(window, game_objects) def init(window, game_objects): """ Anlegen aller Objekte des Spiels """ game_objects[OBJ_OVER] = GameOverLabel(window) game_objects[OBJ_BALL] = BouncingBall(window) game_objects[OBJ_PADDLE] = Paddle(window) game_objects[OBJ_COUNTER] = Counter(window) game_objects[OBJ_HIGHSCORE] = Highscore(window) def user_input(window, game_objects): """ Abarbeiten aller Events :return: False, falls das Spiel beendet wird, sonst Truepong-oo.py """ def process_event(): """ Verarbeitung eines Events """ if event.type == pygame.QUIT: return False if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: return False if event.key == pygame.K_SPACE and game_objects[OBJ_OVER].is_over: init(window, game_objects) if event.key == pygame.K_LEFT: game_objects[OBJ_PADDLE].dx -= game_objects[OBJ_PADDLE].speed if event.key == pygame.K_RIGHT: game_objects[OBJ_PADDLE].dx += game_objects[OBJ_PADDLE].speed if event.type == pygame.KEYUP: if event.key == pygame.K_LEFT: game_objects[OBJ_PADDLE].dx += game_objects[OBJ_PADDLE].speed if event.key == pygame.K_RIGHT: game_objects[OBJ_PADDLE].dx -= game_objects[OBJ_PADDLE].speed return True result = True for event in pygame.event.get(): result = result and process_event() return result def update(window, game_objects): """ Aktualisierung aller Objekt """ window.update(game_objects) for o in game_objects.values(): o.update(game_objects) def draw(window, game_objects): """ Zeichnen aller Objekte """ window.draw() for o in game_objects.values(): o.draw(window) pygame.display.flip() if __name__ == "__main__": main()