我是Clojure的初学者,我一直在玩这里提供的蛇游戏代码https://github.com/stuarthalloway/programming-clojure/blob/master/src/examples/snake.clj。当我运行游戏时,蛇似乎在移动时跳过位置,它并没有像我预期的那样正方形。但当我转动蛇并按住方向键时,它开始正常运行并逐个方向移动,一旦我松开钥匙,它就会再次开始跳过。我想这与Swing绘制组件或处理事件的方式有关,但我还没有能够解决这个问题或调试它。即使我没有按下钥匙,我也希望我的蛇在没有跳过的情况下逐个方向移动。这是我的代码,它与我给出的关于空间和墙壁的一些小修改的链接基本相同:
(ns app.core
(:import (java.awt Color Dimension)
(javax.swing JPanel JFrame Timer JOptionPane WindowConstants)
(java.awt.event ActionListener KeyListener))
(:use app.util.import-static))
(import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN)
; ---------------------------------------------------------------------
; functional model
; ---------------------------------------------------------------------
; constants to describe time, space and motion
(def width 19)
(def height 10)
(def point-size 25)
(def turn-millis 150)
(def win-length 500)
(def dirs {VK_LEFT [-1 0]
VK_RIGHT [1 0]
VK_UP [0 -1]
VK_DOWN [0 1]})
; math functions for game board
(defn add-points [& pts]
(vec (apply map + pts)))
(defn point-to-screen-rect [pt]
(map #(* point-size %)
[(pt 0) (pt 1) 1 1]))
; function for creating an apple
(defn create-apple []
{:location [(rand-int width) (rand-int height)]
:color (Color. 210 50 90)
:type :apple})
; function for creating a snake
(defn create-snake []
{:body (list [1 1])
:dir [1 0]
:type :snake
:color (Color. 15 160 70)})
; function for moving a snake
(defn move [{:keys [body dir] :as snake} & grow]
(assoc snake :body (cons (add-points (first body) dir)
(if grow body (butlast body)))))
; function for checking if the player won
(defn win? [{body :body}]
(>= (count body) win-length))
; function for checking if the player lost the game,
; which means that head of the snake has overlaped
; with its body
(defn head-overlaps-body? [{[head & body] :body}]
(contains? (set body) head))
(defn out-of-bounds? [{[head] :body}]
(or (< (head 0) 0)
(> (head 0) width)
(< (head 1) 0)
(> (head 1) height)))
(defn lose? [snake]
(or (head-overlaps-body? snake) (out-of-bounds? snake)))
; function for checking if the snake eats an apple
; (check if head location equals apple location)
(defn eats? [{[snake-head] :body} {apple :location}]
(= snake-head apple))
; function that changes direction
(defn turn [snake newdir]
(assoc snake :dir newdir))
; ---------------------------------------------------------------------
; mutable model
; ---------------------------------------------------------------------
; function that resets the game state
(defn reset-game [snake apple]
(dosync (ref-set snake (create-snake))
(ref-set apple (create-apple)))
nil)
; function for updating snake's direction
(defn update-direction [snake newdir]
(when newdir (dosync (alter snake turn newdir))))
; function for updating positions of snake and apple
(defn update-positions [snake apple]
(dosync
(if (eats? @snake @apple)
(do (ref-set apple (create-apple))
(alter snake move :grow))
(alter snake move)))
nil)
; ---------------------------------------------------------------------
; gui
; ---------------------------------------------------------------------
; function for making a point on the screen
(defn fill-point [g pt color]
(let [[x y width height] (point-to-screen-rect pt)]
(.setColor g color)
(.fillRect g x y width height)))
; function for painting snakes and apples
(defmulti paint (fn [g object & _] (:type object)))
(defmethod paint :apple [g {:keys [location color]}]
(fill-point g location color))
(defmethod paint :snake [g {:keys [body color]}]
(doseq [point body]
(fill-point g point color)))
; game panel
(defn game-panel [frame snake apple]
(proxy [JPanel ActionListener KeyListener] []
(paintComponent [g]
(proxy-super paintComponent g)
(paint g @snake)
(paint g @apple))
(actionPerformed [e]
(update-positions snake apple)
(when (lose? @snake)
(reset-game snake apple)
(JOptionPane/showMessageDialog frame "Game over!"))
(when (win? @snake)
(reset-game snake apple)
(JOptionPane/showMessageDialog frame "You win!"))
(.repaint this))
(keyPressed [e]
(update-direction snake (dirs (.getKeyCode e))))
(getPreferredSize []
(Dimension. (* (inc width) point-size)
(* (inc height) point-size)))
(keyReleased [e])
(keyTyped [e])))
; main game function
(defn game []
(let [snake (ref (create-snake))
apple (ref (create-apple))
frame (JFrame. "Snake")
panel (game-panel frame snake apple)
timer (Timer. turn-millis panel)]
(doto panel
(.setFocusable true)
(.addKeyListener panel))
(doto frame
(.add panel)
(.pack)
(.setVisible true)
(.setResizable false)
(.setDefaultCloseOperation WindowConstants/EXIT_ON_CLOSE))
(.start timer)
[snake, apple, timer]))
此处也尝试了此实现http://java.ociweb.com/mark/programming/ClojureSnake.html,它也有同样的问题。这可能是与Swing相关的问题。我正在考虑使用Seesaw库,但由于它是Swing的包装器,问题可能会停留。