如何使用 Racket 的“big-bang”感应多个按键

How to sense multiple key presses with Racket's `big-bang`

我正在用 Racket 开发一个简单的 Asteroids 游戏,一切正常,除了我想让玩家同时移动和开火。

控制键如下:

还有我的 on-key 处理程序:

(define (direct-ship w a-key)
  (define a-ship (world-ship w))
  (define a-direction
    (+ (ship-direction a-ship)
    (cond
      [(key=? a-key "left") -5]
      [(key=? a-key "right") 5]
      [else 0])))
  (define a-speed
    (+ (ship-speed a-ship)
       (cond
         [(key=? a-key "up") 1]
         [(key=? a-key "down") -1]
         [else 0])))
  (define bullets
    (cond
      [(key=? a-key " ") (cons (new-bullet a-ship) (world-bullets w))]
      [else (world-bullets w)]))

  (world (world-asteroids w)
         (ship (ship-pos a-ship) a-direction a-speed)
         bullets
         (world-score w)))

鉴于此过程的签名,我认为它一次只能处理一个字符。所以也许我需要使用不同的处理程序?或者不同的键?

在 github 上查看完整来源: https://github.com/ericclack/racket-examples/blob/master/asteroids4.rkt

问题是 on-key 处理程序一次只为一个键调用。即使您能够同时按下向右箭头和向上箭头,on-key 也会被调用两次。

处理此问题的一种方法是为每个键在全局 table 中存储有关键是打开还是关闭的信息。给定这样一个 table 你可以在 on-key 中使用它来检查当前正在处理的键以外的键的状态。

以下是 Space Invaders 克隆的片段。首先是全局键盘 table.

;;; Keyboard
; The keyboard state is kept in a hash table. 
; Use key-down? to find out, whether a key is pressed or not.
(define the-keyboard (make-hasheq))
(define (key-down! k) (hash-set! the-keyboard k #t))
(define (key-up! k)   (hash-set! the-keyboard k #f))
(define (key-down? k) (hash-ref  the-keyboard k #f))

然后事件的处理 - 由于上下文的原因,没有 big-bang 就完成了,但这里的想法很重要。

;;; Canvas
; Key events sent to the canvas updates the information in the-keyboard.
; Paint events calls draw-world. To prevent flicker we suspend flushing
; while drawing commences.
(define game-canvas%
  (class canvas%
    (define/override (on-event e) ; mouse events
      'ignore)
    (define/override (on-char e)  ; key event
      (define key     (send e get-key-code))
      (define release (send e get-key-release-code))
      (when (eq? release 'press)  ; key down?
        (key-down! key))
      (when (eq? key 'release)    ; key up?
        (key-up! release)
        (when (eq? release #\space)
          (play-sound "shoot.mp3" #t))))
    (define/override (on-paint)   ; repaint (exposed or resized)
      (define dc (send this get-dc))
      (send this suspend-flush)
      (send dc clear)
      (draw-world the-world dc)
      (send this resume-flush))
    (super-new)))

如您所见,按键事件处理程序仅存储按键是向上还是向下(出于某些奇怪的原因播放 "shoot.mp3" 示例)。那么玩家实际移动到哪里(根据箭头键)?

实际移动在on-tick(或等价物)中处理。 在 on-tick 中处理移动可确保玩家在按下按键时不会移动额外的距离。