diff --git a/Funktional/Undo_Doodle.py b/Funktional/Undo_Doodle.py index 9f00c8a..7bbbb8c 100644 --- a/Funktional/Undo_Doodle.py +++ b/Funktional/Undo_Doodle.py @@ -2,88 +2,133 @@ import pygame class Game: + def __init__(self): + # Initialisiert alle Module von Pygame pygame.init() + + # Erstellt das Hauptfenster mit einer Auflösung von 800x600 Pixeln self.screen = pygame.display.set_mode((800, 600)) + + # Setzt den Fenstertitel pygame.display.set_caption("Undo Doodle") + + # Erstellt eine Clock, um die Bildwiederholrate (Framerate) zu steuern self.clock = pygame.time.Clock() - # Der Zustand ist eine Liste von Strichen. - # Jeder Strich ist eine Liste von Punkten (tuples). + # Initialisierung des Zustands: + # Der Zustand ist eine Liste von Strichen. Jeder Strich ist eine Liste von Punkten (Tuples). initial_state = [] - # Die Historie speichert die aufeinanderfolgenden, immutablen Zustände + # Die Historie speichert die aufeinanderfolgenden, unveränderbaren (immutablen) Zustände. + # Zu Beginn enthält sie nur den leeren Anfangszustand. self.history = [initial_state] + # Steuerungs-Flags für den Zeichenvorgang self.drawing = False self.current_stroke = [] @property def current_state(self): - """Liefert den aktuellen Zustand (oberstes Element der Historie).""" + """Der @property-Decorator sorgt dafür, dass diese Methode wie ein Attribut aufgerufen wird (ohne Klammern). + + Sie liefert immer den aktuellen Zustand zurück – das ist das jeweils letzte Element am Ende der Historie. + Für den Code, der die Klasse von außen benutzt, sieht es so aus, als würde man einfach nur einen Wert ablesen. + Im Hintergrund kann die Methode aber komplizierte Berechnungen anstellen, Werte prüfen oder Daten umwandeln, + ohne dass der Aufrufer das merken muss. + """ return self.history[-1] def run(self): + # Die Hauptschleife des Spiels (Game Loop) running = True while running: + # 1. Eingaben verarbeiten (Events) running = self.handle_events() + # 2. Spiel-Logik aktualisieren (hier nicht nötig, aber im Loop enthalten) self.update() + # 3. Alles auf den Bildschirm zeichnen self.draw() + # Begrenzt das Spiel auf 60 Bilder pro Sekunde self.clock.tick(60) + + pygame.quit() def handle_events(self): + # Fängt alle Benutzer-Eingaben (Maus, Tastatur, Fenster schließen) ab for event in pygame.event.get(): + # Fall A: Benutzer schließt das Fenster if event.type == pygame.QUIT: return False + # Fall B: Eine Taste wird gedrückt elif event.type == pygame.KEYDOWN: - # Mit 'u' oder Strg+Z einen Schritt zurückgehen (Undo) + # Prüft, ob 'u' oder Strg + 'Z' gedrückt wurde (Undo-Befehl) if event.key == pygame.K_u or (event.key == pygame.K_z and (pygame.key.get_mods() & pygame.KMOD_CTRL)): + # Wir können nur Undo machen, wenn noch mehr als der Urzustand in der Historie ist if len(self.history) > 1: + # Entfernt den obersten/neuesten Zustand aus der Historie -> springt zum vorherigen zurück self.history.pop() print("Undo ausgeführt!") + # Fall C: Maustaste wird heruntergedrückt (Zeichnen starten) elif event.type == pygame.MOUSEBUTTONDOWN: - if event.button == 1: # Linke Maustaste gedrückt + if event.button == 1: # Linke Maustaste self.drawing = True + # Startet einen neuen Strich und fügt direkt den ersten Punkt (Mausposition) hinzu self.current_stroke = [event.pos] + # Fall D: Maus wird bei gedrückter Taste bewegt (Linie ziehen) elif event.type == pygame.MOUSEMOTION: if self.drawing: + # Fügt den aktuellen Punkt zur Liste des aktuellen Strichs hinzu self.current_stroke.append(event.pos) + # Fall E: Maustaste wird losgelassen (Strich beenden) elif event.type == pygame.MOUSEBUTTONUP: if event.button == 1 and self.drawing: self.drawing = False if self.current_stroke: - # IMMUTABLE ANSATZ: - # Erstelle einen völlig neuen Zustand (neue Liste), - # anstatt den alten Zustand direkt zu verändern. + # IMMUTABLE ANSATZ IN AKTION: + # Statt den alten Zustand direkt zu verändern (z.B. mit .append()), + # erstellen wir mit `self.current_state + [self.current_stroke]` + # eine völlig neue Liste (ein neues Objekt). new_strokes = self.current_state + [self.current_stroke] + + # Dieser neue Zustand wird als nächster Schritt an die Historie angehängt. self.history.append(new_strokes) + + # Setzt den temporären Strich für den nächsten Zug zurück self.current_stroke = [] return True def update(self): + # Platzhalter für Logik-Updates pass def draw(self): - self.screen.fill((255, 255, 255)) # Hintergrund weiß füllen + # Füllt den gesamten Bildschirm mit Weiß + self.screen.fill((255, 255, 255)) - # 1. Zeichne alle fertigen Striche aus dem aktuellen Zustand + # 1. Zeichnet alle bereits fertigen Striche aus dem aktuellen Zustand + # Dank @property greifen wir hier ganz einfach ohne Klammern auf `self.current_state` zu. for stroke in self.current_state: if len(stroke) > 1: + # Zeichnet eine glatte Linie über alle Punkte des Striches (schwarz, Stärke 3) pygame.draw.lines(self.screen, (0, 0, 0), False, stroke, 3) - # 2. Zeichne den aktuellen Strich während des Ziehens in einer Vorschau-Farbe + # 2. Zeichnet den aktuellen Strich in Echtzeit als Vorschau während des Ziehens if self.drawing and len(self.current_stroke) > 1: + # Die Vorschau wird in einem helleren Grau angezeigt pygame.draw.lines(self.screen, (120, 120, 120), False, self.current_stroke, 3) + # Bringt die gezeichnete Leinwand gebündelt auf den Bildschirm (verhindert Flackern) pygame.display.flip() if __name__ == "__main__": + # Startet das Spiel, wenn die Datei direkt ausgeführt wird game = Game() game.run() \ No newline at end of file