Аналог Python is_pressed в Common Lisp? - PullRequest
0 голосов
/ 09 ноября 2018

Я делаю простую LTK-игру в Common Lisp и хотел бы, чтобы персонаж игрока прыгал при нажатии на пробел. Однако окно постоянно обновляется, поэтому я не могу дождаться ввода пользователя. Я хочу проверить каждый кадр, нажата ли клавиша пробела. Несмотря на большое количество поисков, я не смог найти способ сделать это в Лиспе. Все, что я нашел, - это традиционные запросы ввода-вывода, которые останавливают поток программы.

Спасибо за любую помощь.

1 Ответ

0 голосов
/ 09 ноября 2018

Вы используете холст 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))

Но это было неправильно, потому что последовательность нажатие-влево , нажатие-вправо и отпускание-вправо приведут к остановке прямоугольника.

...