Как лучше всего реализовать работу с клавиатурой? На любом языке, где я пишу программу, интерактивную с клавиатурой (например, игру в тетрис), я получаю код, который выглядит так:
for event in pygame.event.get():
if event.type == KEYDOWN:
if False: pass #make everything an elif
elif rotating: pass
elif event.key == K_q:
elif event.key == K_e:
elif event.key == K_LEFT:
curpiece.shift(-1, 0)
shadowpiece = curpiece.clone(); setupshadow(shadowpiece)
elif event.key == K_RIGHT:
curpiece.shift(1, 0)
shadowpiece = curpiece.clone(); setupshadow(shadowpiece)
(сокращенно). Мне это не нравится, так как это должно происходить в моем основном цикле, и это мешает всем частям программы. Это также делает невозможным наличие экрана конфигурации пользователя, на котором он может изменять, какие клавиши соответствуют какому действию. Есть ли хороший шаблон для этого с использованием некоторой формы обратных вызовов функций?






Вы можете создать словарь, в котором клавиши являются вводом, а значение - функцией, которая обрабатывает нажатие клавиш:
def handle_quit():
quit()
def handle_left():
curpiece.shift(-1, 0)
shadowpiece = curpiece.clone(); setupshadow(shadowpiece)
def handle_right():
curpiece.shift(1, 0)
shadowpiece = curpiece.clone(); setupshadow(shadowpiece)
def handle_pause():
if not paused:
paused = True
branch = {
K_q: handle_quit
K_e: handle_pause
K_LEFT: handle_left
K_RIGHT: handle_right
}
for event in pygame.event.get():
if event.type == KEYDOWN:
branch[event.key]()
Тогда смена ключей - это вопрос модификации ключей словаря.
Я не уверен, но я считаю, что эти функции defs должны идти над при назначении ветки, иначе вы просто получите словарь, полный Nones. Хороший ответ, наверное, я бы тоже так поступил.
я предполагаю, что их все равно нужно будет определить внутри цикла main () - или, возможно, принять какой-то аргумент в класс GameState
Возможно, было бы лучше сохранить обратный словарь: func2keys = { handle_quit: [K_q], handle_left: [K_LEFT, K_a], ...}. И сгенерируйте новый словарь branch, если конфигурация изменилась. branch = dict((k, f) for f, keys in func2keys.items() for k in keys)
помимо ответ superjoe30 можно использовать два уровня отображения (два словаря)
Я думаю, это упростило бы создание пользовательских сопоставлений. т.е. чтобы пользователи могли сопоставить свои ключи "командам", а не "имени функции".
Вы также можете получить имя команды из строки документации функции.
Вместо функций можно использовать объекты с атрибутом __call__.
То, что я делаю сейчас, - это какой-то класс / функция / поток для сбора входных данных, который будет проверять список предопределенных привязок клавиш-> событий.
Что-то вроде этого:
class InputHandler:
def __init__ (self, eventDispatcher):
self.keys = {}
self.eventDispatcher = eventDispatcher
def add_key_binding (self, key, event):
self.keys.update((key, event,))
def gather_input (self):
for event in pygame.event.get():
if event.type == KEYDOWN:
event = self.keys.get(event.key, None)
if not event is None:
self.eventDispatcher.dispatch(event)
....
inputHandler = InputHandler(EventDispatcher)
inputHandler.add_key_binding(K_q, "quit_event")
...
inputHandler.gather_input()
....
Это в основном то, что делает superjoe30, за исключением того, что вместо прямого вызова обратных вызовов я добавляю еще один уровень разделения с помощью системы диспетчеризации событий, так что любой код, который заботится о нажатых клавишах, просто прослушивает это событие.
Кроме того, ключи можно легко привязать к различным событиям, которые можно прочитать из файла конфигурации или чего-то еще, и любой ключ, который не привязан к событию, просто игнорируется.
if event is not None выглядит лучше.
Также см. stackoverflow.com/questions/292095/…