zoukankan      html  css  js  c++  java
  • 贪吃蛇小游戏

    在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 }
  • 相关阅读:
    Codeforces 1491 D. Zookeeper and The Infinite Zoo (二进制处理)
    Codeforces 1479A. Searching Local Minimum(注意输入+二分)
    Codeforces 1480B. The Great Hero(阅读模拟题,注意数据范围和攻击顺序)
    Codeforces 1480A. Yet Another String Game (阅读理解题)
    windows 10 开启全盘瞬间索引功能
    JetBrains CLion C++ IDE连接wsl2(Ubuntu)时,报错"Unable to establish SSL connection"解决方案
    WorkFlowy 的 MarkDown 预览方案解决
    git 学习 完全学懂
    jeecgboot <j-popup
    面试之类加载器
  • 原文地址:https://www.cnblogs.com/1123-wyl/p/14985620.html
Copyright © 2011-2022 走看看