原文: http://zetcode.com/gui/jrubyswing/nibbles/

在 JRuby Swing 编程教程的这一部分中,我们将创建一个贪食蛇游戏克隆。

贪食蛇是较旧的经典视频游戏。 它最初是在 70 年代后期创建的。 后来它被带到 PC 上。 在这个游戏中,玩家控制蛇。 目的是尽可能多地吃苹果。 蛇每次吃一个苹果,它的身体就会长大。 蛇必须避开墙壁和自己的身体。

开发

蛇的每个关节的大小为 10px。 蛇由光标键控制。 最初,蛇具有三个关节。 游戏立即开始。 游戏结束后,我们在窗口中心显示"Game Over"消息。

  1. #!/usr/local/bin/jruby
  2. # ZetCode JRuby Swing tutorial
  3. #
  4. # In this program, we create
  5. # a Nibbles game clone.
  6. #
  7. # author: Jan Bodnar
  8. # website: www.zetcode.com
  9. # last modified: December 2010
  10. include Java
  11. import java.awt.Color
  12. import java.awt.Font
  13. import java.awt.Dimension
  14. import java.awt.Toolkit
  15. import java.awt.event.ActionListener
  16. import java.awt.event.KeyEvent
  17. import java.awt.event.KeyListener
  18. import javax.swing.JFrame
  19. import javax.swing.ImageIcon
  20. import javax.swing.JPanel
  21. import javax.swing.Timer
  22. NWIDTH = 300
  23. NHEIGHT = 300
  24. DOT_SIZE = 10
  25. ALL_DOTS = NWIDTH * NHEIGHT / (DOT_SIZE * DOT_SIZE)
  26. RAND_POS = 25
  27. DELAY = 140
  28. $x = [0] * ALL_DOTS
  29. $y = [0] * ALL_DOTS
  30. class Board < JPanel
  31. include KeyListener, ActionListener
  32. def initialize
  33. super
  34. self.setFocusable true
  35. self.initGame
  36. end
  37. def initGame
  38. @left = false
  39. @right = true
  40. @up = false
  41. @down = false
  42. @inGame = true
  43. @dots = 3
  44. begin
  45. iid = ImageIcon.new "dot.png"
  46. @ball = iid.getImage
  47. iia = ImageIcon.new "apple.png"
  48. @apple = iia.getImage
  49. iih = ImageIcon.new "head.png"
  50. @head = iih.getImage
  51. rescue
  52. puts "cannot load images"
  53. end
  54. for i in 0..@dots
  55. $x[i] = 50 - i * 10
  56. $y[i] = 50
  57. end
  58. self.locateApple
  59. self.setBackground Color.black
  60. self.addKeyListener self
  61. @timer = Timer.new DELAY, self
  62. @timer.start
  63. end
  64. def paint g
  65. super g
  66. if @inGame
  67. self.drawObjects g
  68. Toolkit.getDefaultToolkit.sync
  69. g.dispose
  70. else
  71. self.gameOver g
  72. end
  73. end
  74. def drawObjects g
  75. g.drawImage @apple, @apple_x, @apple_y, self
  76. for z in 0..@dots
  77. if z == 0
  78. g.drawImage @head, $x[z], $y[z], self
  79. else
  80. g.drawImage @ball, $x[z], $y[z], self
  81. end
  82. end
  83. end
  84. def gameOver g
  85. msg = "Game Over"
  86. small = Font.new "Helvetica", Font::BOLD, 14
  87. metr = self.getFontMetrics small
  88. g.setColor Color.white
  89. g.setFont small
  90. g.drawString msg, (NWIDTH - metr.stringWidth(msg)) / 2,
  91. NHEIGHT / 2
  92. @timer.stop
  93. end
  94. def checkApple
  95. if $x[0] == @apple_x and $y[0] == @apple_y
  96. @dots = @dots + 1
  97. self.locateApple
  98. end
  99. end
  100. def move
  101. z = @dots
  102. while z > 0
  103. $x[z] = $x[(z - 1)]
  104. $y[z] = $y[(z - 1)]
  105. z = z - 1
  106. end
  107. if @left
  108. $x[0] -= DOT_SIZE
  109. end
  110. if @right
  111. $x[0] += DOT_SIZE
  112. end
  113. if @up
  114. $y[0] -= DOT_SIZE
  115. end
  116. if @down
  117. $y[0] += DOT_SIZE
  118. end
  119. end
  120. def checkCollision
  121. z = @dots
  122. while z > 0
  123. if z > 4 and $x[0] == $x[z] and $y[0] == $y[z]
  124. @inGame = false
  125. end
  126. z = z - 1
  127. end
  128. if $y[0] > NHEIGHT - DOT_SIZE
  129. @inGame = false
  130. end
  131. if $y[0] < 0
  132. @inGame = false
  133. end
  134. if $x[0] > NWIDTH - DOT_SIZE
  135. @inGame = false
  136. end
  137. if $x[0] < 0
  138. @inGame = false
  139. end
  140. end
  141. def locateApple
  142. r = rand RAND_POS
  143. @apple_x = r * DOT_SIZE
  144. r = rand RAND_POS
  145. @apple_y = r * DOT_SIZE
  146. end
  147. def actionPerformed e
  148. if @inGame
  149. self.checkApple
  150. self.checkCollision
  151. self.move
  152. end
  153. self.repaint
  154. end
  155. def keyReleased e
  156. end
  157. def keyPressed e
  158. key = e.getKeyCode
  159. if key == KeyEvent::VK_LEFT and not @right
  160. @left = true
  161. @up = false
  162. @down = false
  163. end
  164. if key == KeyEvent::VK_RIGHT and not @left
  165. @right = true
  166. @up = false
  167. @down = false
  168. end
  169. if key == KeyEvent::VK_UP and not @down
  170. @up = true
  171. @right = false
  172. @left = false
  173. end
  174. if key == KeyEvent::VK_DOWN and not @up
  175. @down = true
  176. @right = false
  177. @left = false
  178. end
  179. end
  180. end
  181. class Example < JFrame
  182. def initialize
  183. super "Nibbles"
  184. self.initUI
  185. end
  186. def initUI
  187. board = Board.new
  188. board.setPreferredSize Dimension.new NWIDTH, NHEIGHT
  189. self.add board
  190. self.pack
  191. self.setResizable false
  192. self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
  193. self.setLocationRelativeTo nil
  194. self.setVisible true
  195. end
  196. end
  197. Example.new

首先,我们将定义一些在游戏中使用的常量。

WIDTHHEIGHT常数确定电路板的大小。 DOT_SIZE是苹果的大小和蛇的点。 ALL_DOTS常数定义了板上可能的最大点数。 RAND_POS常数用于计算苹果的随机位置。 DELAY常数确定游戏的速度。

  1. $x = [0] * ALL_DOTS
  2. $y = [0] * ALL_DOTS

这两个数组存储蛇的所有可能关节的 x,y 坐标。

initGame方法初始化变量,加载图像并启动超时功能。

  1. def paint g
  2. super g
  3. if @inGame
  4. self.drawObjects g
  5. Toolkit.getDefaultToolkit.sync
  6. g.dispose
  7. else
  8. self.gameOver g
  9. end
  10. end

paint方法内部,我们检查@inGame变量。 如果为真,则绘制对象。 苹果和蛇的关节。 否则,我们显示"Game Over"文本。 Toolkit.getDefaultToolkit.sync方法可确保显示为最新。 这对于动画很有用。

  1. def drawObjects g
  2. g.drawImage @apple, @apple_x, @apple_y, self
  3. for z in 0..@dots
  4. if z == 0
  5. g.drawImage @head, $x[z], $y[z], self
  6. else
  7. g.drawImage @ball, $x[z], $y[z], self
  8. end
  9. end
  10. end

drawObjects方法绘制苹果和蛇的关节。 蛇的第一个关节是其头部,用红色圆圈表示。

  1. def gameOver g
  2. msg = "Game Over"
  3. small = Font.new "Helvetica", Font::BOLD, 14
  4. metr = self.getFontMetrics small
  5. g.setColor Color.white
  6. g.setFont small
  7. g.drawString msg, (NWIDTH - metr.stringWidth(msg)) / 2,
  8. NHEIGHT / 2
  9. @timer.stop
  10. end

gameOver方法中,我们在窗口中心显示"Game Over"消息。 我们也停止计时器。

  1. def checkApple
  2. if $x[0] == @apple_x and $y[0] == @apple_y
  3. @dots = @dots + 1
  4. self.locateApple
  5. end
  6. end

checkApple方法检查蛇是否击中了苹果对象。 如果是这样,我们添加另一个蛇形关节并调用locateApple方法,该方法将随机放置一个新的Apple对象。

move方法中,我们有游戏的关键算法。 要了解它,请看一下蛇是如何运动的。 您控制蛇的头。 您可以使用光标键更改其方向。 其余关节在链上向上移动一个位置。 第二关节移动到第一个关节的位置,第三关节移动到第二个关节的位置,依此类推。

  1. while z > 0
  2. $x[z] = $x[(z - 1)]
  3. $y[z] = $y[(z - 1)]
  4. z = z - 1
  5. end

该代码将关节向上移动。

  1. if @left
  2. $x[0] -= DOT_SIZE
  3. end

将头向左移动。

checkCollision方法中,我们确定蛇是否击中了自己或撞墙之一。

  1. while z > 0
  2. if z > 4 and $x[0] == $x[z] and $y[0] == $y[z]
  3. @inGame = false
  4. end
  5. z = z - 1
  6. end

如果蛇用头撞到关节之一,我们就结束游戏。

  1. if $y[0] > NHEIGHT - DOT_SIZE
  2. @inGame = false
  3. end

如果蛇击中了棋盘的底部,我们就结束了游戏。

locateApple方法在板上随机放置一个苹果。

  1. r = rand RAND_POS

我们得到一个从 0 到RAND_POS-1的随机数。

  1. @apple_x = r * DOT_SIZE
  2. ...
  3. @apple_y = r * DOT_SIZE

这些行设置了apple对象的 x,y 坐标。

  1. def actionPerformed e
  2. if @inGame
  3. self.checkApple
  4. self.checkCollision
  5. self.move
  6. end
  7. self.repaint
  8. end

每隔DELAY ms,将调用actionPerformed方法。 如果我们参与了游戏,我们将调用三种构建游戏逻辑的方法。

Board类的keyPressed方法中,我们确定按下的键。

  1. if key == KeyEvent::VK_LEFT and not @right
  2. @left = true
  3. @up = false
  4. @down = false
  5. end

如果单击左光标键,则将@left变量设置为true。 在move方法中使用此变量来更改蛇对象的坐标。 还要注意,当蛇向右行驶时,我们不能立即向左转。

  1. class Example < JFrame
  2. def initialize
  3. super "Nibbles"
  4. self.initUI
  5. end
  6. def initUI
  7. board = Board.new
  8. board.setPreferredSize Dimension.new NWIDTH, NHEIGHT
  9. self.add board
  10. self.pack
  11. self.setResizable false
  12. self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
  13. self.setLocationRelativeTo nil
  14. self.setVisible true
  15. end
  16. end

在这个类中,我们设置了贪食蛇游戏。

JRuby Swing 中的贪食蛇 - 图1

图:贪食蛇

这是使用 Swing 库和 JRuby 编程语言编写的贪食蛇电脑游戏。