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

在 Java SWT 教程的这一部分中,我们创建了贪食蛇游戏克隆。

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

开发

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

Board.java

  1. package com.zetcode;
  2. import org.eclipse.swt.SWT;
  3. import org.eclipse.swt.graphics.Color;
  4. import org.eclipse.swt.graphics.Font;
  5. import org.eclipse.swt.graphics.GC;
  6. import org.eclipse.swt.graphics.Image;
  7. import org.eclipse.swt.graphics.ImageData;
  8. import org.eclipse.swt.graphics.Point;
  9. import org.eclipse.swt.widgets.Canvas;
  10. import org.eclipse.swt.widgets.Display;
  11. import org.eclipse.swt.widgets.Event;
  12. import org.eclipse.swt.widgets.Shell;
  13. public class Board extends Canvas {
  14. private final int WIDTH = 300;
  15. private final int HEIGHT = 300;
  16. private final int DOT_SIZE = 10;
  17. private final int ALL_DOTS = 900;
  18. private final int RAND_POS = 29;
  19. private final int DELAY = 140;
  20. private int x[] = new int[ALL_DOTS];
  21. private int y[] = new int[ALL_DOTS];
  22. private int dots;
  23. private int apple_x;
  24. private int apple_y;
  25. private boolean left = false;
  26. private boolean right = true;
  27. private boolean up = false;
  28. private boolean down = false;
  29. private boolean inGame = true;
  30. private Image ball;
  31. private Image apple;
  32. private Image head;
  33. private Display display;
  34. private Shell shell;
  35. private Runnable runnable;
  36. public Board(Shell shell) {
  37. super(shell, SWT.NULL);
  38. this.shell = shell;
  39. initBoard();
  40. }
  41. private void initBoard() {
  42. display = shell.getDisplay();
  43. addListener(SWT.Paint, event -> doPainting(event));
  44. addListener(SWT.KeyDown, event -> onKeyDown(event));
  45. addListener(SWT.Dispose, event -> {
  46. ball.dispose();
  47. apple.dispose();
  48. head.dispose();
  49. });
  50. Color col = new Color(shell.getDisplay(), 0, 0, 0);
  51. setBackground(col);
  52. col.dispose();
  53. loadImages();
  54. initGame();
  55. }
  56. private void loadImages() {
  57. ImageData iib = new ImageData("dot.png");
  58. ball = new Image(display, iib);
  59. ImageData iia = new ImageData("apple.png");
  60. apple = new Image(display, iia);
  61. ImageData iih = new ImageData("head.png");
  62. head = new Image(display, iih);
  63. }
  64. private void initGame() {
  65. dots = 3;
  66. for (int z = 0; z < dots; z++) {
  67. x[z] = 50 - z * 10;
  68. y[z] = 50;
  69. }
  70. locateApple();
  71. runnable = new Runnable() {
  72. @Override
  73. public void run() {
  74. if (inGame) {
  75. checkApple();
  76. checkCollision();
  77. move();
  78. }
  79. display.timerExec(DELAY, this);
  80. redraw();
  81. }
  82. };
  83. display.timerExec(DELAY, runnable);
  84. };
  85. private void doPainting(Event e) {
  86. GC gc = e.gc;
  87. Color col = new Color(shell.getDisplay(), 0, 0, 0);
  88. gc.setBackground(col);
  89. col.dispose();
  90. gc.setAntialias(SWT.ON);
  91. if (inGame) {
  92. drawObjects(e);
  93. } else {
  94. gameOver(e);
  95. }
  96. }
  97. private void drawObjects(Event e) {
  98. GC gc = e.gc;
  99. gc.drawImage(apple, apple_x, apple_y);
  100. for (int z = 0; z < dots; z++) {
  101. if (z == 0) {
  102. gc.drawImage(head, x[z], y[z]);
  103. } else {
  104. gc.drawImage(ball, x[z], y[z]);
  105. }
  106. }
  107. }
  108. private void gameOver(Event e) {
  109. GC gc = e.gc;
  110. String msg = "Game Over";
  111. Font font = new Font(e.display, "Helvetica", 12, SWT.NORMAL);
  112. Color whiteCol = new Color(e.display, 255, 255, 255);
  113. gc.setForeground(whiteCol);
  114. gc.setFont(font);
  115. Point size = gc.textExtent(msg);
  116. gc.drawText(msg, (WIDTH - size.x) / 2, (HEIGHT - size.y) / 2);
  117. font.dispose();
  118. whiteCol.dispose();
  119. display.timerExec(-1, runnable);
  120. }
  121. private void checkApple() {
  122. if ((x[0] == apple_x) && (y[0] == apple_y)) {
  123. dots++;
  124. locateApple();
  125. }
  126. }
  127. private void move() {
  128. for (int z = dots; z > 0; z--) {
  129. x[z] = x[(z - 1)];
  130. y[z] = y[(z - 1)];
  131. }
  132. if (left) {
  133. x[0] -= DOT_SIZE;
  134. }
  135. if (right) {
  136. x[0] += DOT_SIZE;
  137. }
  138. if (up) {
  139. y[0] -= DOT_SIZE;
  140. }
  141. if (down) {
  142. y[0] += DOT_SIZE;
  143. }
  144. }
  145. public void checkCollision() {
  146. for (int z = dots; z > 0; z--) {
  147. if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
  148. inGame = false;
  149. }
  150. }
  151. if (y[0] > HEIGHT - DOT_SIZE) {
  152. inGame = false;
  153. }
  154. if (y[0] < 0) {
  155. inGame = false;
  156. }
  157. if (x[0] > WIDTH - DOT_SIZE) {
  158. inGame = false;
  159. }
  160. if (x[0] < 0) {
  161. inGame = false;
  162. }
  163. }
  164. public void locateApple() {
  165. int r = (int) (Math.random() * RAND_POS);
  166. apple_x = ((r * DOT_SIZE));
  167. r = (int) (Math.random() * RAND_POS);
  168. apple_y = ((r * DOT_SIZE));
  169. }
  170. private void onKeyDown(Event e) {
  171. int key = e.keyCode;
  172. if ((key == SWT.ARROW_LEFT) && (!right)) {
  173. left = true;
  174. up = false;
  175. down = false;
  176. }
  177. if ((key == SWT.ARROW_RIGHT) && (!left)) {
  178. right = true;
  179. up = false;
  180. down = false;
  181. }
  182. if ((key == SWT.ARROW_UP) && (!down)) {
  183. up = true;
  184. right = false;
  185. left = false;
  186. }
  187. if ((key == SWT.ARROW_DOWN) && (!up)) {
  188. down = true;
  189. right = false;
  190. left = false;
  191. }
  192. }
  193. }

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

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

  1. private int x[] = new int[ALL_DOTS];
  2. private int y[] = new int[ALL_DOTS];

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

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

  1. runnable = new Runnable() {
  2. @Override
  3. public void run() {
  4. if (inGame) {
  5. checkApple();
  6. checkCollision();
  7. move();
  8. }
  9. display.timerExec(DELAY, this);
  10. redraw();
  11. }
  12. };

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

  1. if (inGame) {
  2. drawObjects(e);
  3. } else {
  4. gameOver(e);
  5. }

doPainting()方法内部,我们检查inGame变量。 如果是真的,我们将绘制对象-苹果和蛇关节。 否则,我们显示"Game Over"文本。

  1. private void drawObjects(Event e) {
  2. GC gc = e.gc;
  3. gc.drawImage(apple, apple_x, apple_y);
  4. for (int z = 0; z < dots; z++) {
  5. if (z == 0) {
  6. gc.drawImage(head, x[z], y[z]);
  7. } else {
  8. gc.drawImage(ball, x[z], y[z]);
  9. }
  10. }
  11. }

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

  1. public void checkApple() {
  2. if ((x[0] == apple_x) && (y[0] == apple_y)) {
  3. dots++;
  4. locateApple();
  5. }
  6. }

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

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

  1. for (int z = dots; z > 0; z--) {
  2. x[z] = x[(z - 1)];
  3. y[z] = y[(z - 1)];
  4. }

该代码将关节向上移动。

  1. if (left) {
  2. x[0] -= DOT_SIZE;
  3. }

将头向左移动。

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

  1. for (int z = dots; z > 0; z--) {
  2. if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
  3. inGame = false;
  4. }
  5. }

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

  1. if (y[0] > HEIGHT - DOT_SIZE) {
  2. inGame = false;
  3. }

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

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

  1. int r = (int) (Math.random() * RAND_POS);

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

  1. apple_x = ((r * DOT_SIZE));
  2. ...
  3. apple_y = ((r * DOT_SIZE));

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

onKeyDown()方法中,我们确定按下的键。

  1. if ((key == SWT.ARROW_LEFT) && (!right)) {
  2. left = true;
  3. up = false;
  4. down = false;
  5. }

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

Nibbles.java

  1. package com.zetcode;
  2. import org.eclipse.swt.SWT;
  3. import org.eclipse.swt.layout.FillLayout;
  4. import org.eclipse.swt.widgets.Display;
  5. import org.eclipse.swt.widgets.Shell;
  6. /**
  7. * ZetCode Java SWT tutorial
  8. *
  9. * In this code example, we create
  10. * a Nibbles game clone
  11. *
  12. * Author: Jan Bodnar
  13. * Website: zetcode.com
  14. * Last modified: June 2015
  15. */
  16. public class Nibbles {
  17. private final int WIDTH = 300;
  18. private final int HEIGHT = 300;
  19. public Nibbles(Display display) {
  20. initUI(display);
  21. }
  22. @SuppressWarnings("unused")
  23. private void initUI(Display display) {
  24. Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
  25. FillLayout layout = new FillLayout();
  26. shell.setLayout(layout);
  27. Board board = new Board(shell);
  28. shell.setText("Nibbles");
  29. int borW = shell.getSize().x - shell.getClientArea().width;
  30. int borH = shell.getSize().y - shell.getClientArea().height;
  31. shell.setSize(WIDTH + borW, HEIGHT + borH);
  32. shell.open();
  33. while (!shell.isDisposed()) {
  34. if (!display.readAndDispatch()) {
  35. display.sleep();
  36. }
  37. }
  38. }
  39. @SuppressWarnings("unused")
  40. public static void main(String[] args) {
  41. Display display = new Display();
  42. Nibbles ex = new Nibbles(display);
  43. display.dispose();
  44. }
  45. }

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

  1. int borW = shell.getSize().x - shell.getClientArea().width;
  2. int borH = shell.getSize().y - shell.getClientArea().height;
  3. shell.setSize(WIDTH + borW, HEIGHT + borH);

在设置外壳大小时,我们还需要考虑窗口装饰。

Java SWT 中的贪食蛇 - 图1

图:贪食蛇

这是使用 SWT 库和 Java 编程语言编程的贪食蛇电脑游戏。