IT WORKS 11!!!!!!!!!!!!!!!!!

Es funktioniert und umbennungen
This commit is contained in:
Kristoph Laemmerzahl 2025-12-03 16:08:36 +01:00
parent 316a2be7f0
commit 953a0d294e
11 changed files with 955 additions and 519 deletions

View File

@ -4,7 +4,7 @@
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" /> <excludeFolder url="file://$MODULE_DIR$/.venv" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="jdk" jdkName="Python 3.12 (pythonProject)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
</module> </module>

2
.idea/misc.xml generated
View File

@ -3,5 +3,5 @@
<component name="Black"> <component name="Black">
<option name="sdkName" value="Python 3.12 (Memory GlobalMatch)" /> <option name="sdkName" value="Python 3.12 (Memory GlobalMatch)" />
</component> </component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (Memory GlobalMatch)" project-jdk-type="Python SDK" /> <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (pythonProject)" project-jdk-type="Python SDK" />
</project> </project>

View File

@ -1,448 +1,448 @@
import pygame import pygame
import random import random
import sys import sys
import os import os
import time import time
from pythonosc import dispatcher, osc_server from pythonosc import dispatcher, osc_server
import threading import threading
# ===== OSC Setup ===== # ===== OSC Setup =====
touch_x, touch_y = None, None touch_x, touch_y = None, None
clap_trigger = False clap_trigger = False
def osc_touch(address, x, y): def osc_touch(address, x, y):
"""Wird aufgerufen, wenn /touch x y über OSC empfangen wird.""" """Wird aufgerufen, wenn /touch x y über OSC empfangen wird."""
global touch_x, touch_y global touch_x, touch_y
touch_x, touch_y = x, y touch_x, touch_y = x, y
def osc_clap(address, *args): def osc_clap(address, *args):
"""Wird aufgerufen, wenn /clap empfangen wird.""" """Wird aufgerufen, wenn /clap empfangen wird."""
global clap_trigger global clap_trigger
clap_trigger = True clap_trigger = True
def start_osc_server(): def start_osc_server():
disp = dispatcher.Dispatcher() disp = dispatcher.Dispatcher()
disp.map("/touch", osc_touch) disp.map("/touch", osc_touch)
disp.map("/clap", osc_clap) disp.map("/clap", osc_clap)
# Dein gesture_input_osc sendet an 127.0.0.1:5005 → hier auch 5005 # Dein gesture_input_osc sendet an 127.0.0.1:5005 → hier auch 5005
server = osc_server.ThreadingOSCUDPServer(("127.0.0.1", 5005), disp) server = osc_server.ThreadingOSCUDPServer(("127.0.0.1", 5005), disp)
print(" OSC server läuft auf Port 5005") print(" OSC server läuft auf Port 5005")
server.serve_forever() server.serve_forever()
# Starte den OSC Listener im Hintergrund # Starte den OSC Listener im Hintergrund
threading.Thread(target=start_osc_server, daemon=True).start() threading.Thread(target=start_osc_server, daemon=True).start()
# ------------------------------- # -------------------------------
# Global Match CountryCapital Memory Game # Global Match CountryCapital Memory Game
# ------------------------------- # -------------------------------
# --- Colors & Layout --- # --- Colors & Layout ---
CARD_FRONT_COLOR = (245, 246, 248) CARD_FRONT_COLOR = (245, 246, 248)
CARD_BACK_COLOR = (100, 100, 200) CARD_BACK_COLOR = (100, 100, 200)
MATCH_COLOR = (160, 220, 160) MATCH_COLOR = (160, 220, 160)
TEXT_COLOR = (10, 30, 40) TEXT_COLOR = (10, 30, 40)
CAPITAL_COLOR = (0, 60, 180) CAPITAL_COLOR = (0, 60, 180)
BG_COLOR = (10, 42, 83) BG_COLOR = (10, 42, 83)
BUTTON_FILL = (18, 122, 138) BUTTON_FILL = (18, 122, 138)
BUTTON_BORDER = (255, 255, 255) BUTTON_BORDER = (255, 255, 255)
FPS = 30 FPS = 30
SCREEN_WIDTH, SCREEN_HEIGHT = 900, 600 SCREEN_WIDTH, SCREEN_HEIGHT = 900, 600
# --- Background / Logo --- # --- Background / Logo ---
BASE_DIR = os.path.dirname(os.path.abspath(__file__)) BASE_DIR = os.path.dirname(os.path.abspath(__file__))
BACKGROUND_FILE = os.path.join(BASE_DIR, "GlobalHintergrund.png") BACKGROUND_FILE = os.path.join(BASE_DIR, "GlobalHintergrund.png")
def build_card_back(image_path, size): def build_card_back(image_path, size):
"""Load and scale a logo image to fill card backs.""" """Load and scale a logo image to fill card backs."""
w, h = size w, h = size
surf = pygame.Surface((w, h), pygame.SRCALPHA).convert_alpha() surf = pygame.Surface((w, h), pygame.SRCALPHA).convert_alpha()
if os.path.exists(image_path): if os.path.exists(image_path):
try: try:
img = pygame.image.load(image_path).convert_alpha() img = pygame.image.load(image_path).convert_alpha()
img = pygame.transform.smoothscale(img, (w, h)) img = pygame.transform.smoothscale(img, (w, h))
surf.blit(img, (0, 0)) surf.blit(img, (0, 0))
except pygame.error as e: except pygame.error as e:
print(f" Error loading {image_path}: {e}") print(f" Error loading {image_path}: {e}")
surf.fill((127, 127, 200)) surf.fill((127, 127, 200))
else: else:
print(f"Image not found: {image_path}") print(f"Image not found: {image_path}")
surf.fill((127, 127, 200)) surf.fill((127, 127, 200))
return surf return surf
class MemoryGame: class MemoryGame:
def __init__(self): def __init__(self):
# Game Data # Game Data
self.deck = [] self.deck = []
self.pair_map = {} self.pair_map = {}
self.matched = [] self.matched = []
self.revealed = [] self.revealed = []
self.cards = [] self.cards = []
self.card_rects = [] self.card_rects = []
self.selected = [] self.selected = []
self.scores = [0, 0] self.scores = [0, 0]
self.current_player = 0 self.current_player = 0
self.found_pairs = 0 self.found_pairs = 0
self.total_pairs = 0 self.total_pairs = 0
# UI and States # UI and States
self.font = None self.font = None
self.small_font = None self.small_font = None
self.buttons = [] self.buttons = []
self.running = True self.running = True
self.state = "mode" self.state = "mode"
self.selected_continents = [] self.selected_continents = []
self.level = None self.level = None
self.pair_count = 6 self.pair_count = 6
self.player_mode = 2 self.player_mode = 2
# "Bestätigungsphase" = wir warten auf Klatschen # "Bestätigungsphase" = wir warten auf Klatschen
self.awaiting_confirmation = False self.awaiting_confirmation = False
self.confirmation_start_time = None self.confirmation_start_time = None
self.confirmation_time_limit = 5 # Sekunden self.confirmation_time_limit = 5 # Sekunden
# Visuals # Visuals
self.card_back = None self.card_back = None
# ------------------------------- # -------------------------------
# File Loading # File Loading
# ------------------------------- # -------------------------------
def load_cards(self, filename): def load_cards(self, filename):
if not os.path.exists(filename): if not os.path.exists(filename):
print(f" File not found: {filename}") print(f" File not found: {filename}")
return [] return []
pairs = [] pairs = []
with open(filename, "r", encoding="utf-8") as f: with open(filename, "r", encoding="utf-8") as f:
for line in f: for line in f:
parts = line.strip().split() parts = line.strip().split()
if len(parts) >= 2: if len(parts) >= 2:
pairs.append((parts[0], parts[1])) pairs.append((parts[0], parts[1]))
return pairs return pairs
def prepare_deck(self): def prepare_deck(self):
self.deck = [] self.deck = []
for continent in self.selected_continents: for continent in self.selected_continents:
base = continent base = continent
if self.level == "Easy": if self.level == "Easy":
self.deck += self.load_cards(base + "-major.txt") self.deck += self.load_cards(base + "-major.txt")
elif self.level == "Normal": elif self.level == "Normal":
self.deck += self.load_cards(base + "-major.txt") self.deck += self.load_cards(base + "-major.txt")
self.deck += self.load_cards(base + "-Minor.txt") self.deck += self.load_cards(base + "-Minor.txt")
elif self.level == "Hard": elif self.level == "Hard":
self.deck += self.load_cards(base + "-major.txt") self.deck += self.load_cards(base + "-major.txt")
self.deck += self.load_cards(base + "-Minor.txt") self.deck += self.load_cards(base + "-Minor.txt")
self.deck += self.load_cards(base + "-Dependent.txt") self.deck += self.load_cards(base + "-Dependent.txt")
if not self.deck: if not self.deck:
print("No cards loaded. Check your text files.") print("No cards loaded. Check your text files.")
sys.exit() sys.exit()
random.shuffle(self.deck) random.shuffle(self.deck)
self.deck = self.deck[:self.pair_count] self.deck = self.deck[:self.pair_count]
# ------------------------------- # -------------------------------
# Setup & Layout # Setup & Layout
# ------------------------------- # -------------------------------
def setup_game(self): def setup_game(self):
self.cards = [] self.cards = []
self.pair_map = {} self.pair_map = {}
for country, capital in self.deck: for country, capital in self.deck:
self.cards.append({"text": country, "type": "country"}) self.cards.append({"text": country, "type": "country"})
self.cards.append({"text": capital, "type": "capital"}) self.cards.append({"text": capital, "type": "capital"})
self.pair_map[country] = capital self.pair_map[country] = capital
self.pair_map[capital] = country self.pair_map[capital] = country
random.shuffle(self.cards) random.shuffle(self.cards)
self.matched = [False] * len(self.cards) self.matched = [False] * len(self.cards)
self.revealed = [False] * len(self.cards) self.revealed = [False] * len(self.cards)
self.total_pairs = len(self.deck) self.total_pairs = len(self.deck)
self.selected = [] self.selected = []
self.found_pairs = 0 self.found_pairs = 0
self.current_player = 0 self.current_player = 0
self.scores = [0, 0] self.scores = [0, 0]
self.awaiting_confirmation = False self.awaiting_confirmation = False
self.confirmation_start_time = None self.confirmation_start_time = None
cols = 4 cols = 4
rows = (len(self.cards) + cols - 1) // cols rows = (len(self.cards) + cols - 1) // cols
margin = 10 margin = 10
card_width = (SCREEN_WIDTH - (cols + 1) * margin) // cols card_width = (SCREEN_WIDTH - (cols + 1) * margin) // cols
card_height = (SCREEN_HEIGHT - (rows + 1) * margin - 100) // rows card_height = (SCREEN_HEIGHT - (rows + 1) * margin - 100) // rows
self.card_back = build_card_back(BACKGROUND_FILE, (card_width, card_height)) self.card_back = build_card_back(BACKGROUND_FILE, (card_width, card_height))
y_offset = 80 y_offset = 80
self.card_rects = [] self.card_rects = []
for i in range(len(self.cards)): for i in range(len(self.cards)):
col = i % cols col = i % cols
row = i // cols row = i // cols
x = margin + col * (card_width + margin) x = margin + col * (card_width + margin)
y = y_offset + margin + row * (card_height + margin) y = y_offset + margin + row * (card_height + margin)
self.card_rects.append(pygame.Rect(x, y, card_width, card_height)) self.card_rects.append(pygame.Rect(x, y, card_width, card_height))
# ------------------------------- # -------------------------------
# Drawing # Drawing
# ------------------------------- # -------------------------------
def draw_menu(self, screen, title, options): def draw_menu(self, screen, title, options):
screen.fill(BG_COLOR) screen.fill(BG_COLOR)
title_text = self.font.render(title, True, (255, 255, 255)) title_text = self.font.render(title, True, (255, 255, 255))
screen.blit(title_text, (SCREEN_WIDTH // 2 - title_text.get_width() // 2, 100)) screen.blit(title_text, (SCREEN_WIDTH // 2 - title_text.get_width() // 2, 100))
self.buttons = [] self.buttons = []
for i, option in enumerate(options): for i, option in enumerate(options):
rect = pygame.Rect(SCREEN_WIDTH // 2 - 150, 200 + i * 70, 300, 50) 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_FILL, rect, border_radius=10)
pygame.draw.rect(screen, BUTTON_BORDER, rect, 2, border_radius=10) pygame.draw.rect(screen, BUTTON_BORDER, rect, 2, border_radius=10)
text = self.font.render(option, True, (255, 255, 255)) text = self.font.render(option, True, (255, 255, 255))
screen.blit(text, text.get_rect(center=rect.center)) screen.blit(text, text.get_rect(center=rect.center))
self.buttons.append((rect, option)) self.buttons.append((rect, option))
pygame.display.flip() pygame.display.flip()
def draw_game(self, screen): def draw_game(self, screen):
screen.fill(BG_COLOR) screen.fill(BG_COLOR)
if self.player_mode == 2: if self.player_mode == 2:
title = self.font.render(f"Player {self.current_player + 1}'s turn", True, (255, 255, 255)) 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)) score_text = self.font.render(f"Scores: P1={self.scores[0]} P2={self.scores[1]}", True, (220, 230, 235))
else: else:
title = self.font.render("Single Player Mode", True, (255, 255, 255)) 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)) score_text = self.font.render(f"Score: {self.scores[0]}", True, (220, 230, 235))
screen.blit(title, (20, 20)) screen.blit(title, (20, 20))
screen.blit(score_text, (20, 50)) screen.blit(score_text, (20, 50))
# Hinweis: wenn 2 Karten offen sind, warte auf Clap # Hinweis: wenn 2 Karten offen sind, warte auf Clap
if self.awaiting_confirmation and len(self.selected) == 2: 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)) 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)) screen.blit(hint, (SCREEN_WIDTH // 2 - hint.get_width() // 2, 80))
for i, rect in enumerate(self.card_rects): for i, rect in enumerate(self.card_rects):
if self.matched[i]: if self.matched[i]:
pygame.draw.rect(screen, MATCH_COLOR, rect) pygame.draw.rect(screen, MATCH_COLOR, rect)
elif self.revealed[i]: elif self.revealed[i]:
pygame.draw.rect(screen, CARD_FRONT_COLOR, rect) pygame.draw.rect(screen, CARD_FRONT_COLOR, rect)
else: else:
screen.blit(self.card_back, rect.topleft) screen.blit(self.card_back, rect.topleft)
pygame.draw.rect(screen, (0, 0, 0), rect, 2) pygame.draw.rect(screen, (0, 0, 0), rect, 2)
if self.revealed[i] or self.matched[i]: if self.revealed[i] or self.matched[i]:
card = self.cards[i] card = self.cards[i]
text_color = CAPITAL_COLOR if card["type"] == "capital" else TEXT_COLOR text_color = CAPITAL_COLOR if card["type"] == "capital" else TEXT_COLOR
text = self.font.render(card["text"], True, text_color) text = self.font.render(card["text"], True, text_color)
screen.blit(text, text.get_rect(center=(rect.centerx, rect.centery - 10))) screen.blit(text, text.get_rect(center=(rect.centerx, rect.centery - 10)))
label = self.small_font.render( label = self.small_font.render(
"(Capital)" if card["type"] == "capital" else "(Country)", True, (80, 80, 80) "(Capital)" if card["type"] == "capital" else "(Country)", True, (80, 80, 80)
) )
screen.blit(label, label.get_rect(center=(rect.centerx, rect.centery + 20))) screen.blit(label, label.get_rect(center=(rect.centerx, rect.centery + 20)))
pygame.display.flip() pygame.display.flip()
# ------------------------------- # -------------------------------
# Interaction Logic # Interaction Logic
# ------------------------------- # -------------------------------
def handle_click(self, pos): def handle_click(self, pos):
# ✅ Blockiere Touch über dem Spielfeld (Menü-Bereich oben) # ✅ Blockiere Touch über dem Spielfeld (Menü-Bereich oben)
if self.state == "game" and pos[1] < 80: if self.state == "game" and pos[1] < 80:
return return
# Menüs: mit Touch durchklicken # Menüs: mit Touch durchklicken
if self.state in ["mode", "continent", "americas", "difficulty", "pairs", "timer"]: if self.state in ["mode", "continent", "americas", "difficulty", "pairs", "timer"]:
for rect, option in self.buttons: for rect, option in self.buttons:
if rect.collidepoint(pos): if rect.collidepoint(pos):
if self.state == "mode": if self.state == "mode":
self.player_mode = 1 if option == "1 Player" else 2 self.player_mode = 1 if option == "1 Player" else 2
self.state = "continent" self.state = "continent"
elif self.state == "continent": elif self.state == "continent":
if option == "Americas": if option == "Americas":
self.state = "americas" self.state = "americas"
elif option == "All Continents": elif option == "All Continents":
self.selected_continents = ["Europe", "Asia", "Africa", self.selected_continents = ["Europe", "Asia", "Africa",
"Oceania", "North-America", "South-America"] "Oceania", "North-America", "South-America"]
self.state = "difficulty" self.state = "difficulty"
else: else:
self.selected_continents = [option] self.selected_continents = [option]
self.state = "difficulty" self.state = "difficulty"
elif self.state == "americas": elif self.state == "americas":
if option == "North-America": if option == "North-America":
self.selected_continents = ["North-America"] self.selected_continents = ["North-America"]
elif option == "South-America": elif option == "South-America":
self.selected_continents = ["South-America"] self.selected_continents = ["South-America"]
elif option == "Americas": elif option == "Americas":
self.selected_continents = ["North-America", "South-America"] self.selected_continents = ["North-America", "South-America"]
self.state = "difficulty" self.state = "difficulty"
elif self.state == "difficulty": elif self.state == "difficulty":
self.level = option self.level = option
self.state = "pairs" self.state = "pairs"
elif self.state == "pairs": elif self.state == "pairs":
self.pair_count = int(option) self.pair_count = int(option)
self.state = "timer" self.state = "timer"
elif self.state == "timer": elif self.state == "timer":
self.confirmation_time_limit = int(option.replace("s", "")) self.confirmation_time_limit = int(option.replace("s", ""))
self.prepare_deck() self.prepare_deck()
self.setup_game() self.setup_game()
self.state = "game" self.state = "game"
return return
# Spiel: Karten nur anklicken, wenn wir NICHT gerade auf Clap warten # Spiel: Karten nur anklicken, wenn wir NICHT gerade auf Clap warten
elif self.state == "game": elif self.state == "game":
if self.awaiting_confirmation or len(self.selected) >= 2: if self.awaiting_confirmation or len(self.selected) >= 2:
# Während wir auf Klatschen warten, keine weiteren Karten öffnen # Während wir auf Klatschen warten, keine weiteren Karten öffnen
return return
for i, rect in enumerate(self.card_rects): for i, rect in enumerate(self.card_rects):
if rect.collidepoint(pos) and not self.revealed[i] and not self.matched[i]: if rect.collidepoint(pos) and not self.revealed[i] and not self.matched[i]:
self.revealed[i] = True self.revealed[i] = True
self.selected.append(i) self.selected.append(i)
return return
# ------------------------------- # -------------------------------
# OSC Input Processing # OSC Input Processing
# ------------------------------- # -------------------------------
def process_osc_input(self): def process_osc_input(self):
"""Verarbeite die aktuellen OSC-Eingaben (Touch & Clap).""" """Verarbeite die aktuellen OSC-Eingaben (Touch & Clap)."""
global touch_x, touch_y, clap_trigger global touch_x, touch_y, clap_trigger
# TOUCH: als Klick ins Spiel (Menü oder Karte) # TOUCH: als Klick ins Spiel (Menü oder Karte)
if touch_x is not None and touch_y is not None: if touch_x is not None and touch_y is not None:
pos = (int(touch_x), int(touch_y)) pos = (int(touch_x), int(touch_y))
self.handle_click(pos) self.handle_click(pos)
touch_x, touch_y = None, None # Reset touch_x, touch_y = None, None # Reset
# CLAP: wenn 2 Karten offen und wir warten → Paar auswerten # CLAP: wenn 2 Karten offen und wir warten → Paar auswerten
if clap_trigger: if clap_trigger:
if self.awaiting_confirmation and len(self.selected) == 2: if self.awaiting_confirmation and len(self.selected) == 2:
self.resolve_pair() self.resolve_pair()
clap_trigger = False clap_trigger = False
# ------------------------------- # -------------------------------
# Paar auswerten (nach Clap oder Timeout) # Paar auswerten (nach Clap oder Timeout)
# ------------------------------- # -------------------------------
def resolve_pair(self): def resolve_pair(self):
"""Prüft das aktuelle Kartenpaar und aktualisiert Punkte / Spieler.""" """Prüft das aktuelle Kartenpaar und aktualisiert Punkte / Spieler."""
if len(self.selected) != 2: if len(self.selected) != 2:
return return
a, b = self.selected a, b = self.selected
text_a = self.cards[a]["text"] text_a = self.cards[a]["text"]
text_b = self.cards[b]["text"] text_b = self.cards[b]["text"]
is_match = self.pair_map.get(text_a) == text_b is_match = self.pair_map.get(text_a) == text_b
if is_match: if is_match:
self.matched[a] = self.matched[b] = True self.matched[a] = self.matched[b] = True
self.scores[self.current_player] += 1 self.scores[self.current_player] += 1
self.found_pairs += 1 self.found_pairs += 1
else: else:
# Falsches Paar: Karten wieder umdrehen, Punkt abziehen # Falsches Paar: Karten wieder umdrehen, Punkt abziehen
self.revealed[a] = self.revealed[b] = False self.revealed[a] = self.revealed[b] = False
self.scores[self.current_player] -= 1 self.scores[self.current_player] -= 1
# Reset für nächste Runde # Reset für nächste Runde
self.selected = [] self.selected = []
self.awaiting_confirmation = False self.awaiting_confirmation = False
self.confirmation_start_time = None self.confirmation_start_time = None
# Spielerwechsel immer nach einem Paar (wie vorher) # Spielerwechsel immer nach einem Paar (wie vorher)
if self.player_mode == 2: if self.player_mode == 2:
self.current_player = 1 - self.current_player self.current_player = 1 - self.current_player
# ------------------------------- # -------------------------------
# Game Logic # Game Logic
# ------------------------------- # -------------------------------
def check_selected(self): def check_selected(self):
if self.state != "game": if self.state != "game":
return return
# Wenn zwei Karten offen sind → Warte auf Clap # Wenn zwei Karten offen sind → Warte auf Clap
if len(self.selected) == 2 and not self.awaiting_confirmation: if len(self.selected) == 2 and not self.awaiting_confirmation:
self.awaiting_confirmation = True self.awaiting_confirmation = True
self.confirmation_start_time = time.time() self.confirmation_start_time = time.time()
# Timeout: wenn zu lange kein Clap → Karten zurückdrehen, Spielerwechsel # Timeout: wenn zu lange kein Clap → Karten zurückdrehen, Spielerwechsel
if self.awaiting_confirmation and self.confirmation_start_time is not None: if self.awaiting_confirmation and self.confirmation_start_time is not None:
if time.time() - self.confirmation_start_time > self.confirmation_time_limit: if time.time() - self.confirmation_start_time > self.confirmation_time_limit:
if len(self.selected) == 2: if len(self.selected) == 2:
a, b = self.selected a, b = self.selected
self.revealed[a] = self.revealed[b] = False self.revealed[a] = self.revealed[b] = False
self.selected = [] self.selected = []
self.awaiting_confirmation = False self.awaiting_confirmation = False
self.confirmation_start_time = None self.confirmation_start_time = None
if self.player_mode == 2: if self.player_mode == 2:
self.current_player = 1 - self.current_player self.current_player = 1 - self.current_player
# ------------------------------- # -------------------------------
# Winner Screen # Winner Screen
# ------------------------------- # -------------------------------
def display_winner(self, screen): def display_winner(self, screen):
if self.player_mode == 1: if self.player_mode == 1:
text = f" Final Score: {self.scores[0]}" text = f" Final Score: {self.scores[0]}"
else: else:
if self.scores[0] > self.scores[1]: if self.scores[0] > self.scores[1]:
text = " Player 1 Wins!" text = " Player 1 Wins!"
elif self.scores[1] > self.scores[0]: elif self.scores[1] > self.scores[0]:
text = " Player 2 Wins!" text = " Player 2 Wins!"
else: else:
text = " Draw!" text = " Draw!"
win_text = self.font.render(text, True, (255, 255, 0)) win_text = self.font.render(text, True, (255, 255, 0))
rect = win_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)) rect = win_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))
screen.blit(win_text, rect) screen.blit(win_text, rect)
pygame.display.flip() pygame.display.flip()
# ------------------------------- # -------------------------------
# Main Loop # Main Loop
# ------------------------------- # -------------------------------
def run(self): def run(self):
pygame.init() pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Global Match CountryCapital Memory Game") pygame.display.set_caption("Global Match CountryCapital Memory Game")
clock = pygame.time.Clock() clock = pygame.time.Clock()
self.font = pygame.font.SysFont(None, 32) self.font = pygame.font.SysFont(None, 32)
self.small_font = pygame.font.SysFont(None, 22) self.small_font = pygame.font.SysFont(None, 22)
while self.running: while self.running:
for event in pygame.event.get(): for event in pygame.event.get():
if event.type == pygame.QUIT: if event.type == pygame.QUIT:
self.running = False self.running = False
# Maus komplett ignorieren (kein Klick mehr) # Maus komplett ignorieren (kein Klick mehr)
# elif event.type == pygame.MOUSEBUTTONDOWN: # elif event.type == pygame.MOUSEBUTTONDOWN:
# self.handle_click(event.pos) # self.handle_click(event.pos)
# Wenn du zum Debuggen Maus willst, obige Zeilen entkommentieren. # Wenn du zum Debuggen Maus willst, obige Zeilen entkommentieren.
# HIER werden jetzt *jeden Frame* die OSC-Eingaben verarbeitet # HIER werden jetzt *jeden Frame* die OSC-Eingaben verarbeitet
self.process_osc_input() self.process_osc_input()
# Menü + Spiel zeichnen # Menü + Spiel zeichnen
if self.state == "mode": if self.state == "mode":
self.draw_menu(screen, "Select Player Mode", ["1 Player", "2 Players"]) self.draw_menu(screen, "Select Player Mode", ["1 Player", "2 Players"])
elif self.state == "continent": elif self.state == "continent":
self.draw_menu(screen, "Select Continent", self.draw_menu(screen, "Select Continent",
["Europe", "Americas", "Asia", "Africa", "Oceania", "All Continents"]) ["Europe", "Americas", "Asia", "Africa", "Oceania", "All Continents"])
elif self.state == "americas": elif self.state == "americas":
self.draw_menu(screen, "Select Region", ["North-America", "South-America", "Americas"]) self.draw_menu(screen, "Select Region", ["North-America", "South-America", "Americas"])
elif self.state == "difficulty": elif self.state == "difficulty":
self.draw_menu(screen, "Select Difficulty", ["Easy", "Normal", "Hard"]) self.draw_menu(screen, "Select Difficulty", ["Easy", "Normal", "Hard"])
elif self.state == "pairs": elif self.state == "pairs":
self.draw_menu(screen, "Select Number of Pairs", ["4", "6", "8", "10", "12"]) self.draw_menu(screen, "Select Number of Pairs", ["4", "6", "8", "10", "12"])
elif self.state == "timer": elif self.state == "timer":
self.draw_menu(screen, "Select Confirmation Time", ["3s", "5s", "8s", "10s"]) self.draw_menu(screen, "Select Confirmation Time", ["3s", "5s", "8s", "10s"])
elif self.state == "game": elif self.state == "game":
self.draw_game(screen) self.draw_game(screen)
self.check_selected() self.check_selected()
if self.found_pairs == self.total_pairs: if self.found_pairs == self.total_pairs:
self.display_winner(screen) self.display_winner(screen)
pygame.time.wait(4000) pygame.time.wait(4000)
self.running = False self.running = False
clock.tick(FPS) clock.tick(FPS)
pygame.quit() pygame.quit()
sys.exit() sys.exit()
# ------------------------------- # -------------------------------
# Run # Run
# ------------------------------- # -------------------------------
if __name__ == "__main__": if __name__ == "__main__":
game = MemoryGame() game = MemoryGame()
game.run() game.run()

View File

@ -2,7 +2,7 @@ import cv2
import json import json
import numpy as np import numpy as np
CAM_INDEX = 0 CAM_INDEX = 1
OUTPUT_FILE = "calibration.json" OUTPUT_FILE = "calibration.json"
# Reihenfolge: P1=oben links, P2=oben rechts, P3=unten rechts, P4=unten links # Reihenfolge: P1=oben links, P2=oben rechts, P3=unten rechts, P4=unten links
@ -75,7 +75,7 @@ def main():
ok, frame = cap.read() ok, frame = cap.read()
if not ok: if not ok:
break break
frame = cv2.flip(frame, 1) frame = cv2.flip(frame, -1)#<----------------------------------------------------------------Flip old:frame = cv2.flip(frame, 1)
h, w, _ = frame.shape h, w, _ = frame.shape

View File

@ -1 +1 @@
[[164, 384], [370, 425], [444, 487], [447, 393]] [[24, 118], [630, 135], [627, 459], [36, 461]]

View File

@ -1,23 +1,21 @@
import cv2 import cv2
import mediapipe as mp import mediapipe as mp
import numpy as np import numpy as np
import math, time import math, time, json
from pythonosc import udp_client from pythonosc import udp_client
# ------------------------------- # -------------------------------
# SETTINGS # SETTINGS
# ------------------------------- # -------------------------------
TOUCH_CAM_INDEX = 0 # deine Touch-Kamera TOUCH_CAM_INDEX = 1 # deine Touch-Kamera
GESTURE_CAM_INDEX = 2 # deine Clap / Gesture 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 GAME_SCREEN_HEIGHT = 600
# Wie "streng" ist der Touch? STILL_REQUIRED = 1.0 # Sekunden die der Finger stabil sein muss
STILL_REQUIRED = 1.0 # Sekunden, die der Finger fast still sein muss MOVE_TOLERANCE = 25 # Bewegungsschwelle (Pixel)
MOVE_TOLERANCE = 25 # maximal erlaubte Bewegung (Pixel)
# OSC Client → sendet ans Spiel
client = udp_client.SimpleUDPClient("127.0.0.1", 5005) client = udp_client.SimpleUDPClient("127.0.0.1", 5005)
# Globale Zustände # Globale Zustände
@ -26,6 +24,46 @@ finger_still_start = None
prev_touch_time = 0.0 prev_touch_time = 0.0
prev_clap_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(): def run_gesture_input():
global last_finger_pos, finger_still_start 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_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) hands_gesture = mp_hands.Hands(max_num_hands=2, min_detection_confidence=0.6)
# Kameras öffnen cam_touch = cv2.VideoCapture(TOUCH_CAM_INDEX)#<--------------------------------------------------------------------------Flip old:frame_touch = cv2.flip(frame_touch, 1)
cam_touch = cv2.VideoCapture(TOUCH_CAM_INDEX)
cam_gesture = cv2.VideoCapture(GESTURE_CAM_INDEX) cam_gesture = cv2.VideoCapture(GESTURE_CAM_INDEX)
if not cam_touch.isOpened(): if not cam_touch.isOpened():
print(" Touch-Kamera konnte NICHT geöffnet werden!") print(" Touch-Kamera konnte NICHT geöffnet werden!")
else: else:
print(f"Touch-Kamera geöffnet (Index {TOUCH_CAM_INDEX})") print(f"Touch-Kamera geöffnet (Index {TOUCH_CAM_INDEX})")
if not cam_gesture.isOpened(): if not cam_gesture.isOpened():
print(" Gesture-Kamera konnte NICHT geöffnet werden!") print(" Gesture-Kamera konnte NICHT geöffnet werden!")
else: else:
print(f"Gesture-Kamera geöffnet (Index {GESTURE_CAM_INDEX})") print(f"Gesture-Kamera geöffnet (Index {GESTURE_CAM_INDEX})")
@ -58,24 +95,24 @@ def run_gesture_input():
ok2, frame_gest = cam_gesture.read() ok2, frame_gest = cam_gesture.read()
if not ok1 or not ok2: if not ok1 or not ok2:
print(" Eine Kamera liefert kein Bild.") print(" Eine Kamera liefert kein Bild.")
break break
frame_touch = cv2.flip(frame_touch, 1) frame_touch = cv2.flip(frame_touch, -1)
frame_gest = cv2.flip(frame_gest, 1) frame_gest = cv2.flip(frame_gest, 1)
# ---------------------------------------
# TOUCH (Zeigefinger) mit STILLSTAND
# ---------------------------------------
rgb_t = cv2.cvtColor(frame_touch, cv2.COLOR_BGR2RGB) rgb_t = cv2.cvtColor(frame_touch, cv2.COLOR_BGR2RGB)
res_t = hands_touch.process(rgb_t) 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: if res_t.multi_hand_landmarks:
lm = res_t.multi_hand_landmarks[0] lm = res_t.multi_hand_landmarks[0]
mp_draw.draw_landmarks(frame_touch, lm, mp_hands.HAND_CONNECTIONS) 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: if lm.landmark[8].y < lm.landmark[5].y:
last_finger_pos = None last_finger_pos = None
finger_still_start = None finger_still_start = None
@ -84,15 +121,14 @@ def run_gesture_input():
fx = int(lm.landmark[8].x * tw) fx = int(lm.landmark[8].x * tw)
fy = int(lm.landmark[8].y * th) fy = int(lm.landmark[8].y * th)
sx = int(fx * (GAME_SCREEN_WIDTH / tw)) # → Homographie anwenden
sy = int(fy * (GAME_SCREEN_HEIGHT / th)) sx, sy = map_point_homography(fx, fy)
now = time.time() now = time.time()
current_pos = (fx, fy) current_pos = (fx, fy)
# erster Messpunkt # erster Messpunkt
if last_finger_pos is None: if last_finger_pos is None:
#erster Punkt
last_finger_pos = current_pos last_finger_pos = current_pos
finger_still_start = now finger_still_start = now
else: else:
@ -100,66 +136,31 @@ def run_gesture_input():
current_pos[1] - last_finger_pos[1]) current_pos[1] - last_finger_pos[1])
if dist < MOVE_TOLERANCE: if dist < MOVE_TOLERANCE:
#Finger ist "ruhig"
if finger_still_start is None: if finger_still_start is None:
finger_still_start = now finger_still_start = now
else: else:
still_time = now - finger_still_start still_time = now - finger_still_start
if still_time >= STILL_REQUIRED and (now - prev_touch_time) > 0.5: if still_time >= STILL_REQUIRED and (now - prev_touch_time) > 0.5:
client.send_message("/touch", [sx, sy]) 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 prev_touch_time = now
finger_still_start = None finger_still_start = None
else: else:
finger_still_start = now 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.circle(frame_touch, (fx, fy), 10, (0, 255, 0), -1)
cv2.putText(frame_touch, f"{sx},{sy}", (fx + 10, fy - 10), 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: else:
# keine Hand → Reset
last_finger_pos = None last_finger_pos = None
finger_still_start = None finger_still_start = None
# --------------------------------------- # -------------------------------------------------------------
# CLAP (zwei Hände) # GESTURE detection (clap)
# --------------------------------------- # -------------------------------------------------------------
rgb_g = cv2.cvtColor(frame_gest, cv2.COLOR_BGR2RGB) rgb_g = cv2.cvtColor(frame_gest, cv2.COLOR_BGR2RGB)
res_g = hands_gesture.process(rgb_g) res_g = hands_gesture.process(rgb_g)
gh, gw, _ = frame_gest.shape gh, gw, _ = frame_gest.shape
@ -179,7 +180,7 @@ def run_gesture_input():
client.send_message("/clap", 1) client.send_message("/clap", 1)
print("👏 SEND /clap") print("👏 SEND /clap")
cv2.putText(frame_gest, "👏", (int(gw/2)-20, 80), 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("Touch-Cam", frame_touch)
cv2.imshow("Gesture-Cam", frame_gest) cv2.imshow("Gesture-Cam", frame_gest)

View File

@ -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()

196
old/gesture_input_osc.py Normal file
View File

@ -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()

View File

@ -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()

View File

@ -3,7 +3,7 @@ from pythonosc import dispatcher, osc_server
import threading import threading
#python test_touch_area.py #python test_touch_area.py
SCREEN_WIDTH = 900 SCREEN_WIDTH = 900
SCREEN_HEIGHT = 600 SCREEN_HEIGHT = 500
# Letzter Touchpunkt # Letzter Touchpunkt
touch_pos = None touch_pos = None