当前位置:   article > 正文

JAVA程序设计:一篇文章教你完整写出贪吃蛇小游戏_贪吃蛇java的设计与实现

贪吃蛇java的设计与实现

1 运行展示


2 设计思路

        设计一个贪吃蛇游戏,我们应当遵循了一系列步骤来确保游戏的基本功能:蛇的移动、吃苹果、游戏结束判断、以及重新开始游戏。以下是设计思路的分步总结.

        1. 初始化游戏界面

  • 使用Swing组件:利用JFrameJPanel创建游戏窗口和绘图面板。
  • 设置游戏参数:定义蛇的大小、游戏区域的大小、苹果的位置等参数。
  • 初始化游戏状态:设置蛇的初始长度、位置、移动方向等。

        2. 游戏逻辑实现

  • 蛇的移动:通过数组记录蛇身体每一部分的位置,并在每次移动时更新这些位置。
  • 吃苹果:检测蛇头和苹果的位置,若重合则增加蛇的长度,并重新放置苹果。
  • 碰撞检测:检查蛇头是否触碰到边界或自身,若是,则游戏结束。

        3. 绘制游戏界面

  • 重写paintComponent方法:使用Graphics对象绘制蛇、苹果和游戏结束时的提示信息。
  • 使用计时器javax.swing.Timer用于控制游戏的更新频率,每个时间间隔都会重绘界面并更新游戏状态。

        4. 控制游戏

  • 键盘监听:实现KeyListener接口,根据用户的按键输入改变蛇的移动方向。
  • 暂停功能:通过监听特定的按键(如P键)来实现游戏的暂停和继续。

        5. 重新开始游戏

  • 添加按钮:在游戏面板中添加一个重新开始的按钮。
  • 事件监听:为按钮添加ActionListener,在游戏结束时显示按钮,并在点击时重置游戏状态。

3 技术列举

使用到的Java概念

  • 面向对象编程:通过类和对象来模拟游戏中的实体(如蛇、苹果)和行为(如移动、吃苹果)。
  • 事件驱动编程:使用事件监听器来响应用户的输入(键盘事件)和交互(按钮点击事件)。
  • 图形用户界面(GUI):使用Java Swing库来创建和管理窗口、按钮、绘图等GUI组件。
  • 多线程和并发:使用计时器(Timer)来控制游戏逻辑的周期性执行,这涉及到Java的并发编程。

4 核心功能实现思路

          在贪吃蛇游戏中,最主要的两个功能是蛇的移动游戏状态的管理(包括吃苹果和碰撞检测)。这两个功能是游戏运行的核心,决定了游戏的基本玩法和用户体验。

        1. 蛇的移动

       蛇的移动是贪吃蛇游戏的基础。玩家通过控制蛇的移动来吃苹果,游戏的乐趣和挑战性主要来自于如何控制蛇的移动路径和速度。

实现思路

  • 数据结构:使用两个数组分别存储蛇身体每一部分的x和y坐标。蛇头是数组的第一个元素,蛇尾是最后一个元素。
  • 移动逻辑:在每个时间间隔,根据蛇的当前移动方向更新蛇头的位置,然后让蛇身体的每一部分移动到前一部分的位置,实现蛇的整体移动。
  • 方向控制:通过监听键盘事件,允许玩家改变蛇的移动方向,但不能让蛇直接反向移动。

        2. 游戏状态的管理

        游戏状态的管理决定了游戏的进程和结束条件。它包括检测蛇是否吃到苹果以增长身体,以及蛇是否撞到自己或游戏边界导致游戏结束。

实现思路

  • 吃苹果
    • 检测碰撞:在蛇移动后,检查蛇头的位置是否与苹果的位置重合。如果是,表示蛇吃到了苹果。
    • 增长蛇身:增加蛇的长度,并在游戏区域内随机生成一个新的苹果位置。
  • 碰撞检测
    • 边界检测:检查蛇头是否移动出游戏区域的边界。如果是,游戏结束。
    • 自身碰撞检测:检查蛇头是否与蛇身的其他部分位置重合。如果是,同样游戏结束。
  • 游戏结束处理:当游戏结束时,停止游戏循环,可能显示游戏结束的信息,并提供重新开始游戏的选项。

        这两个功能的实现是构建一个可玩的贪吃蛇游戏的关键。它们不仅涉及到游戏的基本逻辑和用户交互,还需要考虑如何在界面上有效地反映游戏状态的变化,提供流畅和有趣的游戏体验。


5 核心功能代码实现

        1. 蛇的移动

  1. private void move() {
  2. for (int z = dots; z > 0; z--) {
  3. x[z] = x[(z - 1)];
  4. y[z] = y[(z - 1)];
  5. }
  6. if (leftDirection) {
  7. x[0] -= DOT_SIZE;
  8. }
  9. if (rightDirection) {
  10. x[0] += DOT_SIZE;
  11. }
  12. if (upDirection) {
  13. y[0] -= DOT_SIZE;
  14. }
  15. if (downDirection) {
  16. y[0] += DOT_SIZE;
  17. }
  18. }

        2. 游戏状态的管理

                 1 吃苹果方法

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

                2 碰撞检测方法

  1. private void checkCollision() {
  2. for (int z = dots; z > 0; z--) {
  3. if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
  4. inGame = false;
  5. }
  6. }
  7. if (y[0] >= HEIGHT) {
  8. inGame = false;
  9. }
  10. if (y[0] < 0) {
  11. inGame = false;
  12. }
  13. if (x[0] >= WIDTH) {
  14. inGame = false;
  15. }
  16. if (x[0] < 0) {
  17. inGame = false;
  18. }
  19. if (!inGame) {
  20. timer.stop();
  21. }
  22. }

6 全部代码实现

  1. import javax.swing.*;
  2. import java.awt.*;
  3. import java.awt.event.ActionEvent;
  4. import java.awt.event.ActionListener;
  5. import java.awt.event.KeyEvent;
  6. import java.awt.event.KeyListener;
  7. import java.util.ArrayList;
  8. import java.util.Collections;
  9. import java.util.Random;
  10. public class SnakeGame extends JPanel implements KeyListener, ActionListener {
  11. private final int WIDTH = 300;
  12. private final int HEIGHT = 300;
  13. private final int DOT_SIZE = 10;
  14. private final int ALL_DOTS = 900;
  15. private final int RANDOM_POSITION = 29;
  16. private final int DELAY = 140;
  17. private final int x[] = new int[ALL_DOTS];
  18. private final int y[] = new int[ALL_DOTS];
  19. // 在SnakeGame类中添加
  20. private JButton restartButton;
  21. private int dots;
  22. private int apple_x;
  23. private int apple_y;
  24. private boolean leftDirection = false;
  25. private boolean rightDirection = true;
  26. private boolean upDirection = false;
  27. private boolean downDirection = false;
  28. private boolean inGame = true;
  29. private boolean paused = false;
  30. private Timer timer;
  31. private Random random;
  32. private ArrayList<Integer> highScores = new ArrayList<>();
  33. public SnakeGame() {
  34. initBoard();
  35. }
  36. private void initBoard() {
  37. restartButton = new JButton("Restart");
  38. restartButton.setBounds(WIDTH / 2 - 50, HEIGHT / 2, 100, 30);
  39. restartButton.addActionListener(this);
  40. restartButton.setFocusable(false);
  41. restartButton.setVisible(false); // 初始时不显示按钮
  42. this.add(restartButton);
  43. addKeyListener(this);
  44. setBackground(Color.black);
  45. setFocusable(true);
  46. setPreferredSize(new Dimension(WIDTH, HEIGHT));
  47. initGame();
  48. }
  49. private void initGame() {
  50. dots = 3;
  51. for (int z = 0; z < dots; z++) {
  52. x[z] = 50 - z * 10;
  53. y[z] = 50;
  54. }
  55. locateApple();
  56. timer = new Timer(DELAY, this);
  57. timer.start();
  58. }
  59. private void restartGame() {
  60. dots = 3;
  61. for (int z = 0; z < dots; z++) {
  62. x[z] = 50 - z * 10;
  63. y[z] = 50;
  64. }
  65. rightDirection = true;
  66. leftDirection = false;
  67. upDirection = false;
  68. downDirection = false;
  69. inGame = true;
  70. restartButton.setVisible(false); // 重置游戏时隐藏按钮
  71. timer.start(); // 重新开始计时
  72. locateApple(); // 重新放置苹果
  73. }
  74. public void paintComponent(Graphics g) {
  75. super.paintComponent(g);
  76. doDrawing(g);
  77. }
  78. private void doDrawing(Graphics g) {
  79. if (inGame) {
  80. g.setColor(Color.red);
  81. g.fillOval(apple_x, apple_y, DOT_SIZE, DOT_SIZE);
  82. for (int z = 0; z < dots; z++) {
  83. if (z == 0) {
  84. g.setColor(Color.green);
  85. g.fillRect(x[z], y[z], DOT_SIZE, DOT_SIZE);
  86. } else {
  87. g.setColor(new Color(45, 180, 0));
  88. g.fillRect(x[z], y[z], DOT_SIZE, DOT_SIZE);
  89. }
  90. }
  91. Toolkit.getDefaultToolkit().sync();
  92. } else {
  93. gameOver(g);
  94. }
  95. }
  96. private void gameOver(Graphics g) {
  97. String msg = "Game Over";
  98. Font small = new Font("Helvetica", Font.BOLD, 14);
  99. FontMetrics metr = getFontMetrics(small);
  100. g.setColor(Color.white);
  101. g.setFont(small);
  102. g.drawString(msg, (WIDTH - metr.stringWidth(msg)) / 2, HEIGHT / 2);
  103. checkHighScore();
  104. displayHighScores(g);
  105. restartButton.setVisible(true); // 显示重新开始按钮
  106. }
  107. private void checkHighScore() {
  108. highScores.add(dots);
  109. Collections.sort(highScores, Collections.reverseOrder());
  110. while (highScores.size() > 3) {
  111. highScores.remove(highScores.size() - 1);
  112. }
  113. }
  114. private void displayHighScores(Graphics g) {
  115. g.drawString("High Scores:", 100, HEIGHT / 2 + 50);
  116. for (int i = 0; i < highScores.size(); i++) {
  117. g.drawString((i + 1) + ". " + highScores.get(i), 100, HEIGHT / 2 + 65 + i * 15);
  118. }
  119. }
  120. private void checkApple() {
  121. if ((x[0] == apple_x) && (y[0] == apple_y)) {
  122. dots++;
  123. locateApple();
  124. }
  125. }
  126. private void move() {
  127. for (int z = dots; z > 0; z--) {
  128. x[z] = x[(z - 1)];
  129. y[z] = y[(z - 1)];
  130. }
  131. if (leftDirection) {
  132. x[0] -= DOT_SIZE;
  133. }
  134. if (rightDirection) {
  135. x[0] += DOT_SIZE;
  136. }
  137. if (upDirection) {
  138. y[0] -= DOT_SIZE;
  139. }
  140. if (downDirection) {
  141. y[0] += DOT_SIZE;
  142. }
  143. }
  144. private void checkCollision() {
  145. for (int z = dots; z > 0; z--) {
  146. if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
  147. inGame = false;
  148. }
  149. }
  150. if (y[0] >= HEIGHT) {
  151. inGame = false;
  152. }
  153. if (y[0] < 0) {
  154. inGame = false;
  155. }
  156. if (x[0] >= WIDTH) {
  157. inGame = false;
  158. }
  159. if (x[0] < 0) {
  160. inGame = false;
  161. }
  162. if (!inGame) {
  163. timer.stop();
  164. }
  165. }
  166. private void locateApple() {
  167. int r = (int) (Math.random() * RANDOM_POSITION);
  168. apple_x = ((r * DOT_SIZE));
  169. r = (int) (Math.random() * RANDOM_POSITION);
  170. apple_y = ((r * DOT_SIZE));
  171. }
  172. @Override
  173. public void actionPerformed(ActionEvent e) {
  174. if (inGame) {
  175. checkApple();
  176. checkCollision();
  177. move();
  178. } else if (e.getSource() == restartButton) {
  179. restartGame();
  180. }
  181. repaint();
  182. }
  183. @Override
  184. public void keyPressed(KeyEvent e) {
  185. int key = e.getKeyCode();
  186. if ((key == KeyEvent.VK_LEFT) && (!rightDirection)) {
  187. leftDirection = true;
  188. upDirection = false;
  189. downDirection = false;
  190. }
  191. if ((key == KeyEvent.VK_RIGHT) && (!leftDirection)) {
  192. rightDirection = true;
  193. upDirection = false;
  194. downDirection = false;
  195. }
  196. if ((key == KeyEvent.VK_UP) && (!downDirection)) {
  197. upDirection = true;
  198. rightDirection = false;
  199. leftDirection = false;
  200. }
  201. if ((key == KeyEvent.VK_DOWN) && (!upDirection)) {
  202. downDirection = true;
  203. rightDirection = false;
  204. leftDirection = false;
  205. }
  206. if (key == KeyEvent.VK_P) {
  207. paused = !paused;
  208. if (paused) {
  209. timer.stop();
  210. } else {
  211. timer.start();
  212. }
  213. }
  214. }
  215. @Override
  216. public void keyReleased(KeyEvent e) {
  217. }
  218. @Override
  219. public void keyTyped(KeyEvent e) {
  220. }
  221. public static void main(String[] args) {
  222. SwingUtilities.invokeLater(() -> {
  223. JFrame frame = new JFrame("Snake Game");
  224. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  225. frame.add(new SnakeGame());
  226. frame.pack();
  227. frame.setResizable(false);
  228. frame.setVisible(true);
  229. frame.setLocationRelativeTo(null);
  230. });
  231. }
  232. }

7 总结

        本文提供了一个贪吃蛇游戏的完整Java实现,包括游戏设计思路关键功能的实现方法、以及完整的代码。游戏使用Java Swing库构建图形用户界面,通过面向对象编程、事件驱动编程、图形用户界面(GUI)设计、以及多线程和并发控制,实现了一个基本但功能完整的贪吃蛇游戏。游戏的核心功能包括蛇的移动吃苹果碰撞检测游戏状态管理、以及游戏的重新开始功能。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/820899
推荐阅读
相关标签
  

闽ICP备14008679号