贪吃蛇小游戏

在B站看视频的时候,看到下面这个视频就比较感兴趣,跟着手动做了一遍,写下这篇博客,

     【狂神说Java】一小时开发贪吃蛇游戏  https://www.bilibili.com/video/BV1HE41127CV?p=1 

最终游戏的界面长这个(都是画图做的图片,能力有限)

   具体的步骤,可以参照上面的视频听一下,简单易懂,(但是有一个问题,就是头部和身体重合的时候,头部的图片在下面被隐藏了,待改正,但是还没想好怎么实现,有知道怎么实现的,跪求告诉了...)

游戏规则:小蛇初始长度为3,可以上下左右四个方向移动,吃到蓝色的食物,身体长度加一,分数加10,可以穿过自己的身体,也可以穿过墙壁,(没有死亡,因为触碰身体就死掉的话很不爽,也有这个功能,只不过注释掉了)

      

玩了一会,之后的界面

      

之前使用IDEA写的代码,但是一直说的激活码失效了,一直自动退出,就很气,改用eclipse了 

下面开始说一下,编写的步骤:

   1. 首先创建一个简单的java项目 File -> 右键 - > New -> Project 

     

2.创建以下目录

      

3,把相关的图片添加到此文件夹下

       

下面就是对应图片。。。有需要直接拿走,别吐槽,我自己都惊呆了,技术有限

              

这个是对应图片的名字

 

 

4. 创建一个 StartGames 类,用来绘制窗口

     

 1 package com.wang.snake;
 2 
 3 import javax.swing.*;
 4 
 5 public class StartGames {
 6     public static void main(String[] args) {
 7         // 1.绘制一个静态窗口
 8         JFrame frame = new JFrame("贪吃蛇小游戏");
 9         // 2.设置界面的大小
10         frame.setBounds(100,10,910,720);
11         // 3.设置窗口不可调整大小
12         frame.setResizable(false);
13         // 4.“X”的关闭事件,关闭窗口
14         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
15         // 5.把面板添加到窗口里
16         frame.add(new GamePanel());
17         // 6.让窗口可以展示
18         frame.setVisible(true);
19     }
20 }

5.创建Data类,获取添加的图片信息

 1 package com.wang.snake;
 2 
 3 import javax.swing.*;
 4 import java.net.URL;
 5 
 6 /**
 7  * 存放数据
 8  */
 9 public class Data {
10 
11     // 获取标题栏图片的相对路径
12     public static URL headerURL = Data.class.getResource("/statics/header.png");
13     // 转换成图片
14     public static ImageIcon header = new ImageIcon(headerURL);
15 
16     // 获取小蛇的头部图片
17     public static URL upURL = Data.class.getResource("/statics/up.png");
18     public static URL rightURL = Data.class.getResource("/statics/right.png");
19     public static URL downURL = Data.class.getResource("/statics/down.png");
20     public static URL leftURL = Data.class.getResource("/statics/left.png");
21     public static ImageIcon upHeader = new ImageIcon(upURL);
22     public static ImageIcon rightHeader = new ImageIcon(rightURL);
23     public static ImageIcon downHeader = new ImageIcon(downURL);
24     public static ImageIcon leftHeader = new ImageIcon(leftURL);
25 
26     // 获取小蛇的身体图片
27     public static URL bodyURL = Data.class.getResource("/statics/body.png");
28     public static ImageIcon body = new ImageIcon(bodyURL);
29 
30     // 获取食物的图片
31     public static URL foodURL = Data.class.getResource("/statics/food.png");
32     public static ImageIcon food = new ImageIcon(foodURL);
33 
34 }

 6.创建GamePanel类,绘制界面,包含了标题块,游戏块,以及小蛇的移动等功能的实现类

 

  1 package com.wang.snake;
  2 
  3 import javax.swing.*;
  4 import java.awt.*;
  5 import java.awt.event.ActionEvent;
  6 import java.awt.event.ActionListener;
  7 import java.awt.event.KeyEvent;
  8 import java.awt.event.KeyListener;
  9 import java.util.Iterator;
 10 import java.util.Random;
 11 
 12 public class GamePanel extends JPanel implements KeyListener, ActionListener {
 13 
 14     // 游戏是否开始flag
 15     boolean isStart = false;
 16     // 游戏是否失败flag
 17     boolean isFail = false;  
 18  
 19     //定义一个记分系统
 20     int score;
 21 
 22     // 定义一个小蛇
 23     String fx;                      // 定义小蛇头部的朝向
 24     int length;                    // 小蛇的长度
 25     int[] snakeX = new int[600];   // 小蛇的X坐标
 26     int[] snakeY = new int[500];   // 小蛇的Y坐标
 27 
 28     // 定义一个食物
 29     int foodX;   // 食物的X坐标
 30     int foodY;   // 食物的Y坐标
 31     // 随机数的对象
 32     Random random = new Random();
 33 
 34     // 定义一个定时器
 35     Timer timer = new Timer(100,this);
 36 
 37     //创建构造器,调用init方法,
 38     public  GamePanel(){
 39         // 小蛇的初始化
 40         init();
 41 
 42         // 获取键盘的监听事件
 43         // 获取焦点,默认是不获取焦点的
 44         this.setFocusable(true);
 45         // 添加监听
 46         this.addKeyListener(this);
 47         // 启动定时器
 48         timer.start();
 49     }
 50 
 51     // 小蛇和食物的初始化的长度及位置
 52     public void init(){
 53         // 记分的初期值设置为0
 54         score = 0;
 55         
 56         // 小蛇的长度是3个,头部位置在X:100,Y:100的坐标处,朝向右
 57         fx = "R";
 58         length = 3;
 59         snakeX[0] = 100;
 60         snakeY[0] = 100;
 61         // 身体部分的坐标,第一部分的位置在X:75,Y:100的坐标处,
 62         snakeX[1] = 75;
 63         snakeY[1] = 100;
 64         // 第二部分的位置在X:50,Y:100的坐标处,
 65         snakeX[2] = 50;
 66         snakeY[2] = 100;
 67 
 68         // 食物的初期的位置随机的坐标,
 69         foodX = 25 + 25 * random.nextInt(34);
 70         foodY = 75 + 25 * random.nextInt(24);
 71     }
 72 
 73     // Graphics 画笔
 74     // 面板:画界面,画小蛇
 75     @Override
 76     protected void paintComponent(Graphics g) {
 77         // 调用父类方法,清屏
 78         super.paintComponent(g);
 79 
 80         // 设置面板背景颜色(黑色)
 81         this.setBackground(Color.white);
 82 
 83         // 绘制头部的标题栏
 84         Data.header.paintIcon(this,g,25,10);
 85         
 86         // 绘制游戏区域
 87         g.fillRect(25,75,850,600);
 88 
 89         // 创建一个静态的小蛇
 90         if (fx.equals("R")){
 91             Data.rightHeader.paintIcon(this,g,snakeX[0], snakeY[0]);
 92         } else if (fx.equals("L")){
 93             Data.leftHeader.paintIcon(this,g,snakeX[0], snakeY[0]);
 94         } else if (fx.equals("U")){
 95             Data.upHeader.paintIcon(this,g,snakeX[0], snakeY[0]);
 96         } else if (fx.equals("D")){
 97             Data.downHeader.paintIcon(this,g,snakeX[0], snakeY[0]);
 98         }
 99 
100         // 通过for循环,创建小蛇的身体部分
101         for (int i = 1; i < length; i++){
102             Data.body.paintIcon(this,g,snakeX[i], snakeY[i]);
103         }
104 
105         // 把食物添加到窗口里
106         Data.food.paintIcon(this,g,foodX, foodY);
107 
108         // 游戏开始提示:是否开始游戏
109         if (isStart == false){
110             // 提示文字的属性,
111             g.setColor(Color.white);
112             g.setFont(new Font("微软雅黑",Font.BOLD,40));
113             g.drawString("按下空格开始游戏",300,300);
114         }
115         
116         // 游戏失败提示:游戏失败
117         if (isFail){
118             // 提示文字的属性,
119             g.setColor(Color.red);
120             g.setFont(new Font("微软雅黑",Font.BOLD,40));
121             g.drawString("游戏失败,按下空格重新开始游戏",200,300);
122         }
123         
124         // 记分提示文字的属性,
125         g.setColor(Color.white);
126         g.setFont(new Font("微软雅黑",Font.BOLD,18));
127         g.drawString("长度:" + length,750,33);
128         g.drawString("分数:" + score,750,55);
129     }
130 
131     /**
132      * 接受键盘的输入,进行监听
133      * @param e
134      */
135     @Override
136     public void keyPressed(KeyEvent e) {
137         // 获取键盘按下的是哪一个键
138         int keyCode = e.getKeyCode();
139 
140         // 如果按下的是空格键,就启动或者暂停
141         if (keyCode == KeyEvent.VK_SPACE){
142             // 如果游戏失败
143             if (isFail) {
144                 // 游戏重新开始
145                 isFail = false;
146                 // 重新初始化游戏
147                 init();
148             } else {
149                 // 暂停游戏
150                 isStart = !isStart;
151             }        
152 
153             // 刷新界面
154             repaint();
155         }
156 
157         // 判断键盘按下的上下左右键,控制小蛇的走向
158         if (keyCode == KeyEvent.VK_LEFT) {
159             fx = "L";
160         } else if (keyCode == KeyEvent.VK_RIGHT){
161             fx = "R";
162         }else if (keyCode == KeyEvent.VK_UP) {
163             fx = "U";
164         }else if (keyCode == KeyEvent.VK_DOWN) {
165             fx = "D";
166         }
167 
168     }
169 
170     /**
171      * 定时器,监听事件,执行定时操作
172      * @param e
173      */
174     @Override
175     public void actionPerformed(ActionEvent e) {
176 
177         // 如果游戏处于开始状态,并且游戏没有结束,小蛇就移动
178         if (isStart && isFail == false){
179 
180             // 除了头部,身体都跟着往右移动
181             for (int i = length - 1; i > 0; i--) {
182                 snakeX[i] = snakeX[i-1];
183                 snakeY[i] = snakeY[i-1];
184             }
185 
186             // 通过控制方向,移动头部
187             if (fx.equals("R")){
188                 // 头部向右移动一步
189                 snakeX[0] = snakeX[0] + 25;
190                 // 如果已经到达边界,则从最右侧重新开始
191                 if (snakeX[0] > 850){
192                     snakeX[0] = 25;
193                 }
194             } else if (fx.equals("L")){
195                 // 头部向左移动一步
196                 snakeX[0] = snakeX[0] - 25;
197                 // 如果已经到达边界,则从最右侧重新开始
198                 if (snakeX[0] < 25){
199                     snakeX[0] = 850;
200                 }
201             } else if (fx.equals("U")){
202                 // 头部向左移动一步
203                 snakeY[0] = snakeY[0] - 25;
204                 // 如果已经到达边界,则从最右侧重新开始
205                 if (snakeY[0] < 75){
206                     snakeY[0] = 650;
207                 }
208             } else if (fx.equals("D")){
209                 // 头部向左移动一步
210                 snakeY[0] = snakeY[0] + 25;
211                 // 如果已经到达边界,则从最右侧重新开始
212                 if (snakeY[0] > 650){
213                     snakeY[0] = 75;
214                 }
215             }
216 
217             //System.out.println("snakeX = "+snakeX[0]+" foodX="+foodX +"////snakeY = "+snakeY[0]+" foodY="+foodY);
218             // 判断小蛇的头的坐标和食物的坐标重合的情况下
219               if (snakeX[0] == foodX && snakeY[0] == foodY)
220             {
221                 // 小蛇的身体长度加1
222                 length ++ ;
snakeX[length-1] = snakeX[length-2] * 2 - snakeX[length-3]; snakeY[length-1] = snakeY[length-2] * 2 - snakeY[length-3];
223 224 // 每当吃掉一个食物,积分+10 225 score = score + 10; 226 227 // 生成新的食物 228 foodX = 25 + 25 * random.nextInt(34); 229 foodY = 75 + 25 * random.nextInt(24); 230 } 231 232 // 结束判断:头和身体有接触,则游戏失败 233 //for (int i = 1; i < length; i++) { 234 // if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) { 235 // // isFail = true; 236 // 237 // } 238 //}
239 // 刷新界面 240 repaint(); 241 } 242 // 定时器启动 243 timer.start(); 244 } 245 246 /** 247 * 键盘按下, 248 * @param e 249 */ 250 @Override 251 public void keyTyped(KeyEvent e) { 252 253 } 254 255 /** 256 * 257 * @param e 258 */ 259 @Override 260 public void keyReleased(KeyEvent e) { 261 262 } 263 264 265 }

 

 

 

第二次改版,添加了计时器的功能,同时修改了之前Date类的类名=> ImageData 

 

代码如下 GamePanel类的改修,底色为粉色的部分 

 

  1 package com.wang.snake;
  2 
  3 import java.awt.Color;
  4 import java.awt.Font;
  5 import java.awt.Graphics;
  6 import java.awt.event.ActionEvent;
  7 import java.awt.event.ActionListener;
  8 import java.awt.event.KeyEvent;
  9 import java.awt.event.KeyListener;
 10 import java.text.AttributedCharacterIterator;
 11 import java.util.Random;
 12 
 13 import javax.swing.JPanel;
 14 import javax.swing.Timer;
 15 
 16 public class GamePanel extends JPanel implements KeyListener, ActionListener {
 17 
 18     // 游戏是否开始flag
 19     boolean isStart = false;
 20     // 游戏是否失败flag
 21     boolean isFail = false;  
 22     
 23     // 计时器的显示格式: 时分秒
 24     String initialTime = "00:00:00";  
 25     // 程序第一次开始时间  
 26     long programStart = System.currentTimeMillis();  
 27     // 程序一开始就是暂停的  
 28     long pauseStart = programStart;   
 29     // 程序暂停的总时间  
 30     long pauseCount = 0;  
 31      33     //定义一个记分系统
 34     int score;
 35 
 36     // 定义一个小蛇
 37     String fx;                      // 定义小蛇头部的朝向
 38     int length;                    // 小蛇的长度
 39     int[] snakeX = new int[600];   // 小蛇的X坐标
 40     int[] snakeY = new int[500];   // 小蛇的Y坐标
 41 
 42     // 定义一个食物
 43     int foodX;   // 食物的X坐标
 44     int foodY;   // 食物的Y坐标
 45     // 随机数的对象
 46     Random random = new Random();
 47 
 48     // 定义一个定时器
 49     Timer timer = new Timer(100,this);
 50 
 51     //创建构造器,调用init方法,
 52     public  GamePanel(){
 53         // 小蛇的初始化
 54         init();
 55 
 56         // 获取键盘的监听事件
 57         // 获取焦点,默认是不获取焦点的
 58         this.setFocusable(true);
 59         // 添加监听
 60         this.addKeyListener(this);
 61         // 启动定时器
 62         timer.start();
 63     }
 64 
 65     // 小蛇和食物的初始化的长度及位置
 66     public void init(){
 67         // 记分的初期值设置为0
 68         score = 0;
 69         
 70         // 小蛇的长度是3个,头部位置在X:100,Y:100的坐标处,朝向右
 71         fx = "R";
 72         length = 3;
 73         snakeX[0] = 100;
 74         snakeY[0] = 100;
 75         // 身体部分的坐标,第一部分的位置在X:75,Y:100的坐标处,
 76         snakeX[1] = 75;
 77         snakeY[1] = 100;
 78         // 第二部分的位置在X:50,Y:100的坐标处,
 79         snakeX[2] = 50;
 80         snakeY[2] = 100;
 81 
 82         // 食物的初期的位置随机的坐标,
 83         foodX = 25 + 25 * random.nextInt(34);
 84         foodY = 75 + 25 * random.nextInt(24);
 85     }
 86 
 87     // Graphics 画笔
 88     // 面板:画界面,画小蛇
 89     @Override
 90     protected void paintComponent(Graphics g) {
 91         // 调用父类方法,清屏
 92         super.paintComponent(g);
 93         
 94         // 设置面板背景颜色(黑色)
 95         this.setBackground(Color.white);
 96 
 97         // 绘制头部的标题栏
 98         ImageData.header.paintIcon(this,g,25,10);
 99         
100         // 绘制游戏区域
101         g.fillRect(25,75,850,600);
102 
103         // 创建一个静态的小蛇
104         if (fx.equals("R")){
105             ImageData.rightHeader.paintIcon(this,g,snakeX[0], snakeY[0]);
106         } else if (fx.equals("L")){
107             ImageData.leftHeader.paintIcon(this,g,snakeX[0], snakeY[0]);
108         } else if (fx.equals("U")){
109             ImageData.upHeader.paintIcon(this,g,snakeX[0], snakeY[0]);
110         } else if (fx.equals("D")){
111             ImageData.downHeader.paintIcon(this,g,snakeX[0], snakeY[0]);
112         }
113 
114         // 通过for循环,创建小蛇的身体部分
115         for (int i = 1; i < length; i++){
116             ImageData.body.paintIcon(this,g,snakeX[i], snakeY[i]);
117         }
118 
119         // 把食物添加到窗口里
120         ImageData.food.paintIcon(this,g,foodX, foodY);
121 
122         // 游戏开始提示:是否开始游戏
123         if (isStart == false){
124             // 提示文字的属性,
125             g.setColor(Color.white);
126             g.setFont(new Font("微软雅黑",Font.BOLD,40));
127             g.drawString("按下空格开始游戏",300,300);
128         }
129         
130         // 游戏失败提示:游戏失败
131         if (isFail){
132             // 提示文字的属性,
133             g.setColor(Color.red);
134             g.setFont(new Font("微软雅黑",Font.BOLD,40));
135             g.drawString("游戏失败,按下空格重新开始游戏",200,300);
136         }
137         
138         // 记分提示文字的属性,
139         g.setColor(Color.white);
140         g.setFont(new Font("微软雅黑",Font.BOLD,18));
141         g.drawString("长度:" + length,750,33);
142         g.drawString("分数:" + score,750,55);
143         
144         // 计时器提示文字的属性,
145         g.setColor(Color.white);
146         g.setFont(new Font("微软雅黑",Font.BOLD,18));
147         g.drawString("计时器:" + initialTime,30,55);      
148         
149     }
150 
151     /**
152      * 接受键盘的输入,进行监听
153      * @param e
154      */
155     @Override
156     public void keyPressed(KeyEvent e) {
157         // 获取键盘按下的是哪一个键
158         int keyCode = e.getKeyCode();
159 
160         // 如果按下的是空格键,就启动或者暂停
161         if (keyCode == KeyEvent.VK_SPACE) {
162             // 如果游戏失败
163             if (isFail) {
164                 // 游戏重新开始
165                 isFail = false;
166 
167                 // 计时器时间初始化
168                 programStart = System.currentTimeMillis();
169                 pauseStart = programStart;
170                 pauseCount = 0;
171 
172                 // 重新初始化游戏
173                 init();
174 
175             } else {
176                 // 暂停游戏
177                 isStart = !isStart;
178                 // 如果是游戏开始,则获取暂停的时间,否则暂停的时间等于当前时间
179                 if (isStart) {
180                     pauseCount += (System.currentTimeMillis() - pauseStart);
181                 } else {
182                     pauseStart = System.currentTimeMillis();
183                 }
184             }
185 
186             // 刷新界面
187             repaint();
188         }
189         
190         // 判断键盘按下的上下左右键,控制小蛇的走向
191         if (keyCode == KeyEvent.VK_LEFT) {
192             fx = "L";
193         } else if (keyCode == KeyEvent.VK_RIGHT){
194             fx = "R";
195         }else if (keyCode == KeyEvent.VK_UP) {
196             fx = "U";
197         }else if (keyCode == KeyEvent.VK_DOWN) {
198             fx = "D";
199         }
200     }
201 
202     /**
203      * 定时器,监听事件,执行定时操作
204      * @param e
205      */
206     @Override
207     public void actionPerformed(ActionEvent e) {
208 
209         // 如果游戏处于开始状态,并且游戏没有结束,小蛇就移动
210         if (isStart && isFail == false){
211             // 计时器持续开始计时
212             long elapsed = System.currentTimeMillis() - programStart - pauseCount;    
213             initialTime = format(elapsed);  
214 
215             // 除了头部,身体都跟着往右移动
216             for (int i = length - 1; i > 0; i--) {
217                 snakeX[i] = snakeX[i-1];
218                 snakeY[i] = snakeY[i-1];
219             }
220 
221             // 通过控制方向,移动头部
222             if (fx.equals("R")){
223                 // 头部向右移动一步
224                 snakeX[0] = snakeX[0] + 25;
225                 // 如果已经到达边界,则从最右侧重新开始
226                 if (snakeX[0] > 850){
227                     snakeX[0] = 25;
228                 }
229             } else if (fx.equals("L")){
230                 // 头部向左移动一步
231                 snakeX[0] = snakeX[0] - 25;
232                 // 如果已经到达边界,则从最右侧重新开始
233                 if (snakeX[0] < 25){
234                     snakeX[0] = 850;
235                 }
236             } else if (fx.equals("U")){
237                 // 头部向左移动一步
238                 snakeY[0] = snakeY[0] - 25;
239                 // 如果已经到达边界,则从最右侧重新开始
240                 if (snakeY[0] < 75){
241                     snakeY[0] = 650;
242                 }
243             } else if (fx.equals("D")){
244                 // 头部向左移动一步
245                 snakeY[0] = snakeY[0] + 25;
246                 // 如果已经到达边界,则从最右侧重新开始
247                 if (snakeY[0] > 650){
248                     snakeY[0] = 75;
249                 }
250             }
251 
252             //System.out.println("snakeX = "+snakeX[0]+" foodX="+foodX +"////snakeY = "+snakeY[0]+" foodY="+foodY);
253             // 判断小蛇的头的坐标和食物的坐标重合的情况下
254               if (snakeX[0] == foodX && snakeY[0] == foodY)
255             {
256                 // 小蛇的身体长度加1
257                 length ++ ;
258                 snakeX[length-1] = snakeX[length-2] * 2 - snakeX[length-3];
259                 snakeY[length-1] = snakeY[length-2] * 2 - snakeY[length-3];
260                 
261                 // 每当吃掉一个食物,积分+10
262                 score = score + 10;
263 
264                 // 生成新的食物
265                 foodX = 25 + 25 * random.nextInt(34);
266                 foodY = 75 + 25 * random.nextInt(24);
267             }
268 
269               // 结束判断:头和身体有接触,则游戏失败
270               for (int i = 1; i < length; i++) {
271                   if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
272                        isFail = true;                 
273                   }    
274               } 
275             // 刷新界面
276             repaint();
277         } 
278         // 定时器启动
279         timer.start();
280     }
281     
282     // 将毫秒数格式化  
283     private String format(long elapsed) {  
284         int hour, minute, second, milli;  
285 
286         milli = (int) (elapsed % 1000);  
287         elapsed = elapsed / 1000;  
288 
289         second = (int) (elapsed % 60);  
290         elapsed = elapsed / 60;  
291 
292         minute = (int) (elapsed % 60);  
293         elapsed = elapsed / 60;  
294 
295         hour = (int) (elapsed % 60);  
296 
297         // 带有毫秒的显示
298         //return String.format("%02d:%02d:%02d %03d", hour, minute, second, milli); 
299         return String.format("%02d:%02d:%02d", hour, minute, second);  
300 
301     }  
302     
303     /**
304      * 键盘按下,
305      * @param e
306      */
307     @Override
308     public void keyTyped(KeyEvent e) {
309 
310     }
311 
312     /**
313      *
314      * @param e
315      */
316     @Override
317     public void keyReleased(KeyEvent e) {
318 
319     }
320 }

 

 

 

第三版 添加了限时模式以及菜单栏

初期的画面

 

 

限时模式

 

 

 

 

StartGames类的改修

  1 package com.wang.snake;
  2 
  3 import java.awt.Color;
  4 import java.awt.Font;
  5 
  6 import javax.swing.*;
  7 
  8 public class StartGames {
  9 
 10     private final static Font f = new Font("微软雅黑", Font.PLAIN, 15);
 11     private final static Font f2 = new Font("微软雅黑", Font.PLAIN, 12);
 12 
 13     public final static String ONE_MINUTE = "00:01:00";
 14     public final static String THREE_MINUTE = "00:03:00";
 15     public final static String FIVE_MINUTE = "00:05:00";
 16     public final static int ONE_MINUTE_END_TIME = 60000;
 17     public final static int THREE_MINUTE_END_TIME = 180000;
 18     public final static int FIVE_MINUTE_END_TIME = 300000;
 19     public final static int COUNT_DOWN_TIME_STRAT = 1;
 20 
 21     static JFrame frame;
 22     static GamePanel gamePanel;
 23 
 24     public static void main(String[] args) {
 25         // 1.绘制一个静态窗口
 26         frame = new JFrame("贪吃蛇小游戏");
 27         // 2.设置界面的大小
 28         frame.setBounds(100, 0, 875, 635);        
 29         // 3.添加菜单栏
 30         menuBar();        
 31         // 4.设置窗口不可调整大小
 32         frame.setResizable(false);
 33         // 5.“X”的关闭事件,关闭窗口
 34         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 35         // 6.把面板添加到窗口里
 36         gamePanel = new GamePanel();
 37         frame.add(gamePanel);        
 38         // 7.让窗口可以展示
 39         frame.setVisible(true);
 40 
 41     }
 42 
 43     // 添加菜单栏以及触发的事件
 44     static void menuBar() {
 45         // 添加菜单栏
 46         JMenuBar bar = new JMenuBar();
 47         bar.setBackground(Color.white);
 48         frame.setJMenuBar(bar);
 49         // 设置画面属性
 50         JMenu modes = new JMenu("画面显示设定");
 51         modes.setFont(f);
 52         bar.add(modes);
 53 
 54         // 不同的游戏模式
 55         JMenu Settings = new JMenu("模式选择");
 56         Settings.setFont(f);
 57         bar.add(Settings);
 58 
 59         // 无限模式
 60         JMenuItem infiniteMode = new JMenuItem("无限模式");
 61         infiniteMode.setFont(f2);
 62         Settings.add(infiniteMode);
 63 
 64         // 限时模式
 65         JMenuItem countDownTimeMode = new JMenuItem("限时模式");
 66         countDownTimeMode.setFont(f2);
 67         Settings.add(countDownTimeMode);
 68 
 69         // 限时模式的时长,添加限时时长的菜单栏,以及限时时长
 70         JMenu duration = new JMenu("限时模式的时长");
 71         duration.setFont(f);
 72         bar.add(duration);
 73         JMenuItem oneMinute = new JMenuItem("1分钟");
 74         oneMinute.setFont(f2);
 75         duration.add(oneMinute);
 76         JMenuItem threeMinute = new JMenuItem("3分钟");
 77         threeMinute.setFont(f2);
 78         duration.add(threeMinute);
 79         JMenuItem friveMinute = new JMenuItem("5分钟");
 80         friveMinute.setFont(f2);
 81         duration.add(friveMinute);
 82 
 83         // 选择无限模式时的事件
 84         infiniteMode.addActionListener(e -> {
 85             gamePanel.infiniteTime = "00:00:00";
 86             gamePanel.programStart = System.currentTimeMillis();
 87             gamePanel.pauseStart = gamePanel.programStart;
 88             gamePanel.pauseCount = 0;
 89             // 限时模式未开始
 90             gamePanel.iscountDownTime = 0;
 91             gamePanel.isStart = false;
 92             gamePanel.init();
 93             // 刷新画面
 94             gamePanel.repaint();
 95         });
 96 
 97         // 选择限时模式时的事件
 98         countDownTimeMode.addActionListener(e -> {
 99             // 设置限时3分钟
100             countDownModeMinuteaddActionListener(COUNT_DOWN_TIME_STRAT, THREE_MINUTE, THREE_MINUTE_END_TIME);
101         });
102 
103         // 选择限时1分钟
104         oneMinute.addActionListener(e -> {
105             countDownModeMinuteaddActionListener(COUNT_DOWN_TIME_STRAT, ONE_MINUTE, ONE_MINUTE_END_TIME);
106         });
107 
108         // 选择限时3分钟
109         threeMinute.addActionListener(e -> {
110             countDownModeMinuteaddActionListener(COUNT_DOWN_TIME_STRAT, THREE_MINUTE, THREE_MINUTE_END_TIME);
111         });
112 
113         // 选择限时5分钟
114         friveMinute.addActionListener(e -> {
115             countDownModeMinuteaddActionListener(COUNT_DOWN_TIME_STRAT, FIVE_MINUTE, FIVE_MINUTE_END_TIME);
116         });
117     }
118     
119     // 限时时长的共同方法
120     static void countDownModeMinuteaddActionListener(Integer iscountDownTime, String countDownTime, int endTime) {
121         gamePanel.isStart = false;
122         // 限时模式开始
123         gamePanel.iscountDownTime = iscountDownTime;
124         // 设置默认时长的限时格式
125         gamePanel.countDownTime = countDownTime;
126         // 默认时长的毫秒数
127         gamePanel.endTime = endTime;
128         gamePanel.init();
129         gamePanel.repaint();
130     }
131 }

 

GamePanel类的改修

 

  1 package com.wang.snake;
  2 
  3 import java.awt.Color;
  4 import java.awt.Font;
  5 import java.awt.Graphics;
  6 import java.awt.event.ActionEvent;
  7 import java.awt.event.ActionListener;
  8 import java.awt.event.KeyEvent;
  9 import java.awt.event.KeyListener;
 10 import java.util.Random;
 11 
 12 import javax.swing.JButton;
 13 import javax.swing.JPanel;
 14 import javax.swing.Timer;
 15 
 16 public class GamePanel extends JPanel implements KeyListener, ActionListener {
 17 
 18     private static final long serialVersionUID = 1L;
 19 
 20     // 游戏是否开始flag
 21     boolean isStart = false;
 22     // 游戏是否失败flag
 23     static boolean isFail = false;
 24     
 25     // 计时器的显示格式: 时分秒
 26     String infiniteTime = "00:00:00";
 27     // 程序第一次开始时间
 28     long programStart = System.currentTimeMillis();
 29     // 程序一开始就是暂停的
 30     long pauseStart = programStart;
 31     // 程序暂停的总时间
 32     long pauseCount = 0;
 33     
 34     // 限时模式
 35     String countDownTime;
 36     int endTime;
 37     // 限时模式是否开始:0未开始,1开始,2结束
 38     int iscountDownTime = 0;
 39 
 40     // 定义一个记分系统
 41     int score;
 42 
 43     // 定义一个小蛇
 44     String fx; // 定义小蛇头部的朝向
 45     int length; // 小蛇的长度
 46     int[] snakeX = new int[600]; // 小蛇的X坐标
 47     int[] snakeY = new int[500]; // 小蛇的Y坐标    
 48 
 49     // 定义一个食物
 50     int foodX; // 食物的X坐标
 51     int foodY; // 食物的Y坐标
 52     // 随机数,用于食物的坐标
 53     Random random = new Random();
 54     
 55     // 定时任务间隔时长
 56     int timingDuration = 100;
 57     // 定义一个定时器
 58     Timer timer = new Timer(timingDuration, this);
 59 
 60     // 创建构造器,调用init方法,
 61     public GamePanel() {
 62         // 小蛇的初始化
 63         init();
 64 
 65         // 获取焦点,默认是不获取焦点的
 66         this.setFocusable(true);
 67 
 68         // 添加监听
 69         this.addKeyListener(this);
 70 
 71         // 启动定时器
 72         timer.start();
 73     }
 74 
 75     // 小蛇和食物的初始化的长度及位置
 76     public void init() {
 77         // 记分的初期值设置为0
 78         score = 0;
 79 
 80         // 小蛇的长度是3个,头部位置在X:100,Y:100的坐标处,朝向右
 81         fx = "R";
 82         length = 3;
 83         snakeX[0] = 105;
 84         snakeY[0] = 90;
 85         // 身体部分的坐标,第一部分的位置在X:75,Y:100的坐标处,
 86         snakeX[1] = 80;
 87         snakeY[1] = 90;
 88         // 第二部分的位置在X:50,Y:100的坐标处,
 89         snakeX[2] = 55;
 90         snakeY[2] = 90;
 91 
 92         // 食物的初期的位置随机的坐标,
 93         foodX = 5 + 25 * random.nextInt(34);
 94         foodY = 65 + 25 * random.nextInt(20);
 95     }
 96 
 97     // Graphics 画笔
 98     // 面板:画界面,画小蛇
 99     @Override
100     protected void paintComponent(Graphics g) {
101         // 调用父类方法,清屏
102         super.paintComponent(g);
103 
104         // 设置面板背景颜色(黑色)
105         this.setBackground(Color.white);
106 
107         // 绘制头部的标题栏
108         ImageData.header.paintIcon(this, g, 5, 5);
109 
110         // 绘制游戏区域
111         g.fillRect(5, 65, 850, 500);
112 
113         // 创建一个静态的小蛇
114         if (fx.equals("R")) {
115             ImageData.rightHeader.paintIcon(this, g, snakeX[0], snakeY[0]);
116         } else if (fx.equals("L")) {
117             ImageData.leftHeader.paintIcon(this, g, snakeX[0], snakeY[0]);
118         } else if (fx.equals("U")) {
119             ImageData.upHeader.paintIcon(this, g, snakeX[0], snakeY[0]);
120         } else if (fx.equals("D")) {
121             ImageData.downHeader.paintIcon(this, g, snakeX[0], snakeY[0]);
122         }
123 
124         // 通过for循环,创建小蛇的身体部分
125         for (int i = 1; i < length; i++) {
126             ImageData.body.paintIcon(this, g, snakeX[i], snakeY[i]);
127         }
128 
129         // 把食物添加到窗口里
130         ImageData.food.paintIcon(this, g, foodX, foodY);
131 
132         // 游戏开始提示:是否开始游戏
133         if (isStart == false) {
134             // 提示文字的属性,
135             g.setFont(new Font("微软雅黑", Font.BOLD, 40));
136             // 计时器提示文字的属性,
137             if (iscountDownTime == 1) {
138                 g.setColor(Color.CYAN);
139                 g.drawString("限时模式:按下空格开始游戏", 150, 300);
140             } else {
141                 g.setColor(Color.white);
142                 g.drawString("无限模式:按下空格开始游戏", 150, 300);
143             }    
144         // 游戏失败提示:游戏失败
145         } else if (isFail) {
146             // 提示文字的属性,
147             g.setColor(Color.red);
148             g.setFont(new Font("微软雅黑", Font.BOLD, 40));
149             g.drawString("游戏结束,按下空格重新开始游戏", 150, 300);            
150         }
151 
152         // 记分提示文字的属性,
153         g.setColor(Color.white);
154         g.setFont(new Font("微软雅黑", Font.BOLD, 18));
155         g.drawString("长度:" + length, 740, 28);
156         g.drawString("分数:" + score, 740, 48);
157 
158         // 计时器提示文字的属性,
159         if (iscountDownTime == 1) {
160             g.drawString("限时模式倒计时:" + countDownTime, 10, 48);
161         } else {
162             g.drawString("在线时长:" + infiniteTime, 10, 48);
163         }
164     }
165 
166     /**
167      * 接受键盘的输入,进行监听
168      * 
169      * @param e
170      */
171     @Override
172     public void keyPressed(KeyEvent e) {
173         // 获取键盘按下的是哪一个键
174         int keyCode = e.getKeyCode();
175 
176         // 如果按下的是空格键,就启动或者暂停
177         if (keyCode == KeyEvent.VK_SPACE) {
178             // 如果游戏失败
179             if (isFail) {
180                 // 游戏重新开始
181                 isFail = false;
182 
183                 // 计时器时间初始化
184                 programStart = System.currentTimeMillis();
185                 pauseStart = programStart;
186                 pauseCount = 0;
187 
188                 // 重新初始化游戏
189                 init();
190 
191             } else {
192                 // 暂停游戏
193                 isStart = !isStart;
194                 // 如果是游戏开始,则获取暂停的时间,否则暂停的时间等于当前时间
195                 if (isStart) {
196                     pauseCount += (System.currentTimeMillis() - pauseStart);
197                 } else {
198                     pauseStart = System.currentTimeMillis();
199                 }
200             }
201 
202             // 刷新界面
203             repaint();
204         }
205 
206         // 判断键盘按下的上下左右键,控制小蛇的走向
207         if (keyCode == KeyEvent.VK_LEFT) {
208             fx = "L";
209         } else if (keyCode == KeyEvent.VK_RIGHT) {
210             fx = "R";
211         } else if (keyCode == KeyEvent.VK_UP) {
212             fx = "U";
213         } else if (keyCode == KeyEvent.VK_DOWN) {
214             fx = "D";
215         }
216     }
217 
218     /**
219      * 定时器,监听事件,执行定时操作
220      * 
221      * @param e
222      */
223     @Override
224     public void actionPerformed(ActionEvent e) {
225 
226         // 如果游戏处于开始状态,并且游戏没有结束,小蛇就移动
227         if (isStart && isFail == false) {
228             // 计时器启动
229             format2();
230             // 小蛇的长度增加以及移动,
231             snakeGrowUp();
232             // 刷新界面
233             repaint();
234         }
235         // 定时器启动
236         timer.start();
237     }
238 
239     // 计时器
240     private void format2() {
241         // 如果是限时模式的话,开始进行倒计时
242         if (iscountDownTime == 1) {
243             // 判断限时模式是否到时间了
244             if (endTime >= 1000) {
245                 //System.out.println("endTime = " + endTime);
246                 endTime = endTime - timingDuration;
247                 countDownTime = format(endTime);
248                 //System.out.println("countDownTime = " + countDownTime);
249 
250                 // 限时模式时间到,游戏结束
251             } else {
252                 iscountDownTime = 2;
253                 isFail = true;
254             }
255         // 无限模式
256         } else {
257             // 计时器开始计时
258             long elapsed = System.currentTimeMillis() - programStart - pauseCount;
259             infiniteTime = format(elapsed);
260         }
261     }
262 
263     private void snakeGrowUp() {
264         // 除了头部,身体都跟着往右移动
265         for (int i = length - 1; i > 0; i--) {
266             snakeX[i] = snakeX[i - 1];
267             snakeY[i] = snakeY[i - 1];
268         }
269 
270         // 通过控制方向,移动头部
271         if (fx.equals("R")) {
272             // 头部向右移动一步
273             snakeX[0] = snakeX[0] + 25;
274             // 如果已经到达边界,则从最右侧重新开始
275             if (snakeX[0] > 830) {
276                 snakeX[0] = 5;
277             }
278         } else if (fx.equals("L")) {
279             // 头部向左移动一步
280             snakeX[0] = snakeX[0] - 25;
281             // 如果已经到达边界,则从最右侧重新开始
282             if (snakeX[0] < 5) {
283                 snakeX[0] = 830;
284             }
285         } else if (fx.equals("U")) {
286             // 头部向左移动一步
287             snakeY[0] = snakeY[0] - 25;
288             // 如果已经到达边界,则从最右侧重新开始
289             if (snakeY[0] < 65) {
290                 snakeY[0] = 540;
291             }
292         } else if (fx.equals("D")) {
293             // 头部向左移动一步
294             snakeY[0] = snakeY[0] + 25;
295             // 如果已经到达边界,则从最右侧重新开始
296             if (snakeY[0] > 540) {
297                 snakeY[0] = 65;
298             }
299         }
300 
301         // System.out.println("snakeX = "+snakeX[0]+" foodX="+foodX +"////snakeY =
302         // "+snakeY[0]+" foodY="+foodY);
303         // 判断小蛇的头的坐标和食物的坐标重合的情况下
304         if (snakeX[0] == foodX && snakeY[0] == foodY) {
305             // 小蛇的身体长度加1
306             length++;
307             snakeX[length - 1] = snakeX[length - 2] * 2 - snakeX[length - 3];
308             snakeY[length - 1] = snakeY[length - 2] * 2 - snakeY[length - 3];
309 
310             // 每当吃掉一个食物,积分+10
311             score = score + 10;
312 
313             // 生成新的食物
314             foodX = 5 + 25 * random.nextInt(34);
315             foodY = 65 + 25 * random.nextInt(20);
316         }
317 
318         // 结束判断:头和身体有接触,则游戏失败
319         for (int i = 1; i < length; i++) {
320             if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
321                 // isFail = true;
322             }
323         }
324     }
325 
326     // 将毫秒数格式化
327     private static String format(long elapsed) {
328         int hour, minute, second, milli;
329 
330         milli = (int) (elapsed % 1000);
331         elapsed = elapsed / 1000;
332 
333         second = (int) (elapsed % 60);
334         elapsed = elapsed / 60;
335 
336         minute = (int) (elapsed % 60);
337         elapsed = elapsed / 60;
338 
339         hour = (int) (elapsed % 60);
340 
341         // 带有毫秒的显示
342         // return String.format("%02d:%02d:%02d %03d", hour, minute, second, milli);
343         return String.format("%02d:%02d:%02d", hour, minute, second);
344 
345     }
346 
347     /**
348      * 键盘按下,
349      * 
350      * @param e
351      */
352     @Override
353     public void keyTyped(KeyEvent e) {
354 
355     }
356 
357     /**
358      *
359      * @param e
360      */
361     @Override
362     public void keyReleased(KeyEvent e) {
363 
364     }
365 }

 

posted @ 2021-07-08 13:08  ~Forever  阅读(349)  评论(0编辑  收藏  举报