From 953a0d294e53c93c465efc81bbdac48e9bb4aa94 Mon Sep 17 00:00:00 2001 From: laemmerzahlkr91200 Date: Wed, 3 Dec 2025 16:08:36 +0100 Subject: [PATCH] IT WORKS 11!!!!!!!!!!!!!!!!! Es funktioniert und umbennungen --- .idea/Memory GlobalMatch.iml | 2 +- .idea/misc.xml | 2 +- ...d_Memory_Mouse.py => True_Finished_Game.py | 896 +++++++++--------- ...st_touch_area.cpython-312-pytest-9.0.1.pyc | Bin 0 -> 4178 bytes calibrate_touch_mouse_only.py | 4 +- calibration.json | 2 +- gesture_input_osc.py | 131 +-- old/calibrate_touch_mouse_only.py | 120 +++ old/gesture_input_osc.py | 196 ++++ old/gesture_input_osc_old.py | 119 +++ test_touch_area.py => touch_area.py | 2 +- 11 files changed, 955 insertions(+), 519 deletions(-) rename Finished_Memory_Mouse.py => True_Finished_Game.py (97%) create mode 100644 __pycache__/test_touch_area.cpython-312-pytest-9.0.1.pyc create mode 100644 old/calibrate_touch_mouse_only.py create mode 100644 old/gesture_input_osc.py create mode 100644 old/gesture_input_osc_old.py rename test_touch_area.py => touch_area.py (99%) diff --git a/.idea/Memory GlobalMatch.iml b/.idea/Memory GlobalMatch.iml index 2c80e12..9128335 100644 --- a/.idea/Memory GlobalMatch.iml +++ b/.idea/Memory GlobalMatch.iml @@ -4,7 +4,7 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 46e9a4d..70dc79a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/Finished_Memory_Mouse.py b/True_Finished_Game.py similarity index 97% rename from Finished_Memory_Mouse.py rename to True_Finished_Game.py index 5da9da7..39accec 100644 --- a/Finished_Memory_Mouse.py +++ b/True_Finished_Game.py @@ -1,448 +1,448 @@ -import pygame -import random -import sys -import os -import time -from pythonosc import dispatcher, osc_server -import threading - -# ===== OSC Setup ===== -touch_x, touch_y = None, None -clap_trigger = False - - -def osc_touch(address, x, y): - """Wird aufgerufen, wenn /touch x y über OSC empfangen wird.""" - global touch_x, touch_y - touch_x, touch_y = x, y - - -def osc_clap(address, *args): - """Wird aufgerufen, wenn /clap empfangen wird.""" - global clap_trigger - clap_trigger = True - - -def start_osc_server(): - disp = dispatcher.Dispatcher() - disp.map("/touch", osc_touch) - disp.map("/clap", osc_clap) - - # Dein gesture_input_osc sendet an 127.0.0.1:5005 → hier auch 5005 - server = osc_server.ThreadingOSCUDPServer(("127.0.0.1", 5005), disp) - print(" OSC server läuft auf Port 5005") - server.serve_forever() - - -# Starte den OSC Listener im Hintergrund -threading.Thread(target=start_osc_server, daemon=True).start() - -# ------------------------------- -# Global Match – Country–Capital Memory Game -# ------------------------------- - -# --- Colors & Layout --- -CARD_FRONT_COLOR = (245, 246, 248) -CARD_BACK_COLOR = (100, 100, 200) -MATCH_COLOR = (160, 220, 160) -TEXT_COLOR = (10, 30, 40) -CAPITAL_COLOR = (0, 60, 180) -BG_COLOR = (10, 42, 83) -BUTTON_FILL = (18, 122, 138) -BUTTON_BORDER = (255, 255, 255) -FPS = 30 -SCREEN_WIDTH, SCREEN_HEIGHT = 900, 600 - -# --- Background / Logo --- -BASE_DIR = os.path.dirname(os.path.abspath(__file__)) -BACKGROUND_FILE = os.path.join(BASE_DIR, "GlobalHintergrund.png") - - -def build_card_back(image_path, size): - """Load and scale a logo image to fill card backs.""" - w, h = size - surf = pygame.Surface((w, h), pygame.SRCALPHA).convert_alpha() - if os.path.exists(image_path): - try: - img = pygame.image.load(image_path).convert_alpha() - img = pygame.transform.smoothscale(img, (w, h)) - surf.blit(img, (0, 0)) - except pygame.error as e: - print(f" Error loading {image_path}: {e}") - surf.fill((127, 127, 200)) - else: - print(f"Image not found: {image_path}") - surf.fill((127, 127, 200)) - return surf - - -class MemoryGame: - def __init__(self): - # Game Data - self.deck = [] - self.pair_map = {} - self.matched = [] - self.revealed = [] - self.cards = [] - self.card_rects = [] - self.selected = [] - self.scores = [0, 0] - self.current_player = 0 - self.found_pairs = 0 - self.total_pairs = 0 - - # UI and States - self.font = None - self.small_font = None - self.buttons = [] - self.running = True - self.state = "mode" - self.selected_continents = [] - self.level = None - self.pair_count = 6 - self.player_mode = 2 - - # "Bestätigungsphase" = wir warten auf Klatschen - self.awaiting_confirmation = False - self.confirmation_start_time = None - self.confirmation_time_limit = 5 # Sekunden - - # Visuals - self.card_back = None - - # ------------------------------- - # File Loading - # ------------------------------- - def load_cards(self, filename): - if not os.path.exists(filename): - print(f" File not found: {filename}") - return [] - pairs = [] - with open(filename, "r", encoding="utf-8") as f: - for line in f: - parts = line.strip().split() - if len(parts) >= 2: - pairs.append((parts[0], parts[1])) - return pairs - - def prepare_deck(self): - self.deck = [] - for continent in self.selected_continents: - base = continent - if self.level == "Easy": - self.deck += self.load_cards(base + "-major.txt") - elif self.level == "Normal": - self.deck += self.load_cards(base + "-major.txt") - self.deck += self.load_cards(base + "-Minor.txt") - elif self.level == "Hard": - self.deck += self.load_cards(base + "-major.txt") - self.deck += self.load_cards(base + "-Minor.txt") - self.deck += self.load_cards(base + "-Dependent.txt") - - if not self.deck: - print("No cards loaded. Check your text files.") - sys.exit() - - random.shuffle(self.deck) - self.deck = self.deck[:self.pair_count] - - # ------------------------------- - # Setup & Layout - # ------------------------------- - def setup_game(self): - self.cards = [] - self.pair_map = {} - for country, capital in self.deck: - self.cards.append({"text": country, "type": "country"}) - self.cards.append({"text": capital, "type": "capital"}) - self.pair_map[country] = capital - self.pair_map[capital] = country - - random.shuffle(self.cards) - self.matched = [False] * len(self.cards) - self.revealed = [False] * len(self.cards) - self.total_pairs = len(self.deck) - self.selected = [] - self.found_pairs = 0 - self.current_player = 0 - self.scores = [0, 0] - self.awaiting_confirmation = False - self.confirmation_start_time = None - - cols = 4 - rows = (len(self.cards) + cols - 1) // cols - margin = 10 - card_width = (SCREEN_WIDTH - (cols + 1) * margin) // cols - card_height = (SCREEN_HEIGHT - (rows + 1) * margin - 100) // rows - self.card_back = build_card_back(BACKGROUND_FILE, (card_width, card_height)) - - y_offset = 80 - self.card_rects = [] - for i in range(len(self.cards)): - col = i % cols - row = i // cols - x = margin + col * (card_width + margin) - y = y_offset + margin + row * (card_height + margin) - self.card_rects.append(pygame.Rect(x, y, card_width, card_height)) - - # ------------------------------- - # Drawing - # ------------------------------- - def draw_menu(self, screen, title, options): - screen.fill(BG_COLOR) - title_text = self.font.render(title, True, (255, 255, 255)) - screen.blit(title_text, (SCREEN_WIDTH // 2 - title_text.get_width() // 2, 100)) - self.buttons = [] - for i, option in enumerate(options): - rect = pygame.Rect(SCREEN_WIDTH // 2 - 150, 200 + i * 70, 300, 50) - pygame.draw.rect(screen, BUTTON_FILL, rect, border_radius=10) - pygame.draw.rect(screen, BUTTON_BORDER, rect, 2, border_radius=10) - text = self.font.render(option, True, (255, 255, 255)) - screen.blit(text, text.get_rect(center=rect.center)) - self.buttons.append((rect, option)) - pygame.display.flip() - - def draw_game(self, screen): - screen.fill(BG_COLOR) - if self.player_mode == 2: - title = self.font.render(f"Player {self.current_player + 1}'s turn", True, (255, 255, 255)) - score_text = self.font.render(f"Scores: P1={self.scores[0]} P2={self.scores[1]}", True, (220, 230, 235)) - else: - title = self.font.render("Single Player Mode", True, (255, 255, 255)) - score_text = self.font.render(f"Score: {self.scores[0]}", True, (220, 230, 235)) - screen.blit(title, (20, 20)) - screen.blit(score_text, (20, 50)) - - # Hinweis: wenn 2 Karten offen sind, warte auf Clap - if self.awaiting_confirmation and len(self.selected) == 2: - hint = self.small_font.render("👏 Klatschen, um das Paar zu bestätigen!", True, (255, 255, 0)) - screen.blit(hint, (SCREEN_WIDTH // 2 - hint.get_width() // 2, 80)) - - for i, rect in enumerate(self.card_rects): - if self.matched[i]: - pygame.draw.rect(screen, MATCH_COLOR, rect) - elif self.revealed[i]: - pygame.draw.rect(screen, CARD_FRONT_COLOR, rect) - else: - screen.blit(self.card_back, rect.topleft) - pygame.draw.rect(screen, (0, 0, 0), rect, 2) - - if self.revealed[i] or self.matched[i]: - card = self.cards[i] - text_color = CAPITAL_COLOR if card["type"] == "capital" else TEXT_COLOR - text = self.font.render(card["text"], True, text_color) - screen.blit(text, text.get_rect(center=(rect.centerx, rect.centery - 10))) - label = self.small_font.render( - "(Capital)" if card["type"] == "capital" else "(Country)", True, (80, 80, 80) - ) - screen.blit(label, label.get_rect(center=(rect.centerx, rect.centery + 20))) - - pygame.display.flip() - - # ------------------------------- - # Interaction Logic - # ------------------------------- - def handle_click(self, pos): - # ✅ Blockiere Touch über dem Spielfeld (Menü-Bereich oben) - if self.state == "game" and pos[1] < 80: - return - - # Menüs: mit Touch durchklicken - if self.state in ["mode", "continent", "americas", "difficulty", "pairs", "timer"]: - for rect, option in self.buttons: - if rect.collidepoint(pos): - if self.state == "mode": - self.player_mode = 1 if option == "1 Player" else 2 - self.state = "continent" - elif self.state == "continent": - if option == "Americas": - self.state = "americas" - elif option == "All Continents": - self.selected_continents = ["Europe", "Asia", "Africa", - "Oceania", "North-America", "South-America"] - self.state = "difficulty" - else: - self.selected_continents = [option] - self.state = "difficulty" - elif self.state == "americas": - if option == "North-America": - self.selected_continents = ["North-America"] - elif option == "South-America": - self.selected_continents = ["South-America"] - elif option == "Americas": - self.selected_continents = ["North-America", "South-America"] - self.state = "difficulty" - elif self.state == "difficulty": - self.level = option - self.state = "pairs" - elif self.state == "pairs": - self.pair_count = int(option) - self.state = "timer" - elif self.state == "timer": - self.confirmation_time_limit = int(option.replace("s", "")) - self.prepare_deck() - self.setup_game() - self.state = "game" - return - - # Spiel: Karten nur anklicken, wenn wir NICHT gerade auf Clap warten - elif self.state == "game": - if self.awaiting_confirmation or len(self.selected) >= 2: - # Während wir auf Klatschen warten, keine weiteren Karten öffnen - return - - for i, rect in enumerate(self.card_rects): - if rect.collidepoint(pos) and not self.revealed[i] and not self.matched[i]: - self.revealed[i] = True - self.selected.append(i) - return - - # ------------------------------- - # OSC Input Processing - # ------------------------------- - def process_osc_input(self): - """Verarbeite die aktuellen OSC-Eingaben (Touch & Clap).""" - global touch_x, touch_y, clap_trigger - - # TOUCH: als Klick ins Spiel (Menü oder Karte) - if touch_x is not None and touch_y is not None: - pos = (int(touch_x), int(touch_y)) - self.handle_click(pos) - touch_x, touch_y = None, None # Reset - - # CLAP: wenn 2 Karten offen und wir warten → Paar auswerten - if clap_trigger: - if self.awaiting_confirmation and len(self.selected) == 2: - self.resolve_pair() - clap_trigger = False - - # ------------------------------- - # Paar auswerten (nach Clap oder Timeout) - # ------------------------------- - def resolve_pair(self): - """Prüft das aktuelle Kartenpaar und aktualisiert Punkte / Spieler.""" - if len(self.selected) != 2: - return - - a, b = self.selected - text_a = self.cards[a]["text"] - text_b = self.cards[b]["text"] - is_match = self.pair_map.get(text_a) == text_b - - if is_match: - self.matched[a] = self.matched[b] = True - self.scores[self.current_player] += 1 - self.found_pairs += 1 - else: - # Falsches Paar: Karten wieder umdrehen, Punkt abziehen - self.revealed[a] = self.revealed[b] = False - self.scores[self.current_player] -= 1 - - # Reset für nächste Runde - self.selected = [] - self.awaiting_confirmation = False - self.confirmation_start_time = None - - # Spielerwechsel immer nach einem Paar (wie vorher) - if self.player_mode == 2: - self.current_player = 1 - self.current_player - - # ------------------------------- - # Game Logic - # ------------------------------- - def check_selected(self): - if self.state != "game": - return - - # Wenn zwei Karten offen sind → Warte auf Clap - if len(self.selected) == 2 and not self.awaiting_confirmation: - self.awaiting_confirmation = True - self.confirmation_start_time = time.time() - - # Timeout: wenn zu lange kein Clap → Karten zurückdrehen, Spielerwechsel - if self.awaiting_confirmation and self.confirmation_start_time is not None: - if time.time() - self.confirmation_start_time > self.confirmation_time_limit: - if len(self.selected) == 2: - a, b = self.selected - self.revealed[a] = self.revealed[b] = False - self.selected = [] - self.awaiting_confirmation = False - self.confirmation_start_time = None - if self.player_mode == 2: - self.current_player = 1 - self.current_player - - # ------------------------------- - # Winner Screen - # ------------------------------- - def display_winner(self, screen): - if self.player_mode == 1: - text = f" Final Score: {self.scores[0]}" - else: - if self.scores[0] > self.scores[1]: - text = " Player 1 Wins!" - elif self.scores[1] > self.scores[0]: - text = " Player 2 Wins!" - else: - text = " Draw!" - win_text = self.font.render(text, True, (255, 255, 0)) - rect = win_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)) - screen.blit(win_text, rect) - pygame.display.flip() - - # ------------------------------- - # Main Loop - # ------------------------------- - def run(self): - pygame.init() - screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) - pygame.display.set_caption("Global Match – Country–Capital Memory Game") - clock = pygame.time.Clock() - self.font = pygame.font.SysFont(None, 32) - self.small_font = pygame.font.SysFont(None, 22) - - while self.running: - for event in pygame.event.get(): - if event.type == pygame.QUIT: - self.running = False - # Maus komplett ignorieren (kein Klick mehr) - # elif event.type == pygame.MOUSEBUTTONDOWN: - # self.handle_click(event.pos) - # Wenn du zum Debuggen Maus willst, obige Zeilen entkommentieren. - - # HIER werden jetzt *jeden Frame* die OSC-Eingaben verarbeitet - self.process_osc_input() - - # Menü + Spiel zeichnen - if self.state == "mode": - self.draw_menu(screen, "Select Player Mode", ["1 Player", "2 Players"]) - elif self.state == "continent": - self.draw_menu(screen, "Select Continent", - ["Europe", "Americas", "Asia", "Africa", "Oceania", "All Continents"]) - elif self.state == "americas": - self.draw_menu(screen, "Select Region", ["North-America", "South-America", "Americas"]) - elif self.state == "difficulty": - self.draw_menu(screen, "Select Difficulty", ["Easy", "Normal", "Hard"]) - elif self.state == "pairs": - self.draw_menu(screen, "Select Number of Pairs", ["4", "6", "8", "10", "12"]) - elif self.state == "timer": - self.draw_menu(screen, "Select Confirmation Time", ["3s", "5s", "8s", "10s"]) - elif self.state == "game": - self.draw_game(screen) - self.check_selected() - if self.found_pairs == self.total_pairs: - self.display_winner(screen) - pygame.time.wait(4000) - self.running = False - - clock.tick(FPS) - - pygame.quit() - sys.exit() - - -# ------------------------------- -# Run -# ------------------------------- -if __name__ == "__main__": - game = MemoryGame() - game.run() +import pygame +import random +import sys +import os +import time +from pythonosc import dispatcher, osc_server +import threading + +# ===== OSC Setup ===== +touch_x, touch_y = None, None +clap_trigger = False + + +def osc_touch(address, x, y): + """Wird aufgerufen, wenn /touch x y über OSC empfangen wird.""" + global touch_x, touch_y + touch_x, touch_y = x, y + + +def osc_clap(address, *args): + """Wird aufgerufen, wenn /clap empfangen wird.""" + global clap_trigger + clap_trigger = True + + +def start_osc_server(): + disp = dispatcher.Dispatcher() + disp.map("/touch", osc_touch) + disp.map("/clap", osc_clap) + + # Dein gesture_input_osc sendet an 127.0.0.1:5005 → hier auch 5005 + server = osc_server.ThreadingOSCUDPServer(("127.0.0.1", 5005), disp) + print(" OSC server läuft auf Port 5005") + server.serve_forever() + + +# Starte den OSC Listener im Hintergrund +threading.Thread(target=start_osc_server, daemon=True).start() + +# ------------------------------- +# Global Match – Country–Capital Memory Game +# ------------------------------- + +# --- Colors & Layout --- +CARD_FRONT_COLOR = (245, 246, 248) +CARD_BACK_COLOR = (100, 100, 200) +MATCH_COLOR = (160, 220, 160) +TEXT_COLOR = (10, 30, 40) +CAPITAL_COLOR = (0, 60, 180) +BG_COLOR = (10, 42, 83) +BUTTON_FILL = (18, 122, 138) +BUTTON_BORDER = (255, 255, 255) +FPS = 30 +SCREEN_WIDTH, SCREEN_HEIGHT = 900, 600 + +# --- Background / Logo --- +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +BACKGROUND_FILE = os.path.join(BASE_DIR, "GlobalHintergrund.png") + + +def build_card_back(image_path, size): + """Load and scale a logo image to fill card backs.""" + w, h = size + surf = pygame.Surface((w, h), pygame.SRCALPHA).convert_alpha() + if os.path.exists(image_path): + try: + img = pygame.image.load(image_path).convert_alpha() + img = pygame.transform.smoothscale(img, (w, h)) + surf.blit(img, (0, 0)) + except pygame.error as e: + print(f" Error loading {image_path}: {e}") + surf.fill((127, 127, 200)) + else: + print(f"Image not found: {image_path}") + surf.fill((127, 127, 200)) + return surf + + +class MemoryGame: + def __init__(self): + # Game Data + self.deck = [] + self.pair_map = {} + self.matched = [] + self.revealed = [] + self.cards = [] + self.card_rects = [] + self.selected = [] + self.scores = [0, 0] + self.current_player = 0 + self.found_pairs = 0 + self.total_pairs = 0 + + # UI and States + self.font = None + self.small_font = None + self.buttons = [] + self.running = True + self.state = "mode" + self.selected_continents = [] + self.level = None + self.pair_count = 6 + self.player_mode = 2 + + # "Bestätigungsphase" = wir warten auf Klatschen + self.awaiting_confirmation = False + self.confirmation_start_time = None + self.confirmation_time_limit = 5 # Sekunden + + # Visuals + self.card_back = None + + # ------------------------------- + # File Loading + # ------------------------------- + def load_cards(self, filename): + if not os.path.exists(filename): + print(f" File not found: {filename}") + return [] + pairs = [] + with open(filename, "r", encoding="utf-8") as f: + for line in f: + parts = line.strip().split() + if len(parts) >= 2: + pairs.append((parts[0], parts[1])) + return pairs + + def prepare_deck(self): + self.deck = [] + for continent in self.selected_continents: + base = continent + if self.level == "Easy": + self.deck += self.load_cards(base + "-major.txt") + elif self.level == "Normal": + self.deck += self.load_cards(base + "-major.txt") + self.deck += self.load_cards(base + "-Minor.txt") + elif self.level == "Hard": + self.deck += self.load_cards(base + "-major.txt") + self.deck += self.load_cards(base + "-Minor.txt") + self.deck += self.load_cards(base + "-Dependent.txt") + + if not self.deck: + print("No cards loaded. Check your text files.") + sys.exit() + + random.shuffle(self.deck) + self.deck = self.deck[:self.pair_count] + + # ------------------------------- + # Setup & Layout + # ------------------------------- + def setup_game(self): + self.cards = [] + self.pair_map = {} + for country, capital in self.deck: + self.cards.append({"text": country, "type": "country"}) + self.cards.append({"text": capital, "type": "capital"}) + self.pair_map[country] = capital + self.pair_map[capital] = country + + random.shuffle(self.cards) + self.matched = [False] * len(self.cards) + self.revealed = [False] * len(self.cards) + self.total_pairs = len(self.deck) + self.selected = [] + self.found_pairs = 0 + self.current_player = 0 + self.scores = [0, 0] + self.awaiting_confirmation = False + self.confirmation_start_time = None + + cols = 4 + rows = (len(self.cards) + cols - 1) // cols + margin = 10 + card_width = (SCREEN_WIDTH - (cols + 1) * margin) // cols + card_height = (SCREEN_HEIGHT - (rows + 1) * margin - 100) // rows + self.card_back = build_card_back(BACKGROUND_FILE, (card_width, card_height)) + + y_offset = 80 + self.card_rects = [] + for i in range(len(self.cards)): + col = i % cols + row = i // cols + x = margin + col * (card_width + margin) + y = y_offset + margin + row * (card_height + margin) + self.card_rects.append(pygame.Rect(x, y, card_width, card_height)) + + # ------------------------------- + # Drawing + # ------------------------------- + def draw_menu(self, screen, title, options): + screen.fill(BG_COLOR) + title_text = self.font.render(title, True, (255, 255, 255)) + screen.blit(title_text, (SCREEN_WIDTH // 2 - title_text.get_width() // 2, 100)) + self.buttons = [] + for i, option in enumerate(options): + rect = pygame.Rect(SCREEN_WIDTH // 2 - 150, 200 + i * 70, 300, 50) + pygame.draw.rect(screen, BUTTON_FILL, rect, border_radius=10) + pygame.draw.rect(screen, BUTTON_BORDER, rect, 2, border_radius=10) + text = self.font.render(option, True, (255, 255, 255)) + screen.blit(text, text.get_rect(center=rect.center)) + self.buttons.append((rect, option)) + pygame.display.flip() + + def draw_game(self, screen): + screen.fill(BG_COLOR) + if self.player_mode == 2: + title = self.font.render(f"Player {self.current_player + 1}'s turn", True, (255, 255, 255)) + score_text = self.font.render(f"Scores: P1={self.scores[0]} P2={self.scores[1]}", True, (220, 230, 235)) + else: + title = self.font.render("Single Player Mode", True, (255, 255, 255)) + score_text = self.font.render(f"Score: {self.scores[0]}", True, (220, 230, 235)) + screen.blit(title, (20, 20)) + screen.blit(score_text, (20, 50)) + + # Hinweis: wenn 2 Karten offen sind, warte auf Clap + if self.awaiting_confirmation and len(self.selected) == 2: + hint = self.small_font.render("👏 Klatschen, um das Paar zu bestätigen!", True, (255, 255, 0)) + screen.blit(hint, (SCREEN_WIDTH // 2 - hint.get_width() // 2, 80)) + + for i, rect in enumerate(self.card_rects): + if self.matched[i]: + pygame.draw.rect(screen, MATCH_COLOR, rect) + elif self.revealed[i]: + pygame.draw.rect(screen, CARD_FRONT_COLOR, rect) + else: + screen.blit(self.card_back, rect.topleft) + pygame.draw.rect(screen, (0, 0, 0), rect, 2) + + if self.revealed[i] or self.matched[i]: + card = self.cards[i] + text_color = CAPITAL_COLOR if card["type"] == "capital" else TEXT_COLOR + text = self.font.render(card["text"], True, text_color) + screen.blit(text, text.get_rect(center=(rect.centerx, rect.centery - 10))) + label = self.small_font.render( + "(Capital)" if card["type"] == "capital" else "(Country)", True, (80, 80, 80) + ) + screen.blit(label, label.get_rect(center=(rect.centerx, rect.centery + 20))) + + pygame.display.flip() + + # ------------------------------- + # Interaction Logic + # ------------------------------- + def handle_click(self, pos): + # ✅ Blockiere Touch über dem Spielfeld (Menü-Bereich oben) + if self.state == "game" and pos[1] < 80: + return + + # Menüs: mit Touch durchklicken + if self.state in ["mode", "continent", "americas", "difficulty", "pairs", "timer"]: + for rect, option in self.buttons: + if rect.collidepoint(pos): + if self.state == "mode": + self.player_mode = 1 if option == "1 Player" else 2 + self.state = "continent" + elif self.state == "continent": + if option == "Americas": + self.state = "americas" + elif option == "All Continents": + self.selected_continents = ["Europe", "Asia", "Africa", + "Oceania", "North-America", "South-America"] + self.state = "difficulty" + else: + self.selected_continents = [option] + self.state = "difficulty" + elif self.state == "americas": + if option == "North-America": + self.selected_continents = ["North-America"] + elif option == "South-America": + self.selected_continents = ["South-America"] + elif option == "Americas": + self.selected_continents = ["North-America", "South-America"] + self.state = "difficulty" + elif self.state == "difficulty": + self.level = option + self.state = "pairs" + elif self.state == "pairs": + self.pair_count = int(option) + self.state = "timer" + elif self.state == "timer": + self.confirmation_time_limit = int(option.replace("s", "")) + self.prepare_deck() + self.setup_game() + self.state = "game" + return + + # Spiel: Karten nur anklicken, wenn wir NICHT gerade auf Clap warten + elif self.state == "game": + if self.awaiting_confirmation or len(self.selected) >= 2: + # Während wir auf Klatschen warten, keine weiteren Karten öffnen + return + + for i, rect in enumerate(self.card_rects): + if rect.collidepoint(pos) and not self.revealed[i] and not self.matched[i]: + self.revealed[i] = True + self.selected.append(i) + return + + # ------------------------------- + # OSC Input Processing + # ------------------------------- + def process_osc_input(self): + """Verarbeite die aktuellen OSC-Eingaben (Touch & Clap).""" + global touch_x, touch_y, clap_trigger + + # TOUCH: als Klick ins Spiel (Menü oder Karte) + if touch_x is not None and touch_y is not None: + pos = (int(touch_x), int(touch_y)) + self.handle_click(pos) + touch_x, touch_y = None, None # Reset + + # CLAP: wenn 2 Karten offen und wir warten → Paar auswerten + if clap_trigger: + if self.awaiting_confirmation and len(self.selected) == 2: + self.resolve_pair() + clap_trigger = False + + # ------------------------------- + # Paar auswerten (nach Clap oder Timeout) + # ------------------------------- + def resolve_pair(self): + """Prüft das aktuelle Kartenpaar und aktualisiert Punkte / Spieler.""" + if len(self.selected) != 2: + return + + a, b = self.selected + text_a = self.cards[a]["text"] + text_b = self.cards[b]["text"] + is_match = self.pair_map.get(text_a) == text_b + + if is_match: + self.matched[a] = self.matched[b] = True + self.scores[self.current_player] += 1 + self.found_pairs += 1 + else: + # Falsches Paar: Karten wieder umdrehen, Punkt abziehen + self.revealed[a] = self.revealed[b] = False + self.scores[self.current_player] -= 1 + + # Reset für nächste Runde + self.selected = [] + self.awaiting_confirmation = False + self.confirmation_start_time = None + + # Spielerwechsel immer nach einem Paar (wie vorher) + if self.player_mode == 2: + self.current_player = 1 - self.current_player + + # ------------------------------- + # Game Logic + # ------------------------------- + def check_selected(self): + if self.state != "game": + return + + # Wenn zwei Karten offen sind → Warte auf Clap + if len(self.selected) == 2 and not self.awaiting_confirmation: + self.awaiting_confirmation = True + self.confirmation_start_time = time.time() + + # Timeout: wenn zu lange kein Clap → Karten zurückdrehen, Spielerwechsel + if self.awaiting_confirmation and self.confirmation_start_time is not None: + if time.time() - self.confirmation_start_time > self.confirmation_time_limit: + if len(self.selected) == 2: + a, b = self.selected + self.revealed[a] = self.revealed[b] = False + self.selected = [] + self.awaiting_confirmation = False + self.confirmation_start_time = None + if self.player_mode == 2: + self.current_player = 1 - self.current_player + + # ------------------------------- + # Winner Screen + # ------------------------------- + def display_winner(self, screen): + if self.player_mode == 1: + text = f" Final Score: {self.scores[0]}" + else: + if self.scores[0] > self.scores[1]: + text = " Player 1 Wins!" + elif self.scores[1] > self.scores[0]: + text = " Player 2 Wins!" + else: + text = " Draw!" + win_text = self.font.render(text, True, (255, 255, 0)) + rect = win_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)) + screen.blit(win_text, rect) + pygame.display.flip() + + # ------------------------------- + # Main Loop + # ------------------------------- + def run(self): + pygame.init() + screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) + pygame.display.set_caption("Global Match – Country–Capital Memory Game") + clock = pygame.time.Clock() + self.font = pygame.font.SysFont(None, 32) + self.small_font = pygame.font.SysFont(None, 22) + + while self.running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + self.running = False + # Maus komplett ignorieren (kein Klick mehr) + # elif event.type == pygame.MOUSEBUTTONDOWN: + # self.handle_click(event.pos) + # Wenn du zum Debuggen Maus willst, obige Zeilen entkommentieren. + + # HIER werden jetzt *jeden Frame* die OSC-Eingaben verarbeitet + self.process_osc_input() + + # Menü + Spiel zeichnen + if self.state == "mode": + self.draw_menu(screen, "Select Player Mode", ["1 Player", "2 Players"]) + elif self.state == "continent": + self.draw_menu(screen, "Select Continent", + ["Europe", "Americas", "Asia", "Africa", "Oceania", "All Continents"]) + elif self.state == "americas": + self.draw_menu(screen, "Select Region", ["North-America", "South-America", "Americas"]) + elif self.state == "difficulty": + self.draw_menu(screen, "Select Difficulty", ["Easy", "Normal", "Hard"]) + elif self.state == "pairs": + self.draw_menu(screen, "Select Number of Pairs", ["4", "6", "8", "10", "12"]) + elif self.state == "timer": + self.draw_menu(screen, "Select Confirmation Time", ["3s", "5s", "8s", "10s"]) + elif self.state == "game": + self.draw_game(screen) + self.check_selected() + if self.found_pairs == self.total_pairs: + self.display_winner(screen) + pygame.time.wait(4000) + self.running = False + + clock.tick(FPS) + + pygame.quit() + sys.exit() + + +# ------------------------------- +# Run +# ------------------------------- +if __name__ == "__main__": + game = MemoryGame() + game.run() diff --git a/__pycache__/test_touch_area.cpython-312-pytest-9.0.1.pyc b/__pycache__/test_touch_area.cpython-312-pytest-9.0.1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03d95e3d7a59d75d6e4d44847970672a89cbc7a1 GIT binary patch literal 4178 zcmcH+TWk~A^^WJ-89&B(KoSB;m>?*)KuBN_<+1Iu3E6}|AP@@8de<5}lQ70F?u^;B zo9>bnH7;twOES@2F*O)aWpY&}%5@eYW@-Y>moPXN^4SJbHAAsP@RA@$ z;v-^oRE{3?%ONviZ7 zUnYvW+utp#m$g{jzb|lBj>xJe6Mtk#_Mem^F_H{?q-g%paO|QO4n(LP0uh7m*I;I# zxT#2Fu|1yDom4*tiSV$O8~*BM02%bMcKy`guNr37O%L8}Sga8;eaqOn=xUx(p5q;_ zVG0UTU^YvQzXwXC8bg+1%&7>OPdA3rC}b(I%m;Nk%u!jLn$x6(41+XB#a^7>N-XY!|E=Q>)e;MO#*>QU!r{=v1ewi9`#)OOgf{ z*TP@z0gyq`C9Re6zmf}bETb!(@Beb%cim+akJ-vDkhUUGH~OR>AwTGIj2*+8 zsbcgtCka_@VNC-UOfzY=2?%LCPu~?9gWg4sgwq(@t)w*u-8_j=((ZGle$XS5i(v&K zvM`tsB_c*OIVz+QL^wqhIjSi#Nkm86eb~pY?1k;g1^_G10Qgu!Pi7Dd00e{i*rb{> z%G0QCtWfZPCA2Gp0biBwh$lzIh^*r?a!}JfXS>cEJ2ntF-`73VtMjE%@3Fp*dWUqU z98E-IB5E)j4*i*q39Rwi!3%>#mZQ2O7z@V;gl|fRn62mxQE5Dbm~DEem#z$4DP3D7 zK45Qzzxo0|2K|jcp6PkXc_-Zy?x~|wS7vt2F<T{QH~5^}^PL?Q7o!H;&}vnH8ByaO0RP(r@rE5*-e}20 zAu4rE(`fuOnr5Ym5zAY~DlPkM65rw>l2r-3aUL2N*R&7~zH1t-c+4aQQPSdu>MYbk zLiHAEA!d34?{74TA?gEbnntb4XcGGnn>&VDOlr9|B{Ilasg2h13=<2rnOM0uS3}$Z z?iib9LtDyR6RWuAR+ColX%(k3H`{2+#k2_6UgnvYWG^Rfje3pIAjy~V47|k;P3pC~ z75nWnspUSjr;M9e8cB|_1gWYVx86w>S1C(!&X>v>qv+@~_bm4nvt2ZF-9?hSg61jG ze$7g(AW{j3jT}H|=pxi}AdM&_7?yPh zk)v=L9Yd~!Wg}rFPB#r+_Sp$_B65(3}$OjZ>wa>M+2?s|+B}z^J8=MTOAy5cZ zg4lJV|62d`lgs?N$xkOfE$~~j{MP&7$0rxJeVF6BXz}N|F>r05fLpV;b#e27JpR#3 zckSfiiNghVYu4R5t<9dvxp!o`UwY~$`zQJfo|deqCFj`;iNk^H-U42q#p`GK=A=B{ zSHK6e_+TC%UgeOxdbxhn9sezVp?+Joep{}7JERw9h+VUiPX$b?|uB`$GPeq zFKQZA?ZAOG&T1ncp2%};Al%tFbv4hm0_Le-bPLPo0a~-3*6FLWUp#d#?9F?6*3y%Y zeNWCT)aE^1-_|wU*?DW{{ieCjubOAQa~B^sJ?Z?q`H^?A^(PDK7rw~VogeR7DvF(E zXB0@m;kh%});+~}3$|?Mhl?#;c~3XZ)ZPhif!mbjHs!gNC61qTO}M7^-t>OA9jS-Yr0O;7EuoyeaN1&R%)MbfKG znhO>i-)|x*8nY9|mts*`_0zO!d{L11DP~!7(Zpy%Gi4I(wC680H&= STILL_REQUIRED and (now - prev_touch_time) > 0.5: client.send_message("/touch", [sx, sy]) - print(f"👉 STABILER TOUCH bei {sx},{sy} nach {still_time:.2f}s") + print(f"👉 TOUCH bei {sx},{sy} nach {still_time:.2f}s") prev_touch_time = now finger_still_start = None else: finger_still_start = now - # IMMER aktualisieren - last_finger_pos = current_pos + last_finger_pos = current_pos - # Finger visualisieren cv2.circle(frame_touch, (fx, fy), 10, (0, 255, 0), -1) cv2.putText(frame_touch, f"{sx},{sy}", (fx + 10, fy - 10), - cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) + cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2) - now = time.time() - current_pos = (fx, fy) - - if last_finger_pos is None: - # erster Punkt - last_finger_pos = current_pos - finger_still_start = now - else: - dist = math.hypot(current_pos[0] - last_finger_pos[0], - current_pos[1] - last_finger_pos[1]) - - if dist < MOVE_TOLERANCE: - # Finger ist "ruhig" - if finger_still_start is None: - finger_still_start = now - else: - still_time = now - finger_still_start - if still_time >= STILL_REQUIRED and (now - prev_touch_time) > 0.5: - # JETZT: stabiler Touch → sende genau 1 Klick - client.send_message("/touch", [sx, sy]) - print(f"STABILER TOUCH bei {sx},{sy} nach {still_time:.2f}s") - print("SCREEN COORD:", sx, sy) - - prev_touch_time = now - # reset, damit der nächste Touch erst nach neuer Bewegung kommt - finger_still_start = None - else: - # Finger hat sich deutlich bewegt → Timer neu starten - - finger_still_start = now - last_finger_pos = current_pos else: - # keine Hand → Reset last_finger_pos = None finger_still_start = None - # --------------------------------------- - # CLAP (zwei Hände) - # --------------------------------------- + # ------------------------------------------------------------- + # GESTURE detection (clap) + # ------------------------------------------------------------- rgb_g = cv2.cvtColor(frame_gest, cv2.COLOR_BGR2RGB) res_g = hands_gesture.process(rgb_g) gh, gw, _ = frame_gest.shape @@ -179,7 +180,7 @@ def run_gesture_input(): client.send_message("/clap", 1) print("👏 SEND /clap") cv2.putText(frame_gest, "👏", (int(gw/2)-20, 80), - cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 255), 3) + cv2.FONT_HERSHEY_SIMPLEX, 2, (0,255,255), 3) cv2.imshow("Touch-Cam", frame_touch) cv2.imshow("Gesture-Cam", frame_gest) diff --git a/old/calibrate_touch_mouse_only.py b/old/calibrate_touch_mouse_only.py new file mode 100644 index 0000000..f4d2213 --- /dev/null +++ b/old/calibrate_touch_mouse_only.py @@ -0,0 +1,120 @@ +import cv2 +import json +import numpy as np + +CAM_INDEX = 1 +OUTPUT_FILE = "calibration.json" + +# Reihenfolge: P1=oben links, P2=oben rechts, P3=unten rechts, P4=unten links +POINT_NAMES = ["P1 (oben links)", "P2 (oben rechts)", "P3 (unten rechts)", "P4 (unten links)"] + + +def dist(a, b): + return np.linalg.norm(np.array(a) - np.array(b)) + + +def main(): + cap = cv2.VideoCapture(CAM_INDEX) + if not cap.isOpened(): + print("❌ Kamera konnte nicht geöffnet werden!") + return + + print("📸 Kalibrierung gestartet (nur Maus)") + print("----------------------------------") + print("🖱 Linksklick = Punkt setzen") + print("🖱 Ziehen = Punkt verschieben") + print("🖱 Rechtsklick = Punkt löschen") + print("💾 S / Enter = Speichern") + print("❌ ESC = Abbrechen") + print("----------------------------------") + print("Bitte Punkte in dieser Reihenfolge setzen:") + for i, name in enumerate(POINT_NAMES): + print(f" {i+1}. {name}") + + points = [None, None, None, None] + dragging_index = None + + def mouse_callback(event, mx, my, flags, param): + nonlocal dragging_index, points + + # Linksklick → Punkt setzen oder ziehen + if event == cv2.EVENT_LBUTTONDOWN: + # Prüfen ob Klick auf existierenden Punkt + for i, p in enumerate(points): + if p is not None and dist(p, (mx, my)) < 20: + dragging_index = i + return + + # Neuen Punkt setzen + for i in range(4): + if points[i] is None: + points[i] = (mx, my) + print(f"✔ {POINT_NAMES[i]} gesetzt bei {points[i]}") + return + + # Dragging + elif event == cv2.EVENT_MOUSEMOVE and dragging_index is not None: + points[dragging_index] = (mx, my) + + # Loslassen + elif event == cv2.EVENT_LBUTTONUP: + dragging_index = None + + # Rechtsklick → Punkt löschen + elif event == cv2.EVENT_RBUTTONDOWN: + for i, p in enumerate(points): + if p is not None and dist(p, (mx, my)) < 20: + print(f"🗑 {POINT_NAMES[i]} gelöscht") + points[i] = None + return + + cv2.namedWindow("Calibration") + cv2.setMouseCallback("Calibration", mouse_callback) + + while True: + ok, frame = cap.read() + if not ok: + break + frame = cv2.flip(frame, 1) + + h, w, _ = frame.shape + + # Punkte zeichnen + for i, p in enumerate(points): + if p is not None: + cv2.circle(frame, p, 10, (0, 255, 255), -1) + cv2.putText(frame, f"P{i+1}", (p[0] + 10, p[1] - 10), + cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,255), 2) + + # Polygon zeichnen wenn alle Punkte vorhanden + if all(points): + cv2.polylines(frame, [np.array(points, np.int32)], True, (0, 255, 0), 2) + + # Hinweise + cv2.putText(frame, "Setze P1,P2,P3,P4 mit der Maus | S=Speichern | ESC=Abbruch", + (10, h - 20), + cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255,255,255), 1) + + cv2.imshow("Calibration", frame) + key = cv2.waitKey(10) & 0xFF + + # Speichern + if key in [ord('s'), 13]: # 's' oder Enter + if None in points: + print("⚠️ Nicht alle 4 Punkte wurden gesetzt!") + else: + with open(OUTPUT_FILE, "w") as f: + json.dump(points, f) + print("💾 Kalibrierung gespeichert:", points) + break + + if key == 27: # ESC + print("❌ Kalibrierung abgebrochen") + break + + cap.release() + cv2.destroyAllWindows() + + +if __name__ == "__main__": + main() diff --git a/old/gesture_input_osc.py b/old/gesture_input_osc.py new file mode 100644 index 0000000..6406037 --- /dev/null +++ b/old/gesture_input_osc.py @@ -0,0 +1,196 @@ +import cv2 +import mediapipe as mp +import numpy as np +import math, time +from pythonosc import udp_client + +# ------------------------------- +# SETTINGS +# ------------------------------- +TOUCH_CAM_INDEX = 1 # deine Touch-Kamera +GESTURE_CAM_INDEX = 0 # deine Clap / Gesture Kamera + +GAME_SCREEN_WIDTH = 900 # muss zu deinem Pygame-Fenster passen! +GAME_SCREEN_HEIGHT = 600 + +# Wie "streng" ist der Touch? +STILL_REQUIRED = 1.0 # Sekunden, die der Finger fast still sein muss +MOVE_TOLERANCE = 25 # maximal erlaubte Bewegung (Pixel) + +# OSC Client → sendet ans Spiel +client = udp_client.SimpleUDPClient("127.0.0.1", 5005) + +# Globale Zustände +last_finger_pos = None +finger_still_start = None +prev_touch_time = 0.0 +prev_clap_time = 0.0 + + +def run_gesture_input(): + global last_finger_pos, finger_still_start + global prev_touch_time, prev_clap_time + + mp_hands = mp.solutions.hands + mp_draw = mp.solutions.drawing_utils + + hands_touch = mp_hands.Hands(max_num_hands=1, min_detection_confidence=0.6) + hands_gesture = mp_hands.Hands(max_num_hands=2, min_detection_confidence=0.6) + + # Kameras öffnen + cam_touch = cv2.VideoCapture(TOUCH_CAM_INDEX) + cam_gesture = cv2.VideoCapture(GESTURE_CAM_INDEX) + + if not cam_touch.isOpened(): + print(" Touch-Kamera konnte NICHT geöffnet werden!") + else: + print(f"Touch-Kamera geöffnet (Index {TOUCH_CAM_INDEX})") + + if not cam_gesture.isOpened(): + print(" Gesture-Kamera konnte NICHT geöffnet werden!") + else: + print(f"Gesture-Kamera geöffnet (Index {GESTURE_CAM_INDEX})") + + clap_cooldown = 1.5 + + while True: + ok1, frame_touch = cam_touch.read() + ok2, frame_gest = cam_gesture.read() + + if not ok1 or not ok2: + print(" Eine Kamera liefert kein Bild.") + break + + frame_touch = cv2.flip(frame_touch, 1) + frame_gest = cv2.flip(frame_gest, 1) + + # --------------------------------------- + # TOUCH (Zeigefinger) mit STILLSTAND + # --------------------------------------- + rgb_t = cv2.cvtColor(frame_touch, cv2.COLOR_BGR2RGB) + res_t = hands_touch.process(rgb_t) + th, tw, _ = frame_touch.shape #h= Höhe, w = Breite + + if res_t.multi_hand_landmarks: + lm = res_t.multi_hand_landmarks[0] + mp_draw.draw_landmarks(frame_touch, lm, mp_hands.HAND_CONNECTIONS) + + # Finger muss nach UNTEN zeigen (8 tiefer als 5) + if lm.landmark[8].y < lm.landmark[5].y: + last_finger_pos = None + finger_still_start = None + continue + + fx = int(lm.landmark[8].x * tw) + fy = int(lm.landmark[8].y * th) + + sx = int(fx * (GAME_SCREEN_WIDTH / tw)) + sy = int(fy * (GAME_SCREEN_HEIGHT / th)) + + now = time.time() + current_pos = (fx, fy) + + # erster Messpunkt + if last_finger_pos is None: + #erster Punkt + last_finger_pos = current_pos + finger_still_start = now + else: + dist = math.hypot(current_pos[0] - last_finger_pos[0], + current_pos[1] - last_finger_pos[1]) + + if dist < MOVE_TOLERANCE: + #Finger ist "ruhig" + if finger_still_start is None: + finger_still_start = now + else: + still_time = now - finger_still_start + if still_time >= STILL_REQUIRED and (now - prev_touch_time) > 0.5: + client.send_message("/touch", [sx, sy]) + print(f"👉 STABILER TOUCH bei {sx},{sy} nach {still_time:.2f}s") + prev_touch_time = now + finger_still_start = None + else: + finger_still_start = now + + # IMMER aktualisieren + last_finger_pos = current_pos + + # Finger visualisieren + cv2.circle(frame_touch, (fx, fy), 10, (0, 255, 0), -1) + cv2.putText(frame_touch, f"{sx},{sy}", (fx + 10, fy - 10), + cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) + + now = time.time() + current_pos = (fx, fy) + + if last_finger_pos is None: + # erster Punkt + last_finger_pos = current_pos + finger_still_start = now + else: + dist = math.hypot(current_pos[0] - last_finger_pos[0], + current_pos[1] - last_finger_pos[1]) + + if dist < MOVE_TOLERANCE: + # Finger ist "ruhig" + if finger_still_start is None: + finger_still_start = now + else: + still_time = now - finger_still_start + if still_time >= STILL_REQUIRED and (now - prev_touch_time) > 0.5: + # JETZT: stabiler Touch → sende genau 1 Klick + client.send_message("/touch", [sx, sy]) + print(f"STABILER TOUCH bei {sx},{sy} nach {still_time:.2f}s") + print("SCREEN COORD:", sx, sy) + + prev_touch_time = now + # reset, damit der nächste Touch erst nach neuer Bewegung kommt + finger_still_start = None + else: + # Finger hat sich deutlich bewegt → Timer neu starten + + finger_still_start = now + last_finger_pos = current_pos + else: + # keine Hand → Reset + last_finger_pos = None + finger_still_start = None + + # --------------------------------------- + # CLAP (zwei Hände) + # --------------------------------------- + rgb_g = cv2.cvtColor(frame_gest, cv2.COLOR_BGR2RGB) + res_g = hands_gesture.process(rgb_g) + gh, gw, _ = frame_gest.shape + + if res_g.multi_hand_landmarks and len(res_g.multi_hand_landmarks) == 2: + h1, h2 = res_g.multi_hand_landmarks + + x1 = np.mean([p.x for p in h1.landmark]) * gw + y1 = np.mean([p.y for p in h1.landmark]) * gh + x2 = np.mean([p.x for p in h2.landmark]) * gw + y2 = np.mean([p.y for p in h2.landmark]) * gh + + dist = math.hypot(x2 - x1, y2 - y1) + + if dist < 100 and (time.time() - prev_clap_time) > clap_cooldown: + prev_clap_time = time.time() + client.send_message("/clap", 1) + print("👏 SEND /clap") + cv2.putText(frame_gest, "👏", (int(gw/2)-20, 80), + cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 255), 3) + + cv2.imshow("Touch-Cam", frame_touch) + cv2.imshow("Gesture-Cam", frame_gest) + + if cv2.waitKey(5) & 0xFF == 27: + break + + cam_touch.release() + cam_gesture.release() + cv2.destroyAllWindows() + + +if __name__ == "__main__": + run_gesture_input() diff --git a/old/gesture_input_osc_old.py b/old/gesture_input_osc_old.py new file mode 100644 index 0000000..ed2f2e1 --- /dev/null +++ b/old/gesture_input_osc_old.py @@ -0,0 +1,119 @@ +import cv2 +import mediapipe as mp +import numpy as np +import math, time +from pythonosc import udp_client + +# ------------------------------- +# SETTINGS +# ------------------------------- +TOUCH_CAM_INDEX = 0 # deine Touch-Kamera / oben +GESTURE_CAM_INDEX = 1 # deine Clap / Gesture Kamera / unten + +GAME_SCREEN_WIDTH = 900 # muss zu deinem Pygame-Fenster passen! +GAME_SCREEN_HEIGHT = 600 + +client = udp_client.SimpleUDPClient("127.0.0.1", 5005) + +# ------------------------------- +# MAIN FUNCTION +# ------------------------------- +def run_gesture_input(): + + mp_hands = mp.solutions.hands + mp_draw = mp.solutions.drawing_utils + + hands_touch = mp_hands.Hands(max_num_hands=1, min_detection_confidence=0.6) + hands_gesture = mp_hands.Hands(max_num_hands=2, min_detection_confidence=0.6) + + # Kameras öffnen + cam_touch = cv2.VideoCapture(TOUCH_CAM_INDEX) + cam_gesture = cv2.VideoCapture(GESTURE_CAM_INDEX) + + if not cam_touch.isOpened(): + print("❌ Touch-Kamera konnte NICHT geöffnet werden!") + else: + print(f"✅ Touch-Kamera geöffnet (Index {TOUCH_CAM_INDEX})") + + if not cam_gesture.isOpened(): + print("❌ Gesture-Kamera konnte NICHT geöffnet werden!") + else: + print(f"✅ Gesture-Kamera geöffnet (Index {GESTURE_CAM_INDEX})") + + prev_clap_time = 0 + clap_cooldown = 1.5 + + while True: + + ok1, frame_touch = cam_touch.read() + ok2, frame_gest = cam_gesture.read() + + if not ok1 or not ok2: + print("⚠️ Eine Kamera liefert kein Bild.") + break + + frame_touch = cv2.flip(frame_touch, 1) + frame_gest = cv2.flip(frame_gest, 1) + + # --------------------------------------- + # TOUCH (Zeigefinger) ohne Kalibrierung + # --------------------------------------- + rgb_t = cv2.cvtColor(frame_touch, cv2.COLOR_BGR2RGB) + res_t = hands_touch.process(rgb_t) + + th, tw, _ = frame_touch.shape + + if res_t.multi_hand_landmarks: + lm = res_t.multi_hand_landmarks[0] + mp_draw.draw_landmarks(frame_touch, lm, mp_hands.HAND_CONNECTIONS) + + fx = int(lm.landmark[8].x * tw) + fy = int(lm.landmark[8].y * th) + + # einfache Skalierung auf dein Spiel-Fenster + sx = int(fx * (GAME_SCREEN_WIDTH / tw)) + sy = int(fy * (GAME_SCREEN_HEIGHT / th)) + + # Finger unten? (Touch) + if lm.landmark[8].y > 0.8: + client.send_message("/touch", [sx, sy]) + cv2.putText(frame_touch, f"Touch {sx},{sy}", (40, 60), + cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) + + # --------------------------------------- + # CLAP (zwei Hände) + # --------------------------------------- + rgb_g = cv2.cvtColor(frame_gest, cv2.COLOR_BGR2RGB) + res_g = hands_gesture.process(rgb_g) + + gh, gw, _ = frame_gest.shape + + if res_g.multi_hand_landmarks and len(res_g.multi_hand_landmarks) == 2: + h1, h2 = res_g.multi_hand_landmarks + + x1 = np.mean([p.x for p in h1.landmark]) * gw + y1 = np.mean([p.y for p in h1.landmark]) * gh + x2 = np.mean([p.x for p in h2.landmark]) * gw + y2 = np.mean([p.y for p in h2.landmark]) * gh + + dist = math.hypot(x2 - x1, y2 - y1) + + if dist < 100 and (time.time() - prev_clap_time) > clap_cooldown: + prev_clap_time = time.time() + client.send_message("/clap", 1) + cv2.putText(frame_gest, "👏", (int(gw/2)-20, 80), + cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 255), 3) + + cv2.imshow("Touch-Cam", frame_touch) + cv2.imshow("Gesture-Cam", frame_gest) + + if cv2.waitKey(5) & 0xFF == 27: + break + + cam_touch.release() + cam_gesture.release() + cv2.destroyAllWindows() + + +if __name__ == "__main__": + run_gesture_input() diff --git a/test_touch_area.py b/touch_area.py similarity index 99% rename from test_touch_area.py rename to touch_area.py index 6ee5315..dc10209 100644 --- a/test_touch_area.py +++ b/touch_area.py @@ -3,7 +3,7 @@ from pythonosc import dispatcher, osc_server import threading #python test_touch_area.py SCREEN_WIDTH = 900 -SCREEN_HEIGHT = 600 +SCREEN_HEIGHT = 500 # Letzter Touchpunkt touch_pos = None