Files

149 lines
4.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Tastatur und Controller → abstrakte Aktionen.
Die Aktionen sind absichtlich generisch gehalten. Die spätere Namenseingabe
per Controller benutzt dieselben Richtungen (Buchstabe weiter/zurück,
Position wechseln), deshalb gibt es hier keine spielspezifischen Namen.
"""
from __future__ import annotations
import enum
import pygame
from . import config
class Action(enum.Enum):
"""Abstrakte Eingabe – unabhängig davon, ob Tastatur oder Controller."""
PRIMARY = "primary" # Space / Enter / Controller A
LEFT = "left" # Pfeil links / D-Pad links
RIGHT = "right" # Pfeil rechts / D-Pad rechts
UP = "up" # Pfeil hoch / D-Pad hoch
DOWN = "down" # Pfeil runter / D-Pad runter
BACK = "back" # Esc / Controller B
#: Tastaturbelegung
KEY_ACTIONS: dict[int, Action] = {
pygame.K_SPACE: Action.PRIMARY,
pygame.K_RETURN: Action.PRIMARY,
pygame.K_KP_ENTER: Action.PRIMARY,
pygame.K_LEFT: Action.LEFT,
pygame.K_RIGHT: Action.RIGHT,
pygame.K_UP: Action.UP,
pygame.K_DOWN: Action.DOWN,
pygame.K_ESCAPE: Action.BACK,
}
#: Controller-Tasten. Xbox-Layout: 0=A, 1=B, 2=X, 3=Y.
#: Das D-Pad meldet macOS bei Xbox-Controllern nicht als Hat, sondern als
#: Tasten 11-14 (Reihenfolge hoch, runter, links, rechts).
BUTTON_ACTIONS: dict[int, Action] = {
0: Action.PRIMARY,
1: Action.BACK,
2: Action.LEFT,
3: Action.UP,
11: Action.UP,
12: Action.DOWN,
13: Action.LEFT,
14: Action.RIGHT,
}
#: D-Pad, das als Hat gemeldet wird
HAT_ACTIONS: dict[tuple[int, int], Action] = {
(-1, 0): Action.LEFT,
(1, 0): Action.RIGHT,
(0, -1): Action.UP,
(0, 1): Action.DOWN,
}
#: Analogstick: ab diesem Ausschlag gilt eine Richtung als gewollt
AXIS_DEADZONE = 0.6
#: Offene Joystick-Objekte. Sie müssen am Leben bleiben: gibt Python das
#: Objekt frei, schliesst pygame das Gerät – und SDL liefert danach **keine**
#: Joystick-Events mehr. Genau das war der Grund, weshalb ein erkannter
#: Controller im Spiel nichts bewirkte.
_OPEN_CONTROLLERS: list[pygame.joystick.Joystick] = []
def open_controllers() -> list[str]:
"""Öffnet alle angeschlossenen Controller und hält sie offen.
Nach ``pygame.init()`` aufrufen, und erneut, wenn ein Controller an- oder
abgesteckt wird (``JOYDEVICEADDED`` / ``JOYDEVICEREMOVED``).
"""
_OPEN_CONTROLLERS.clear()
names: list[str] = []
if not pygame.joystick.get_init():
try:
pygame.joystick.init()
except pygame.error:
return names
try:
count = pygame.joystick.get_count()
except pygame.error:
return names
for index in range(count):
try:
stick = pygame.joystick.Joystick(index)
stick.init()
except pygame.error:
continue
_OPEN_CONTROLLERS.append(stick)
names.append(stick.get_name())
return names
def opened_controllers() -> list[pygame.joystick.Joystick]:
"""Die aktuell offenen Controller (für Diagnose-Tools)."""
return list(_OPEN_CONTROLLERS)
def controller_names() -> list[str]:
return [stick.get_name() for stick in _OPEN_CONTROLLERS]
def is_device_event(event: pygame.event.Event) -> bool:
return event.type in (pygame.JOYDEVICEADDED, pygame.JOYDEVICEREMOVED)
def actions_from_event(event: pygame.event.Event) -> list[Action]:
"""Übersetzt ein pygame-Event in null bis eine Aktion."""
if event.type == pygame.KEYDOWN:
action = KEY_ACTIONS.get(event.key)
return [action] if action else []
if event.type == pygame.JOYBUTTONDOWN:
action = BUTTON_ACTIONS.get(event.button)
return [action] if action else []
if event.type == pygame.JOYHATMOTION:
action = HAT_ACTIONS.get(tuple(event.value))
return [action] if action else []
if event.type == pygame.JOYAXISMOTION:
if event.axis == 0 and abs(event.value) >= AXIS_DEADZONE:
return [Action.RIGHT if event.value > 0 else Action.LEFT]
if event.axis == 1 and abs(event.value) >= AXIS_DEADZONE:
return [Action.DOWN if event.value > 0 else Action.UP]
return []
def describe_controller_setup() -> str:
names = controller_names()
if not names:
return "Controller: keiner erkannt (Tastatur: Space=Snap, ←=Drehen, ↑=OnTop, Esc=Ende)"
return "Controller: " + ", ".join(names)
def controller_hint_lines() -> list[str]:
"""Kurzhilfe fürs Log beim Start."""
return [
describe_controller_setup(),
f"Zielauflösung {config.SCREEN_WIDTH}x{config.SCREEN_HEIGHT}, "
f"{config.FPS} fps",
]