Вы используете холст Tk? Может быть, вы могли бы связать обработчики событий с нажатиями клавиш?
(with-ltk ()
(let ((canvas (make-canvas nil)))
(grid canvas 1 1)
(bind canvas
"<Left>"
(lambda (e)
(message-box "Left" "Click" "okcancel" "info")))
(focus canvas)))
Когда вы получаете нажатие клавиши, вы можете установить переменную, которую вы проверяете при обработке кадра.
Это прекрасно работает для одной кнопки, но привязка, например, и стрелка вверх, и стрелка влево для движения регистрируют только по одному за раз. Есть ли способ это исправить?
Вот более подробный пример. Tk может связываться с событиями KeyPress
и KeyRelease
. Вам нужно только управлять нажатыми / неотжатыми состояниями клавиш, которые вы хотите наблюдать, и соответственно устанавливать правильные переменные.
(with-ltk ()
(let ((canvas (make-canvas nil))
(x 50)
(y 50)
(speed 5)
(w 10)
(h 10)
(dx 0)
(dy 0))
(grid canvas 1 1)
(macrolet
((on (event expr)
(check-type event string)
(let ((evar (gensym)))
`(bind canvas ,event
(lambda (,evar)
(declare (ignore ,evar))
,expr)))))
(on "<KeyPress-Left>" (decf dx))
(on "<KeyPress-Right>" (incf dx))
(on "<KeyRelease-Left>" (incf dx))
(on "<KeyRelease-Right>" (decf dx))
(on "<KeyPress-Up>" (decf dy))
(on "<KeyPress-Down>" (incf dy))
(on "<KeyRelease-Up>" (incf dy))
(on "<KeyRelease-Down>" (decf dy)))
(focus canvas)
(let ((rectangle (create-rectangle canvas x y 10 10)))
(labels ((game-loop ()
(incf x (* dx speed))
(incf y (* dy speed))
(set-coords canvas rectangle (list x y (+ x w) (+ y h)))
(after 50 #'game-loop)))
(game-loop)))))
Первая часть вышеупомянутой функции создает холст и связывает обработчики событий для событий KeyPress
/ KeyRelease
для клавиш Left
, Right
, Down
и Up
. Это немного многословно, но достаточно просто для примера. Кроме того, вы можете связать только события "<KeyPress>"
и "<KeyRelease>"
(без дополнительного ключа в строке) и использовать таблицу, индексированную keycode
вашего ключевого события.
Вторая часть - игровой цикл, в котором обе дельты dx
и dy
фактически используются для перемещения прямоугольника; в конце игрового цикла after
обеспечивает повторное выполнение игрового цикла через некоторое время.
Обработчики событий напрямую не манипулируют элементами холста, они используются только для преобразования событий пользовательского интерфейса в изменения логики игры. Вы должны быть осторожны с тем, как и когда происходят события. Например, первая версия вышеупомянутого используется для:
(on "<KeyPress-Left>" (setf dx -1))
(on "<KeyPress-Right>" (setf dx 1))
(on "<KeyRelease-Right>" (setf dx 0))
(on "<KeyRelease-Left>" (setf dx 0))
Но это было неправильно, потому что последовательность нажатие-влево , нажатие-вправо и отпускание-вправо приведут к остановке прямоугольника.