【发布时间】:2018-11-12 19:24:16
【问题描述】:
我正在用 Common Lisp 制作一个简单的 LTK 游戏,并且想让玩家角色在按下空格键时跳跃。但是,窗口在不断更新,所以我不能等待用户输入。我想检查每一帧是否按下了空格键。尽管进行了很多谷歌搜索,但我还是找不到在 Lisp 中执行此操作的方法。我发现的只是阻止程序流程的传统 I/O 提示。
感谢您的帮助。
【问题讨论】:
标签: io common-lisp
我正在用 Common Lisp 制作一个简单的 LTK 游戏,并且想让玩家角色在按下空格键时跳跃。但是,窗口在不断更新,所以我不能等待用户输入。我想检查每一帧是否按下了空格键。尽管进行了很多谷歌搜索,但我还是找不到在 Lisp 中执行此操作的方法。我发现的只是阻止程序流程的传统 I/O 提示。
感谢您的帮助。
【问题讨论】:
标签: io common-lisp
您使用的是 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)))))
上述函数的第一部分为Left、Right、Down 和Up 键创建一个画布并为KeyPress/KeyRelease 事件绑定事件处理程序。这有点冗长,但对于示例来说足够简单。或者,您可以只绑定"<KeyPress>" 和"<KeyRelease>" 事件(字符串中没有其他键),并使用由您的键事件的keycode 索引的表。
第二部分是游戏循环,其中dx 和dy 两个增量实际上都用于移动矩形;在游戏循环结束时,after 确保游戏循环在一段时间后再次运行。
事件处理程序不直接操作画布元素,它们仅用于将 UI 事件转换为游戏逻辑更改。您必须小心事件发生的方式和时间。比如上面第一个版本用来做的:
(on "<KeyPress-Left>" (setf dx -1))
(on "<KeyPress-Right>" (setf dx 1))
(on "<KeyRelease-Right>" (setf dx 0))
(on "<KeyRelease-Left>" (setf dx 0))
但这是错误的,因为顺序 press-left、press-right 和 release-right 会使矩形停止。
【讨论】: