Added Soundeffects

This commit is contained in:
2025-04-25 16:23:43 +02:00
parent 15635d7f96
commit c046995fdc
7 changed files with 123 additions and 112 deletions

BIN
_start.mp3 Normal file

Binary file not shown.

BIN
fail.mp3 Normal file

Binary file not shown.

59
game.py
View File

@@ -6,6 +6,7 @@ import os
import threading import threading
import time import time
import serial.tools.list_ports # Import hinzugefügt import serial.tools.list_ports # Import hinzugefügt
import pygame
class TimeGuessGame: class TimeGuessGame:
def __init__(self, root): def __init__(self, root):
@@ -31,13 +32,14 @@ class TimeGuessGame:
self.canvas_frame_id = None # Hinzugefügt: ID für das Canvas-Fenster self.canvas_frame_id = None # Hinzugefügt: ID für das Canvas-Fenster
self.create_ui() self.create_ui()
pygame.mixer.init()
# Verbindung nicht automatisch beim Start herstellen # Verbindung nicht automatisch beim Start herstellen
# Serielle Kommunikation wird in setup_connection gestartet # Serielle Kommunikation wird in setup_connection gestartet
# Protokoll für das Schließen des Fensters # Protokoll für das Schließen des Fensters
self.root.protocol("WM_DELETE_WINDOW", self.on_closing) self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
def create_ui(self): def create_ui(self):
# ... (Restlicher Code von create_ui bleibt gleich bis zum Ranglisten-Teil) ... # ... (Restlicher Code von create_ui bleibt gleich bis zum Ranglisten-Teil) ...
@@ -105,7 +107,6 @@ class TimeGuessGame:
bg="#9E9E9E", fg="white") bg="#9E9E9E", fg="white")
self.skip_button.pack(side=tk.LEFT, padx=5) self.skip_button.pack(side=tk.LEFT, padx=5)
# --- Frame für Rangliste (Container) --- # --- Frame für Rangliste (Container) ---
leaderboard_outer_frame = tk.Frame(self.root, padx=10, pady=10) leaderboard_outer_frame = tk.Frame(self.root, padx=10, pady=10)
leaderboard_outer_frame.pack(fill=tk.BOTH, expand=True) # Füllt restlichen Platz leaderboard_outer_frame.pack(fill=tk.BOTH, expand=True) # Füllt restlichen Platz
@@ -114,7 +115,8 @@ class TimeGuessGame:
# --- Canvas und Scrollbar für die Rangliste --- # --- Canvas und Scrollbar für die Rangliste ---
self.leaderboard_canvas = tk.Canvas(leaderboard_outer_frame) self.leaderboard_canvas = tk.Canvas(leaderboard_outer_frame)
self.leaderboard_scrollbar = tk.Scrollbar(leaderboard_outer_frame, orient=tk.VERTICAL, command=self.leaderboard_canvas.yview) self.leaderboard_scrollbar = tk.Scrollbar(leaderboard_outer_frame, orient=tk.VERTICAL,
command=self.leaderboard_canvas.yview)
self.scrollable_leaderboard_frame = tk.Frame(self.leaderboard_canvas) # Frame *innerhalb* des Canvas self.scrollable_leaderboard_frame = tk.Frame(self.leaderboard_canvas) # Frame *innerhalb* des Canvas
# --- Änderung 1: ID speichern und Bindung an Canvas --- # --- Änderung 1: ID speichern und Bindung an Canvas ---
@@ -163,7 +165,6 @@ class TimeGuessGame:
self.leaderboard_canvas.configure(scrollregion=self.leaderboard_canvas.bbox("all")) self.leaderboard_canvas.configure(scrollregion=self.leaderboard_canvas.bbox("all"))
# --- Ende Änderung 2 --- # --- Ende Änderung 2 ---
def update_leaderboard_display(self): def update_leaderboard_display(self):
# Bestehende Einträge im scrollbaren Frame löschen # Bestehende Einträge im scrollbaren Frame löschen
for widget in self.scrollable_leaderboard_frame.winfo_children(): for widget in self.scrollable_leaderboard_frame.winfo_children():
@@ -196,25 +197,30 @@ class TimeGuessGame:
data_font = ("Arial", 10) # Kleinere Schrift für Daten data_font = ("Arial", 10) # Kleinere Schrift für Daten
# Rang # Rang
lbl_rank = tk.Label(self.scrollable_leaderboard_frame, text=str(i + 1), bg=bg_color, padx=5, pady=2, font=data_font) lbl_rank = tk.Label(self.scrollable_leaderboard_frame, text=str(i + 1), bg=bg_color, padx=5, pady=2,
font=data_font)
lbl_rank.grid(row=row_num, column=0, sticky='nsew') lbl_rank.grid(row=row_num, column=0, sticky='nsew')
# Name # Name
lbl_name = tk.Label(self.scrollable_leaderboard_frame, text=entry['name'], bg=bg_color, anchor='w', padx=5, pady=2, font=data_font) lbl_name = tk.Label(self.scrollable_leaderboard_frame, text=entry['name'], bg=bg_color, anchor='w', padx=5,
pady=2, font=data_font)
lbl_name.grid(row=row_num, column=1, sticky='nsew') lbl_name.grid(row=row_num, column=1, sticky='nsew')
# Zielzeit # Zielzeit
lbl_target = tk.Label(self.scrollable_leaderboard_frame, text=f"{entry['target_time'] / 1000:.3f}", bg=bg_color, padx=5, pady=2, font=data_font) lbl_target = tk.Label(self.scrollable_leaderboard_frame, text=f"{entry['target_time'] / 1000:.3f}",
bg=bg_color, padx=5, pady=2, font=data_font)
lbl_target.grid(row=row_num, column=2, sticky='nsew') lbl_target.grid(row=row_num, column=2, sticky='nsew')
# Gemessene Zeit # Gemessene Zeit
lbl_elapsed = tk.Label(self.scrollable_leaderboard_frame, text=f"{entry['elapsed_time'] / 1000:.3f}", bg=bg_color, padx=5, pady=2, font=data_font) lbl_elapsed = tk.Label(self.scrollable_leaderboard_frame, text=f"{entry['elapsed_time'] / 1000:.3f}",
bg=bg_color, padx=5, pady=2, font=data_font)
lbl_elapsed.grid(row=row_num, column=3, sticky='nsew') lbl_elapsed.grid(row=row_num, column=3, sticky='nsew')
# Abweichung # Abweichung
deviation = abs(entry['deviation'] / 1000) deviation = abs(entry['deviation'] / 1000)
color = "green" if deviation < 0.5 else "orange" if deviation < 1.0 else "red" color = "green" if deviation < 0.5 else "orange" if deviation < 1.0 else "red"
lbl_dev = tk.Label(self.scrollable_leaderboard_frame, text=f"{deviation:.3f}", fg=color, bg=bg_color, padx=5, pady=2, font=data_font) lbl_dev = tk.Label(self.scrollable_leaderboard_frame, text=f"{deviation:.3f}", fg=color, bg=bg_color,
padx=5, pady=2, font=data_font)
lbl_dev.grid(row=row_num, column=4, sticky='nsew') lbl_dev.grid(row=row_num, column=4, sticky='nsew')
# Wichtig: Nach dem Hinzufügen von Widgets und Konfigurieren der Spalten # Wichtig: Nach dem Hinzufügen von Widgets und Konfigurieren der Spalten
@@ -224,7 +230,6 @@ class TimeGuessGame:
self.scrollable_leaderboard_frame.update_idletasks() self.scrollable_leaderboard_frame.update_idletasks()
self.leaderboard_canvas.configure(scrollregion=self.leaderboard_canvas.bbox("all")) self.leaderboard_canvas.configure(scrollregion=self.leaderboard_canvas.bbox("all"))
# ... (Rest der Methoden: setup_connection, check_initial_connection, etc. bleiben unverändert) ... # ... (Rest der Methoden: setup_connection, check_initial_connection, etc. bleiben unverändert) ...
def setup_connection(self): def setup_connection(self):
if self.connected: if self.connected:
@@ -304,7 +309,6 @@ class TimeGuessGame:
messagebox.showerror("Verbindungsfehler", f"Keine Antwort vom Arduino auf {port_name}:\n{e}") messagebox.showerror("Verbindungsfehler", f"Keine Antwort vom Arduino auf {port_name}:\n{e}")
self.disconnect_serial() # Verbindung sauber trennen self.disconnect_serial() # Verbindung sauber trennen
def disconnect_serial(self): def disconnect_serial(self):
"""Trennt die serielle Verbindung sauber.""" """Trennt die serielle Verbindung sauber."""
self.game_active = False # Spiel stoppen, falls aktiv self.game_active = False # Spiel stoppen, falls aktiv
@@ -362,7 +366,8 @@ class TimeGuessGame:
self.name_entry.delete(0, tk.END) self.name_entry.delete(0, tk.END)
except serial.SerialException as e: except serial.SerialException as e:
messagebox.showerror("Kommunikationsfehler", f"Fehler beim Senden an Arduino:\n{e}\nVerbindung wird getrennt.", parent=self.root) messagebox.showerror("Kommunikationsfehler",
f"Fehler beim Senden an Arduino:\n{e}\nVerbindung wird getrennt.", parent=self.root)
self.disconnect_serial() # Bei Sendefehler Verbindung trennen self.disconnect_serial() # Bei Sendefehler Verbindung trennen
except Exception as e: except Exception as e:
messagebox.showerror("Fehler", f"Fehler beim Starten des Spiels: {e}", parent=self.root) messagebox.showerror("Fehler", f"Fehler beim Starten des Spiels: {e}", parent=self.root)
@@ -371,7 +376,6 @@ class TimeGuessGame:
if self.connected: if self.connected:
self.start_button.config(state=tk.NORMAL) self.start_button.config(state=tk.NORMAL)
def read_serial(self): def read_serial(self):
print("Serial Read-Thread gestartet.") print("Serial Read-Thread gestartet.")
buffer = "" buffer = ""
@@ -414,7 +418,6 @@ class TimeGuessGame:
print("Serial Read-Thread beendet.") print("Serial Read-Thread beendet.")
def handle_connection_loss(self): def handle_connection_loss(self):
"""Wird aufgerufen, wenn ein serieller Fehler auftritt (im Hauptthread).""" """Wird aufgerufen, wenn ein serieller Fehler auftritt (im Hauptthread)."""
if self.connected: # Nur handeln, wenn wir dachten, wir wären verbunden if self.connected: # Nur handeln, wenn wir dachten, wir wären verbunden
@@ -423,7 +426,6 @@ class TimeGuessGame:
messagebox.showerror("Verbindungsfehler", "Verbindung zum Arduino verloren!", parent=self.root) messagebox.showerror("Verbindungsfehler", "Verbindung zum Arduino verloren!", parent=self.root)
self.disconnect_serial() # Ruft UI-Updates etc. auf self.disconnect_serial() # Ruft UI-Updates etc. auf
def process_arduino_message(self, message): def process_arduino_message(self, message):
# Diese Funktion wird jetzt über self.root.after() im Hauptthread aufgerufen # Diese Funktion wird jetzt über self.root.after() im Hauptthread aufgerufen
if not self.connected and not message.startswith("PC_READY"): if not self.connected and not message.startswith("PC_READY"):
@@ -440,7 +442,7 @@ class TimeGuessGame:
'elapsed_time': 0, 'elapsed_time': 0,
'deviation': 0 'deviation': 0
} }
self.target_time_label.config(text=f"Zielzeit: {target_time/1000:.1f} Sekunden") self.target_time_label.config(text=f"Zielzeit: {target_time / 1000:.1f} Sekunden")
self.status_label.config(text="Spiel läuft") # Status hier anpassen self.status_label.config(text="Spiel läuft") # Status hier anpassen
self.instruction_label.config(text="Drücke Taster zum Starten...") # Erste Anweisung nach Zielzeit self.instruction_label.config(text="Drücke Taster zum Starten...") # Erste Anweisung nach Zielzeit
@@ -450,6 +452,9 @@ class TimeGuessGame:
self.status_label.config(text="Bereit zum Start") # Klarerer Status self.status_label.config(text="Bereit zum Start") # Klarerer Status
elif message == "TIME_STARTED": elif message == "TIME_STARTED":
startsound = pygame.mixer.Sound("start.mp3")
pygame.mixer.Sound.play(startsound)
self.status_label.config(text="Zeitmessung läuft...") self.status_label.config(text="Zeitmessung läuft...")
self.instruction_label.config( self.instruction_label.config(
text="Drücke den Taster erneut zum Stoppen") # Verkürzt text="Drücke den Taster erneut zum Stoppen") # Verkürzt
@@ -460,7 +465,7 @@ class TimeGuessGame:
elapsed_time = int(message.split(":")[1].strip()) elapsed_time = int(message.split(":")[1].strip())
self.current_game['elapsed_time'] = elapsed_time self.current_game['elapsed_time'] = elapsed_time
self.elapsed_time_label.config( self.elapsed_time_label.config(
text=f"Deine Zeit: {elapsed_time/1000:.3f} Sekunden") text=f"Deine Zeit: {elapsed_time / 1000:.3f} Sekunden")
elif message.startswith("DEVIATION:"): elif message.startswith("DEVIATION:"):
# Nur verarbeiten, wenn ein Spiel aktiv ist # Nur verarbeiten, wenn ein Spiel aktiv ist
@@ -475,6 +480,16 @@ class TimeGuessGame:
color = "green" if deviation_abs_sec < 0.5 else "orange" if deviation_abs_sec < 1.0 else "red" color = "green" if deviation_abs_sec < 0.5 else "orange" if deviation_abs_sec < 1.0 else "red"
self.deviation_label.config(fg=color) self.deviation_label.config(fg=color)
if deviation_abs_sec >= 10:
fail = pygame.mixer.Sound("fail.mp3")
pygame.mixer.Sound.play(fail)
elif deviation_abs_sec == 0:
winner = pygame.mixer.Sound("winner.mp3")
pygame.mixer.Sound.play(winner)
else:
fail = pygame.mixer.Sound("good.mp3")
pygame.mixer.Sound.play(fail)
elif message == "GAME_ENDED": elif message == "GAME_ENDED":
self.game_active = False # Wichtig: Spielstatus aktualisieren self.game_active = False # Wichtig: Spielstatus aktualisieren
self.status_label.config(text="Spiel beendet") self.status_label.config(text="Spiel beendet")
@@ -489,7 +504,6 @@ class TimeGuessGame:
self.submit_button.config(state=tk.DISABLED) self.submit_button.config(state=tk.DISABLED)
self.skip_button.config(state=tk.DISABLED) self.skip_button.config(state=tk.DISABLED)
elif message == "PC_READY": # Arduino meldet Bereitschaft nach Neustart/Init elif message == "PC_READY": # Arduino meldet Bereitschaft nach Neustart/Init
print("Arduino meldet PC_READY") print("Arduino meldet PC_READY")
# Wenn wir verbunden sind oder versuchen zu verbinden, UI aktualisieren # Wenn wir verbunden sind oder versuchen zu verbinden, UI aktualisieren
@@ -501,7 +515,6 @@ class TimeGuessGame:
# Wenn keine Verbindung (mehr) besteht, nur loggen # Wenn keine Verbindung (mehr) besteht, nur loggen
print("PC_READY empfangen, aber nicht verbunden.") print("PC_READY empfangen, aber nicht verbunden.")
# Handle andere unerwartete Nachrichten (optional) # Handle andere unerwartete Nachrichten (optional)
# else: # else:
# print(f"Unbekannte Nachricht vom Arduino: {message}") # print(f"Unbekannte Nachricht vom Arduino: {message}")
@@ -511,10 +524,9 @@ class TimeGuessGame:
except IndexError as e: except IndexError as e:
print(f"Fehler beim Splitten der Nachricht '{message}': {e}") print(f"Fehler beim Splitten der Nachricht '{message}': {e}")
except Exception as e: except Exception as e:
# Catch-all für unerwartete Fehler während der Nachrichtenverarbeitung
import traceback import traceback
print(f"Unerwarteter Fehler beim Verarbeiten der Nachricht '{message}': {e}") traceback.print_exc()
traceback.print_exc() # Gibt detaillierten Traceback aus print(f"Unerwarteter Fehler bei der Nachrichtenverarbeitung: {e}")
def submit_score(self): def submit_score(self):
name = self.name_entry.get().strip() name = self.name_entry.get().strip()
@@ -644,8 +656,7 @@ class TimeGuessGame:
print("Tkinter-Fenster wird zerstört.") print("Tkinter-Fenster wird zerstört.")
self.root.destroy() # Tkinter-Fenster zerstören self.root.destroy() # Tkinter-Fenster zerstören
if __name__ == "__main__": if __name__ == "__main__":
root = tk.Tk() root = tk.Tk()
app = TimeGuessGame(root) game = TimeGuessGame(root)
root.mainloop() root.mainloop()

BIN
good.mp3 Normal file

Binary file not shown.

BIN
start.mp3 Normal file

Binary file not shown.

BIN
starten.mp3 Normal file

Binary file not shown.

BIN
winner.mp3 Normal file

Binary file not shown.