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 0000000..03d95e3
Binary files /dev/null and b/__pycache__/test_touch_area.cpython-312-pytest-9.0.1.pyc differ
diff --git a/calibrate_touch_mouse_only.py b/calibrate_touch_mouse_only.py
index 9a6521b..d26ffc1 100644
--- a/calibrate_touch_mouse_only.py
+++ b/calibrate_touch_mouse_only.py
@@ -2,7 +2,7 @@ import cv2
import json
import numpy as np
-CAM_INDEX = 0
+CAM_INDEX = 1
OUTPUT_FILE = "calibration.json"
# Reihenfolge: P1=oben links, P2=oben rechts, P3=unten rechts, P4=unten links
@@ -75,7 +75,7 @@ def main():
ok, frame = cap.read()
if not ok:
break
- frame = cv2.flip(frame, 1)
+ frame = cv2.flip(frame, -1)#<----------------------------------------------------------------Flip old:frame = cv2.flip(frame, 1)
h, w, _ = frame.shape
diff --git a/calibration.json b/calibration.json
index 59ba9c3..0bf3ef6 100644
--- a/calibration.json
+++ b/calibration.json
@@ -1 +1 @@
-[[164, 384], [370, 425], [444, 487], [447, 393]]
\ No newline at end of file
+[[24, 118], [630, 135], [627, 459], [36, 461]]
\ No newline at end of file
diff --git a/gesture_input_osc.py b/gesture_input_osc.py
index 4d94042..737de08 100644
--- a/gesture_input_osc.py
+++ b/gesture_input_osc.py
@@ -1,23 +1,21 @@
import cv2
import mediapipe as mp
import numpy as np
-import math, time
+import math, time, json
from pythonosc import udp_client
# -------------------------------
# SETTINGS
# -------------------------------
-TOUCH_CAM_INDEX = 0 # deine Touch-Kamera
-GESTURE_CAM_INDEX = 2 # deine Clap / Gesture Kamera
+TOUCH_CAM_INDEX = 1 # deine Touch-Kamera
+GESTURE_CAM_INDEX = 0 # Clap/Gesture Kamera
-GAME_SCREEN_WIDTH = 900 # muss zu deinem Pygame-Fenster passen!
+GAME_SCREEN_WIDTH = 900
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)
+STILL_REQUIRED = 1.0 # Sekunden die der Finger stabil sein muss
+MOVE_TOLERANCE = 25 # Bewegungsschwelle (Pixel)
-# OSC Client → sendet ans Spiel
client = udp_client.SimpleUDPClient("127.0.0.1", 5005)
# Globale Zustände
@@ -26,6 +24,46 @@ finger_still_start = None
prev_touch_time = 0.0
prev_clap_time = 0.0
+# -------------------------------------
+# KALIBRIERUNG LADEN + HOMOGRAPHIE
+# -------------------------------------
+try:
+ with open("calibration.json", "r") as f:
+ CALIB_POINTS = json.load(f)
+ print("📐 Kalibrierung geladen:", CALIB_POINTS)
+except:
+ CALIB_POINTS = None
+ print("⚠️ Keine Kalibrierung gefunden – benutze Rohkoordinaten!")
+
+H = None
+if CALIB_POINTS is not None:
+ src = np.array(CALIB_POINTS, dtype=np.float32)
+ dst = np.array([
+ [0, 0],
+ [GAME_SCREEN_WIDTH, 0],
+ [GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT],
+ [0, GAME_SCREEN_HEIGHT]
+ ], dtype=np.float32)
+
+ H, _ = cv2.findHomography(src, dst)
+ print("📐 Homographie-Matrix berechnet!")
+
+
+def map_point_homography(x, y):
+ """ Wandelt Kamera-Koordinaten → Bildschirmkoordinaten um """
+ global H
+
+ if H is None:
+ # fallback: KEINE Skalierung (Variante 1 bedeutet reines Homography)
+ return int(x), int(y)
+
+ p = np.array([[[x, y]]], dtype=np.float32)
+ mapped = cv2.perspectiveTransform(p, H)[0][0]
+
+ return int(mapped[0]), int(mapped[1])
+
+
+# -----------------------------------------------------------------
def run_gesture_input():
global last_finger_pos, finger_still_start
@@ -37,17 +75,16 @@ def run_gesture_input():
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_touch = cv2.VideoCapture(TOUCH_CAM_INDEX)#<--------------------------------------------------------------------------Flip old:frame_touch = cv2.flip(frame_touch, 1)
cam_gesture = cv2.VideoCapture(GESTURE_CAM_INDEX)
if not cam_touch.isOpened():
- print(" Touch-Kamera konnte NICHT geöffnet werden!")
+ 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!")
+ print("❌ Gesture-Kamera konnte NICHT geöffnet werden!")
else:
print(f"Gesture-Kamera geöffnet (Index {GESTURE_CAM_INDEX})")
@@ -58,24 +95,24 @@ def run_gesture_input():
ok2, frame_gest = cam_gesture.read()
if not ok1 or not ok2:
- print(" Eine Kamera liefert kein Bild.")
+ print("❌ Eine Kamera liefert kein Bild.")
break
- frame_touch = cv2.flip(frame_touch, 1)
+ 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
+ th, tw, _ = frame_touch.shape
+ # -------------------------------------------------------------
+ # TOUCH detection
+ # -------------------------------------------------------------
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)
+ # Finger zeigt nach unten: landmark 8 tiefer als 5
if lm.landmark[8].y < lm.landmark[5].y:
last_finger_pos = None
finger_still_start = None
@@ -84,15 +121,14 @@ def run_gesture_input():
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))
+ # → Homographie anwenden
+ sx, sy = map_point_homography(fx, fy)
now = time.time()
current_pos = (fx, fy)
- # erster Messpunkt
+ # erster Messpunkt
if last_finger_pos is None:
- #erster Punkt
last_finger_pos = current_pos
finger_still_start = now
else:
@@ -100,66 +136,31 @@ def run_gesture_input():
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")
+ 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