转载请注明出处: https://www.cnblogs.com/love-fromAtoZ/p/11781329.html 

 

Project共有3个类:

  UI类:主要负责绘制界面以及时间监听和键盘监听。

  Snake类:负责内部的地图(int数组),蛇身移动的算法,随机食物位置以及吃掉食物的算法,以及对于是否撞墙和撞到身体的判断。

  Main类:程序入口。

运行效果:

  

程序代码:

UI.java

  1 package Snake;
  2 
  3 import java.awt.event.*;
  4 import javax.swing.*;
  5 import javax.swing.Timer;
  6 import javax.swing.border.EmptyBorder;
  7 import javax.swing.table.*;
  8 import java.awt.*;
  9 import java.util.*;
 10 import java.math.*;
 11 
 12 public class UI extends JFrame implements KeyListener{
 13     static Timer timer;
 14     static Font enFont1 = new Font("Times New Roman",Font.BOLD,15);
 15     static Font enFont2 = new Font("Times New Roman",Font.BOLD,20);
 16     static Queue<Integer> Q = new LinkedList<Integer>();
 17     static JTextField mmp[][] = new JTextField[30][30];
 18     static JPanel drawPanel,scorePanel;
 19     static JLabel scoreText,scoreNum,readMe;
 20     static JButton newGame,Help,Pause;
 21     static Snake snake;
 22     static String HelpMessage = ""
 23             + "Game Rules :\r\n"
 24             + "Use the direction keys to control the snake on your keyboard.\r\n"
 25             + "Use the space key to pause/continue the game.\r\n"
 26             + "The snake can grows longer by eatting food.\r\n"
 27             + "Snake head can not touch the edges of the map.\r\n"
 28             + "Snake head can not touch his body.\r\n"
 29             + "\r\n"
 30             + "Happy game, happy life!\r\n\r\n"
 31             + "Copyright © J_Coder 2019. All rights reserved.";
 32     static int diff = 300;
 33     public UI(){
 34         snake = new Snake();
 35         snake.newSnake();
 36         //timer 被不断 new 会导致计时器间隔越来越小
 37         timer = new Timer(diff, new TimerListener());
 38         //set drawPanel
 39         drawPanel = new JPanel();
 40         drawPanel.setLayout(null);
 41         drawPanel.setBounds(10,5,440,440);
 42         drawPanel.setBackground(Color.DARK_GRAY);
 43         drawPanel.setFocusable(false);
 44         this.add(drawPanel);
 45         for(int i = 0;i <= 21;i ++){
 46             for(int j = 0;j <= 21;j ++){
 47                 mmp[i][j] = new JTextField();
 48                 mmp[i][j].setEditable(false);
 49                 mmp[i][j].setBounds(20*j,20*i,20,20);
 50                 mmp[i][j].setBorder(new EmptyBorder(0,0,0,0));
 51                 if(i == 0 || j == 0 || i == 21 || j == 21) {
 52                     mmp[i][j].setBackground(new Color(0,155,155));
 53                 }
 54                 else mmp[i][j].setBackground(Color.WHITE);
 55                 mmp[i][j].setFocusable(false);
 56                 drawPanel.add(mmp[i][j]);
 57             }
 58         }
 59         //set scorePanel
 60         scorePanel = new JPanel();
 61         scorePanel.setLayout(null);
 62         scorePanel.setBounds(460,5,160,440);
 63         scorePanel.setBackground(Color.LIGHT_GRAY);
 64         scorePanel.setFocusable(false);
 65         this.add(scorePanel);
 66         readMe = new JLabel();
 67         readMe.setBounds(0,0,160,150);
 68         readMe.setOpaque(true);
 69         readMe.setBackground(Color.white);
 70         readMe.setFont(enFont1);
 71         readMe.setText(readMe.getText() + "<html>Description<br>Press ↓ to move up.<br>");
 72         readMe.setText(readMe.getText() + "Press ↑ to move down.<br>");
 73         readMe.setText(readMe.getText() + "Press ← to move left.<br>");
 74         readMe.setText(readMe.getText() + "Press → to move right.<br>");
 75         readMe.setText(readMe.getText() + "Press SPACE to pause or continue the game.</html>");
 76         readMe.setFocusable(false);
 77         scorePanel.add(readMe);
 78         scoreText = new JLabel("SCORE",JLabel.CENTER);
 79         scoreText.setFont(enFont2);
 80         scoreText.setBounds(15,170,130,30);
 81         scoreText.setOpaque(true);
 82         scoreText.setBackground(Color.white);
 83         scoreText.setFocusable(false);
 84         scorePanel.add(scoreText);
 85         scoreNum = new JLabel("0",JLabel.CENTER);
 86         scoreNum.setFont(enFont2);
 87         scoreNum.setBounds(15,210,130,30);
 88         scoreNum.setOpaque(true);
 89         scoreNum.setBackground(Color.white);
 90         scoreNum.setFocusable(false);
 91         scorePanel.add(scoreNum);
 92         newGame = new JButton("New Game");
 93         newGame.setFont(enFont2);
 94         newGame.setBounds(15,260,130,40);
 95         newGame.addActionListener(new ActionListener(){
 96             public void actionPerformed(ActionEvent e){
 97                 snake.newSnake();
 98                 draw();
 99                 timer.start();
100                 scoreNum.setText("0");
101                 Pause.setText("Pause");
102             }
103         });
104         newGame.setFocusable(false);
105         scorePanel.add(newGame);
106         Pause = new JButton("Pause");
107         Pause.setFont(enFont2);
108         Pause.setBounds(15,310,130,40);
109         Pause.addActionListener(new ActionListener(){
110             public void actionPerformed(ActionEvent e){
111                 if(timer == null) return ;
112                 if(Pause.getText().charAt(0) == \'P\') {PauseGame();}
113                 else {ContinueGame();}
114             }
115         });
116         Pause.setFocusable(false);
117         scorePanel.add(Pause);
118         Help = new JButton("Help");
119         Help.setFont(enFont2);
120         Help.setBounds(15,360,130,40);
121         Help.addActionListener(new ActionListener(){
122             public void actionPerformed(ActionEvent e){
123                 if(timer != null) PauseGame();
124                 JOptionPane.showMessageDialog(null, HelpMessage, "Help", JOptionPane.INFORMATION_MESSAGE);
125             }
126         });
127         Help.setFocusable(false);
128         scorePanel.add(Help);
129         //set frame
130         this.setTitle("Snake");
131         this.setLayout(null);
132         this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
133         this.setSize(640, 480);
134         Dimension winSize = Toolkit.getDefaultToolkit().getScreenSize();   
135         this.setLocation((winSize.width - this.getWidth()) / 2,(winSize.height - this.getHeight()) / 2);
136         this.setResizable(false);
137         this.setVisible(true);
138         addKeyListener(this);
139         this.setFocusable(true);
140     }
141     
142     void draw() {
143         for(int i = 1;i <= 20;i ++){
144             for(int j = 1;j <= 20;j ++){
145                 mmp[i][j].setBackground(Color.white);
146                 if(snake.mmp[i][j] == snake.L) {
147                     mmp[i][j].setBackground(new Color(200,50,50));
148                 }
149                 else if(snake.mmp[i][j] != 0 && snake.mmp[i][j] != 1000) {
150                     mmp[i][j].setBackground(new Color(50,50,200));
151                 }
152                 else if(snake.mmp[i][j] == 1000){
153                     mmp[i][j].setBackground(new Color(50,200,50));
154                 }
155             }
156         }
157     }
158     
159     static void PauseGame(){
160         timer.stop();
161         Pause.setText("Continue");
162     }
163     static void ContinueGame(){
164         timer.start();
165         Pause.setText("Pause");
166     }
167 
168     public void gameOver(){
169         timer.stop();
170         int nowScore = Integer.valueOf(scoreNum.getText());
171         String Title = "GameOver";
172         String loseMessage = "Your score is " + scoreNum.getText() + " .\r\n";
173         if(nowScore <= 300){loseMessage += "Your evaluation : Too weak !";}
174         else if(nowScore <= 800){loseMessage += "Your evaluation : Just so so !";}
175         else if(nowScore <= 1300){loseMessage += "Your evaluation : Good job !";}
176         else if(nowScore <= 2000){loseMessage += "Your evaluation : Incredible !";}
177         else if(nowScore < 3500){loseMessage += "Your evaluation : Holy crap !";}
178         else{
179             Title = "Congratulations";
180             loseMessage = "You are already finish this stage !\r\nThank you for your playing !";
181         }
182         JOptionPane.showMessageDialog(null, loseMessage, Title, JOptionPane.INFORMATION_MESSAGE);
183     }
184     
185     class TimerListener implements ActionListener {
186         public void actionPerformed(ActionEvent e) {
187 //            for(int i = 0;i <= 21;i ++) {
188 //                for(int j = 0;j <= 21;j ++) {
189 //                    System.out.printf("%2d",snake.mmp[i][j]);
190 //                }System.out.println();
191 //            }
192             if(Q.size() > 0) snake.dir = Q.poll();
193             Q.clear();
194             int tmp = snake.moveForward();
195             draw();
196             if(tmp == -1){gameOver();}
197             else scoreNum.setText(String.valueOf(Integer.valueOf(scoreNum.getText()) + tmp));
198             if(Integer.valueOf(scoreNum.getText()) >= 3500){
199                 gameOver();
200             }
201         }
202     }
203     //使用 setFocusable() 将 JFrame 设置为 true 
204     //其他组件设置为 false 可以避免点击其他按钮等操作将焦点转移至其他控件
205     public void keyPressed(KeyEvent e) {
206         if(e.getKeyCode() == KeyEvent.VK_SPACE) {
207             if(timer != null) {
208                 if(Pause.getText().charAt(0) == \'P\') {PauseGame();}
209                 else {ContinueGame();}
210             }
211             return ;
212         }
213         if(Pause.getText().charAt(0) == \'C\') {return ;}
214         if(e.getKeyCode() == KeyEvent.VK_UP) {
215             if(snake.dir != 2) Q.offer(1);
216             //if(snake.dir != 2) {snake.dir = 1;}
217             //System.out.println("up");
218             return ;
219         }
220         if(e.getKeyCode() == KeyEvent.VK_DOWN) {
221             if(snake.dir != 1) Q.offer(2);
222             //if(snake.dir != 1) {snake.dir = 2;}
223             //System.out.println("down");
224             return ;
225         }
226         if(e.getKeyCode() == KeyEvent.VK_LEFT) {
227             if(snake.dir != 4) Q.offer(3);
228             //if(snake.dir != 4) {snake.dir = 3;}
229             //System.out.println("left");
230             return ;
231         }
232         if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
233             if(snake.dir != 3) Q.offer(4);
234             //if(snake.dir != 3) {snake.dir = 4;}
235             //System.out.println("right");
236             return ;
237         }
238     }
239     public void keyReleased(KeyEvent e) {}
240     public void keyTyped(KeyEvent e) {}
241 }

Snake.java

 1 package Snake;
 2 
 3 import java.util.*;
 4 import javax.swing.Timer;
 5 import java.awt.event.ActionEvent;
 6 import java.awt.event.ActionListener;
 7 import java.math.*;
 8 import javafx.util.*;
 9 
10 public class Snake {
11     public class pair{
12         int first,second;
13         public pair(int x,int y) {first = x;second = y;}
14     }
15     static int[][] mmp = new int[30][30];
16     static int foodX,foodY;
17     static pair[] body = new pair[500];//max number is head
18      static int Dir[][] = {{0,0},{-1,0},{1,0},{0,-1},{0,1}}; // 1 = up; 2 = down; 3 = left; 4 = right
19     static int L,dir;
20     
21     void newSnake() {
22         dir = 4;L = 3;
23         init();
24         for(int i = 0;i < 400;i ++) {body[i] = new pair(-1,-1);}
25         body[0] = new pair(3,3);
26         body[1] = new pair(3,4);
27         body[2] = new pair(3,5);
28         mmp[3][3] = 1;
29         mmp[3][4] = 2;
30         mmp[3][5] = 3;
31         randFood();
32     }
33     
34     public void randFood() {
35         int x = (int)(Math.random() * 1000) % 20 + 1;
36         int y = (int)(Math.random() * 1000) % 20 + 1;
37         while(mmp[x][y] != 0) {
38             x = (int)(Math.random() * 1000) % 20 + 1;
39             y = (int)(Math.random() * 1000) % 20 + 1;
40         }
41         foodX = x;
42         foodY = y;
43         mmp[x][y] = 1000;
44     } 
45     
46     public void init() {
47         for(int i = 0;i <= 21;i ++) {
48             for(int j = 0;j <= 21;j ++) {
49                 if(i == 0 || j == 0 || i == 21 || j == 21) {mmp[i][j] = -1;}
50                 else mmp[i][j] = 0;
51             }
52         }
53     }
54     
55     public int moveForward() {
56         int ret = 0;
57         body[L].first = body[L-1].first + Dir[dir][0];
58         body[L].second = body[L-1].second + Dir[dir][1];
59         for(int i = 0;i < L;i ++) {
60             body[i] = body[i + 1];
61         }
62         body[L] = new pair(-1,-1);
63         //eat food
64         if(body[L-1].first == foodX && body[L-1].second == foodY){
65             for(int i = L;i >= 1;i --) {
66                 body[i] = body[i - 1];
67             }
68             L ++;
69             ret += 10;
70             randFood();
71         }
72         //touch wall or touch body
73         if(body[L-1].first == 0 || body[L-1].second == 0 || body[L-1].first == 21 || body[L-1].second == 21){
74             return -1;
75         }
76         for(int i = L - 2;i >= 0;i --){
77             if(body[L - 1].first == body[i].first && body[L - 1].second == body[i].second){
78                 return -1;
79             }
80         }
81         init();
82         for(int i = 0;i < L;i ++) {
83             mmp[body[i].first][body[i].second] = i + 1;
84 //            System.out.print(body[i].first);
85 //            System.out.print("  ");
86 //            System.out.println(body[i].second);
87         }
88         mmp[foodX][foodY] = 1000;
89         return ret;
90     }
91     
92 
93 }

Main.java

1 package Snake;
2 
3 public class Main {
4     public static void main(String[] args) {
5         new UI();
6     }
7 }

 

问题以及解决方案:

问题一:添加控件或者点击按钮导致键盘监听无效。

解决方法:将JFrame的setFocusable()设置为true,并将所有控件的setFocusable()都设置为false。原因是在添加或点击控件时,焦点脱离了我们添加键盘监听的东西(JPanle或JFrame等),跑到了我们添加或者点击的控件上,此时执行的是这个控件上的键盘监听,所以将所有的其他控件设置为不可获取焦点可以避免焦点丢失。

问题二:蛇会“回头”,假设蛇往右走,快速连续按下方向键下和右会导致蛇向当前方向的负方向移动。

解决方法:设置一个队列来存储计时器在一次计时内的所有按键,在每次计时结束的时候只执行队首的操作,并将队列清空,这样就保证了每个计时时间内只有一次操作。

问题三:一直点击NewGame按钮会导致计时间隔越来越小。

解决方案:不能多次new Timer(),只在构造函数内new一个Timer,其余的部分用stop()和start()进行操作。

版权声明:本文为love-fromAtoZ原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/love-fromAtoZ/p/11781329.html