import os import time import pygame as pg from pygame import mixer from Blinky import Blinky from Clyde import Clyde from DrawType import DrawType from Inky import Inky from Maze import Maze from Pinky import Pinky from Player import Player from PointGrid import PointGrid from Vec import Vec class Game: def __init__(self): # Variables self.screen_width = self.screen_height = 780 self.cell_count = 30 self.grid_cell_size = 26 self.screen = pg.display.set_mode((self.screen_width, self.screen_height), pg.RESIZABLE + pg.SCALED, vsync=1) self.pause = False self.highscore_out = 0 self.ghosteaten = 0 self.relative_start_time = self.absolute_start_time = time.time() self.delta_start_time = 0 self.clock = pg.time.Clock() # Maze and Points self.maze = Maze(self.grid_cell_size) self.maze.read_file("../Game_Package/maze.pac") self.points = PointGrid(self.grid_cell_size, self.cell_count, self.maze, 3) self.points_to_win = self.calc_points_to_win() # Init Pygame pg.init() pg.display.set_icon(pg.image.load("../Game_Package/PacMan.png")) pg.display.set_caption("PacMan") mixer.init() mixer.music.load("../Game_Package/pacman-background-music.wav") mixer.music.queue("../Game_Package/pacman-background-music.wav", loops=100) mixer.music.set_volume(0.05) mixer.music.play() self.click_sound = pg.mixer.Sound("../Game_Package/click.wav") self.click_sound.set_volume(0.05) # Players and Ghost self.player = Player(Vec(0, 0), Vec(self.grid_cell_size / 2 - 1, self.grid_cell_size / 2 - 1), 2, self.grid_cell_size, self.maze) self.player.auto_place() self.last_time_ghosts_afraid = 0 self.blinky = Blinky(Vec(12 * self.grid_cell_size, 15 * self.grid_cell_size), Vec(self.grid_cell_size, self.grid_cell_size), self.cell_count, self.grid_cell_size, self.player, self.maze.maze, 2) self.pinky = Pinky(Vec(13 * self.grid_cell_size, 15 * self.grid_cell_size), Vec(self.grid_cell_size, self.grid_cell_size), self.cell_count, self.grid_cell_size, self.player, self.maze.maze, 5.5) self.inky = Inky(Vec(14 * self.grid_cell_size, 15 * self.grid_cell_size), Vec(self.grid_cell_size, self.grid_cell_size), self.cell_count, self.grid_cell_size, self.player, self.maze.maze, self.blinky.position, 9) self.clyde = Clyde(Vec(15 * self.grid_cell_size, 15 * self.grid_cell_size), Vec(self.grid_cell_size, self.grid_cell_size), self.cell_count, self.grid_cell_size, self.player, self.maze.maze, 12.5) # Images self.player_image1 = pg.image.load("../Game_Package/PacMan.png") self.player_image2 = pg.image.load("../Game_Package/PacMan2.png") self.blinky_image = pg.image.load("../Game_Package/Blinky.png") self.pinky_image = pg.image.load("../Game_Package/Pinky.png") self.inky_image = pg.image.load("../Game_Package/Inky.png") self.clyde_image = pg.image.load("../Game_Package/Clyde.png") self.ghost_afraid_image = pg.image.load("../Game_Package/GhostAfraid.png") # Scale Images self.player_image1 = pg.transform.scale(self.player_image1, (self.grid_cell_size, self.grid_cell_size)) self.player_image2 = pg.transform.scale(self.player_image2, (self.grid_cell_size, self.grid_cell_size)) self.blinky_image = pg.transform.scale(self.blinky_image, (self.grid_cell_size, self.grid_cell_size)) self.pinky_image = pg.transform.scale(self.pinky_image, (self.grid_cell_size, self.grid_cell_size)) self.inky_image = pg.transform.scale(self.inky_image, (self.grid_cell_size, self.grid_cell_size)) self.clyde_image = pg.transform.scale(self.clyde_image, (self.grid_cell_size, self.grid_cell_size)) self.ghost_afraid_image = pg.transform.scale(self.ghost_afraid_image, (self.grid_cell_size, self.grid_cell_size)) # Call the Game Loop self.game_loop() # Highscore des Spielers wird in der highscore.pac gespeichert bzw. überschrieben def save_highscore(self): if os.path.getsize("highscore.pac") == 0: with open("highscore.pac", "w") as overwrite: overwrite.write(str(int(self.delta_start_time))) self.highscore_out = self.delta_start_time else: with open("highscore.pac", "r") as getScore: if self.delta_start_time < int(getScore.read()): with open("highscore.pac", "w") as overwrite: overwrite.write(str(int(self.delta_start_time))) self.highscore_out = self.delta_start_time # Geister werden gezeichnet, wenn sie nicht gefressen wurden def draw_ghosts(self, afraid): if not self.blinky.eaten: img_blinky = pg.transform.flip(self.ghost_afraid_image if afraid else self.blinky_image, self.blinky.is_mirrored(), False) self.blinky.draw(self.screen, (0, 0, 0), DrawType.image, self.grid_cell_size, img_blinky) if not self.pinky.eaten: img_pinky = pg.transform.flip(self.ghost_afraid_image if afraid else self.pinky_image, self.pinky.is_mirrored(), False) self.pinky.draw(self.screen, (0, 0, 0), DrawType.image, self.grid_cell_size, img_pinky) if not self.inky.eaten: img_inky = pg.transform.flip(self.ghost_afraid_image if afraid else self.inky_image, self.inky.is_mirrored(), False) self.inky.draw(self.screen, (0, 0, 0), DrawType.image, self.grid_cell_size, img_inky) if not self.clyde.eaten: img_clyde = pg.transform.flip(self.ghost_afraid_image if afraid else self.clyde_image, self.clyde.is_mirrored(), False) self.clyde.draw(self.screen, (0, 0, 0), DrawType.image, self.grid_cell_size, img_clyde) # Ausgabe der Punkteanzahl für den Spieler def draw_overlay_points(self): surface = pg.font.SysFont("Comic Sans", 22).render("Punkte: " + str(self.player.score + self.ghosteaten), True, (0, 0, 0)) rect = surface.get_rect() rect.center = (self.screen_width // 2, self.screen_height - 30) self.screen.blit(surface, rect) # Ausgabe der übrigen Lebensanzahl des Spielers def draw_overlay_hp(self): surface = pg.font.SysFont("Comic Sans", 22).render("Übrige Leben: " + str(self.player.hp - 1), True, (0, 0, 0)) rect = surface.get_rect() rect.center = (self.screen_width, self.screen_height - 30) rect.right = (self.screen_width - 10) self.screen.blit(surface, rect) # Berechnung und Ausgabe der Rundenzeit in Minuten und Sekunden def draw_overlay_time(self): delta_time_sec = int(time.time() - self.absolute_start_time) rest = delta_time_sec % 60 delta_time_minute = delta_time_sec - rest surface = pg.font.SysFont("Comic Sans", 22).render(str(delta_time_minute // 60) + " min " + str(rest) + " sek", True, (0, 0, 0)) rect = surface.get_rect() rect.center = (0, self.screen_height - 30) rect.left = 10 self.screen.blit(surface, rect) # Ausgabe des abgespeicherten Highscores def draw_high_score(self): # Wenn die highscore.pac Datei leer ist, wird ein vorgeschriebener Text ausgegeben. if os.path.getsize("../Game_Package/highscore.pac") == 0: text = "Schnellste gewonnene Runde: Keine vorhanden :P" else: # Ansonsten wird die Datei ausgelesen und der Highscore angezeigt. with open("../Game_Package/highscore.pac", "r") as myTest: content = myTest.read() sec = int(content) % 60 minute = int(content) / 60 text = "Schnellste gewonnene Runde: " + str(int(minute)) + " Minuten " + str(sec) + " Sekunden" highscore_screen = pg.font.SysFont("Comic Sans", 22).render(text, True, (0, 0, 0)) highscore_rect = highscore_screen.get_rect() highscore_rect.center = (self.screen_width // 2, self.screen_height // 2 + 80) self.screen.blit(highscore_screen, highscore_rect) def draw_current_score(self): sec = int(self.delta_start_time % 60) minute = int(self.delta_start_time / 60) currentscore_screen = pg.font.SysFont("Comic Sans", 22).render( "Aktuelle Runde: " + str(minute) + " Minuten " + str(sec) + " Sekunden", True, (0, 0, 0)) currentscore_rect = currentscore_screen.get_rect() currentscore_rect.center = (self.screen_width // 2, self.screen_height // 2 + 110) self.screen.blit(currentscore_screen, currentscore_rect) # Alle Attribute des Spielers, der Geister, sowie die Zeit werden zurückgesetzt def restart(self): self.player.hp = 3 self.player.score = 0 self.ghosteaten = 0 self.absolute_start_time = self.relative_start_time = time.time() self.reset_positions() self.blinky.stateChange = self.pinky.stateChange = self.inky.stateChange = self.clyde.stateChange = time.time() self.last_time_ghosts_afraid = 0 self.blinky.chase = self.pinky.chase = self.inky.chase = self.clyde.chase = True self.points = PointGrid(self.grid_cell_size, self.cell_count, self.maze, 3) self.pause = False # Ausgabe des Win-Fensters für den Spieler bei gewonnener Runde def win_screen(self): self.save_highscore() # Highscore wird abgespeichert win_screen = pg.font.SysFont("Comic Sans", 70).render("Gewonnen!", True, (0, 0, 0)) win_rect = win_screen.get_rect() win_rect.center = (self.screen_width // 2, self.screen_height // 2) self.screen.blit(win_screen, win_rect) restart_screen = pg.font.SysFont("Comic Sans", 22).render("Drücke 'R' um noch eine Runde zu spielen!", True, (0, 0, 0)) restart_rect = restart_screen.get_rect() restart_rect.center = (self.screen_width // 2, self.screen_height // 2 + 50) self.screen.blit(restart_screen, restart_rect) self.draw_high_score() self.draw_current_score() keys = pg.key.get_pressed() self.pause = True if keys[pg.K_r]: self.restart() # Ausgabe des Loose-Fensters für den Spieler bei verlorener Runde, Highscore wird hier nicht abgespeichert def loose_screen(self): win_screen = pg.font.SysFont("Comic Sans", 70).render("Game Over!", True, (0, 0, 0)) win_rect = win_screen.get_rect() win_rect.center = (self.screen_width // 2, self.screen_height // 2) self.screen.blit(win_screen, win_rect) restart_screen = pg.font.SysFont("Comic Sans", 22).render("Drücke 'R' um es wieder zu versuchen!", True, (0, 0, 0)) restart_rect = restart_screen.get_rect() restart_rect.center = (self.screen_width // 2, self.screen_height // 2 + 50) self.screen.blit(restart_screen, restart_rect) self.draw_high_score() self.draw_current_score() keys = pg.key.get_pressed() self.pause = True if keys[pg.K_r]: self.restart() def game_loop(self): should_close = False while not should_close: for e in pg.event.get(): if e.type == pg.QUIT: should_close = True # Clear the screen self.screen.fill((255, 255, 255)) # Draw the maze self.maze.draw(self.screen, (200, 200, 200), (150, 150, 150)) # Draw the points self.points.draw(self.screen, (255, 100, 0), DrawType.circle) if not self.pause: self.delta_start_time = time.time() - self.absolute_start_time # Player self.player.poll_keys(self.maze) self.player.move() self.player.check_bounds_move(self.screen_width, self.screen_height) self.player.draw(self.screen, (0, 0, 0), DrawType.image, self.grid_cell_size, pg.transform.rotate( self.player_image1 if self.player.get_state() else self.player_image2, self.player.get_rotation())) self.check_points_possible_to_eat() afraid = time.time() - self.last_time_ghosts_afraid < 8 self.blinky.run(afraid, self.relative_start_time) self.pinky.run(afraid, self.relative_start_time) self.inky.run(afraid, self.relative_start_time) self.clyde.run(afraid, self.relative_start_time) self.draw_ghosts(afraid) self.draw_overlay_points() self.draw_overlay_hp() self.draw_overlay_time() self.check_ghosts_collisions() # Wenn der Spieler mehr oder gleich viele Punkte hat, wie es gezeichnete Punkte gibt, # soll der Win-Screen ausgegeben werden. if self.player.score >= self.points_to_win: self.save_highscore() self.win_screen() # Wenn der Spieler keine Leben mehr hat, wird der loose-Screen angezeigt. elif self.player.hp <= 0: self.loose_screen() pg.display.flip() self.clock.tick(60) self.on_game_exit() # Berechnet die Punkte, die der Spieler benötigt, um das Spiel zu gewinnen. def calc_points_to_win(self): count = 0 # Es wird über das Maze-Array iteriert for i in self.maze.maze: for j in i: # wenn an die Stelle maze[i][j] begehbar, also Null ist, existiert an dieser Stelle ein Punkt, # der gefressen werden muss. if j == 0: count += 1 count -= 4 count *= 10 count += 4 * 50 # Idle-Punkte werden extra berechnet. return count @staticmethod def on_game_exit(): pg.quit() # Punkte die nah genug an Pac Man sind um gefressen zu werden def check_points_possible_to_eat(self): check = Vec(self.player.position.x + self.grid_cell_size / 2, self.player.position.y + self.grid_cell_size / 2) surrounding_points = self.points.get_surrounding_points(check) # Punkte um Pac Man herum werden abgespeichert for point in surrounding_points: # diese werden nun durchiteriert # wenn die Punkte nah genug sind und noch nicht gegessen wurden, wird ein Sound abgespielt # und die Punkte werden gegessen. if check.distance(point.position) < 3: if not point.eaten: pg.mixer.Sound.play(self.click_sound) if point.big: self.player.score += 50 self.last_time_ghosts_afraid = time.time() else: self.player.score += 10 point.eat() # Prüft Kollision zwischen Pac Man und den Geistern im fressbaren Zustand def check_ghosts_collisions(self): global ghosteaten # Wenn Geister im fressbaren Zustand sind, aber ihre Position nicht nah genug an Pac Man dran ist, # passiert nichts. if self.blinky.eaten and self.blinky.position.distance(self.player.position) < 20: pass elif self.pinky.eaten and self.pinky.position.distance(self.player.position) < 20: pass elif self.inky.eaten and self.inky.position.distance(self.player.position) < 20: pass elif self.clyde.eaten and self.clyde.position.distance(self.player.position) < 20: pass elif (self.blinky.position.distance(self.player.position) < 20 or self.pinky.position.distance( self.player.position) < 20 or self.inky.position.distance( self.player.position) < 20 or self.clyde.position.distance(self.player.position) < 20) and \ time.time() - self.player.last_time_hit > 1 and time.time() - self.last_time_ghosts_afraid > 8: self.relative_start_time = time.time() self.player.last_time_hit = time.time() self.player.hp -= 1 self.reset_positions() self.blinky.eaten = False self.pinky.eaten = False self.inky.eaten = False self.clyde.eaten = False # Wenn Geister im fressbaren Zustand sind und ihre Position nah genug an Pac Man ist, # wird ihr Zustand auf gefressen gesetzt und der Spieler bekommt Bonus Punkte elif self.blinky.position.distance(self.player.position) < 20: self.blinky.eaten = True self.ghosteaten = self.ghosteaten + 200 elif self.pinky.position.distance(self.player.position) < 20: self.pinky.eaten = True self.ghosteaten = self.ghosteaten + 200 elif self.inky.position.distance(self.player.position) < 20: self.inky.eaten = True self.ghosteaten = self.ghosteaten + 200 elif self.clyde.position.distance(self.player.position) < 20: self.clyde.eaten = True self.ghosteaten = self.ghosteaten + 200 def reset_positions(self): self.player.auto_place() self.blinky.reset_position() self.pinky.reset_position() self.inky.reset_position() self.clyde.reset_position()