• 贪吃蛇游戏



    一、创建新项目
    创建一个新的项目,并命名。

    创建一个名为images的文件夹用来存放游戏相关图片。

    然后再在项目的src文件下创建一个com.xxx.view的包用来存放所有的图形界面类,

    创建一个com.xxx.controller的包用来存放启动的入口类(控制类)

    二、游戏界面 

    1. package com.snake.view;
    2.  
    3. import java.awt.Color;
    4. import java.awt.EventQueue;
    5. import java.awt.Font;
    6. import java.awt.Frame;
    7. import java.awt.Graphics;
    8. import java.awt.Image;
    9. import java.util.ArrayList;
    10. import java.util.List;
    11. import java.util.Random;
    12.  
    13. import javax.swing.ImageIcon;
    14. import javax.swing.JFrame;
    15. import javax.swing.JOptionPane;
    16. import javax.swing.JPanel;
    17. import javax.swing.Timer;
    18. import javax.swing.border.EmptyBorder;
    19.  
    20.  
    21. import java.awt.event.ActionEvent;
    22. import java.awt.event.ActionListener;
    23. import java.awt.event.KeyAdapter;
    24. import java.awt.event.KeyEvent;
    25.  
    26. public class SnakeJPanel extends JPanel implements ActionListener{
    27.     
    28.     private boolean start;//当前游戏状态
    29.     
    30.     private int speed;//速度
    31.     
    32.     private boolean exist;//当前是否存在食物
    33.     
    34.     private int foodType;//食物种类
    35.     
    36.     private int x;//豆子的横坐标
    37.     private int y;//豆子的纵坐标
    38.     
    39.     private ArrayList<int[]> localList;//蛇
    40.     
    41.     
    42.     public String direction;//方向
    43.     
    44.     private String direction2;//引导方向
    45.     
    46.     public boolean flag;
    47.     
    48.     Random rand = new Random();
    49.  
    50.     private ImageIcon up;
    51.  
    52.     private ImageIcon down;
    53.  
    54.     private ImageIcon right;
    55.  
    56.     private ImageIcon left;
    57.  
    58.     private ImageIcon body;
    59.  
    60.     private ImageIcon food;
    61.  
    62.     private ImageIcon title;
    63.     
    64.     Timer time;
    65.     
    66.     private int score;//当前得分情况
    67.     
    68.     private int num;//吃到的食物个数
    69.     
    70. //    private Image offScreenImage;  //图形缓存
    71.     
    72.     
    73.     //图片绘制
    74.     @Override
    75.     public void paint(Graphics g) {
    76.         
    77.         direction = direction2;
    78.         
    79.         g.setColor(Color.WHITE);
    80.         g.fillRect(0, 0, 900, 700);
    81.         
    82.         //绘制游戏框
    83.         //标题框
    84. //        g.drawRect(25, 30, 800, 75);
    85.         title.paintIcon(this, g, 25, 10);
    86.         
    87.         //内容框
    88.         g.setColor(Color.black);
    89.         g.fillRect(25, 75, 850, 600);
    90.         
    91.         //绘制食物的坐标位置
    92.         if(!exist) {//如果当前不存在豆子,随机绘制一个豆子    
    93.             if(num % 5 == 0) {
    94.                 foodType = 1;
    95.             }else {
    96.                 foodType = 0;
    97.             }
    98.             boolean isProduce = true;
    99.             while(isProduce) {
    100.                 isProduce = false;
    101.                 x = rand.nextInt(33) * 25 + 25;        
    102.                 y = rand.nextInt(23) * 25 + 75;            
    103.                 for (int[] arr:localList) {
    104.                     if(x == arr[0] && y == arr[1]) {    
    105.                         isProduce = true;
    106.                         break;    
    107.                     }
    108.                 }
    109.                 
    110.             }            
    111.             System.out.println(x + "---" + y);
    112.         }
    113.         
    114.          if(eat()) {
    115.             exist = false;
    116.          }else {
    117.             exist = true;
    118.          }
    119.          
    120.          
    121.         if(foodType == 0) {
    122.             //绘制食物
    123.             g.setColor(Color.blue);
    124. //            g.fillRect(x, y, 25, 25);
    125.             g.drawImage(food.getImage(),x, y, 25, 25,null);
    126.         }else {
    127.             //绘制食物
    128.             g.setColor(Color.WHITE);
    129.             g.fillRect(x, y, 25, 25);
    130. //            g.drawImage(food.getImage(),x, y, 25, 25,null);
    131.         }
    132.     
    133.             
    134.         //绘制头
    135.         g.setColor(Color.red);
    136. //        g.fillRect(localList.get(0)[0], localList.get(0)[1], 25, 25);    
    137.         ImageIcon head = null;
    138.         //判断当前方向
    139.         if(direction.equals("R")) {
    140.              head = right;
    141.         }else if(direction.equals("L")) {
    142.              head = left;
    143.         }else if(direction.equals("U")) {
    144.              head = up;
    145.         }else if(direction.equals("D")) {
    146.              head = down;
    147.         }        
    148. //        g.drawImage(head.getImage(), localList.get(0)[0], localList.get(0)[1], 25, 25,null);
    149.         head.paintIcon(this, g,localList.get(0)[0], localList.get(0)[1]);
    150.         
    151.         //绘制身体
    152.         g.setColor(Color.white);
    153.         for (int i = 1; i < localList.size(); i++) {
    154. //            g.fillRect(localList.get(i)[0], localList.get(i)[1], 25, 25);
    155. //            g.drawImage(body.getImage(), localList.get(i)[0], localList.get(i)[1], 25, 25,null);
    156.             body.paintIcon(this, g, localList.get(i)[0], localList.get(i)[1]);
    157.         }
    158. //        g.fillRect(localList.get(1)[0], localList.get(1)[1], 25, 25);
    159. //        g.fillRect(localList.get(2)[0], localList.get(2)[1], 25, 25);
    160.             
    161.         
    162.         //绘制分数和长度
    163.         //长度
    164.         g.setColor(Color.GREEN);
    165.         g.setFont(new Font("宋体", Font.BOLD, 18));
    166.         g.drawString("长度:" + (localList.size() - 1), 25, 30);
    167.         
    168.         //分数
    169.         g.drawString("分数:" + score, 25, 48);
    170.         
    171.         if(!start) {//如果游戏未启动,结束移动和重绘
    172.             g.setColor(Color.white);
    173.             g.setFont(new Font("宋体", Font.BOLD, 30));
    174.             g.drawString("暂停/开始(请按任意键开始,空格键暂停)", 150, 300);
    175.             time.stop();
    176.             
    177.         }else {
    178.             time.start();
    179.         }
    180.             
    181. //        speed();
    182.         //移动后进行下一次绘制        
    183. //      move();//移动
    184. //        repaint();//重新绘制        
    185.     }
    186.     
    187. //    //解决闪烁问题
    188. //    //如果为JFrame 为重量级  程序不会调用update()方法
    189. //    //如果为Frame 为轻量级  重写update()方法 做双缓冲
    190. //    //如果为JPanel 不会闪烁
    191. //      @Override
    192. //        public void update(Graphics g)
    193. //        {
    194. //            System.out.println("update");
    195. //               if(offScreenImage == null)
    196. //                  offScreenImage = this.createImage(900, 700);  //新建一个图像缓存空间,这里图像大小为800*600
    197. //                  Graphics gImage = offScreenImage.getGraphics();  //把它的画笔拿过来,给gImage保存着
    198. //                  paint(gImage);                                   //将要画的东西画到图像缓存空间去
    199. //                  g.drawImage(offScreenImage, 0, 0, null);         //然后一次性显示出来
    200. //        }
    201.     
    202.  
    203.     @Override
    204.     public void actionPerformed(ActionEvent e) {        
    205.         //移动后进行下一次绘制        
    206.         move();//移动
    207.         repaint();//重新绘制        
    208.         
    209.     }
    210.  
    211.     /**
    212.      * 绘制速度
    213.      */
    214. //    private void speed() {
    215. //        try {//按一定速度进行移动
    216. //            Thread.sleep(speed);//控制移动速度
    217. //        } catch (InterruptedException e) {
    218. //            // TODO 自动生成的 catch 块
    219. //            e.printStackTrace();
    220. //        }
    221. //    }
    222.  
    223.     /**
    224.      * 初始化图片
    225.      */
    226.     private void drawImage() {
    227.         up = new ImageIcon("images/up.png");
    228.         down = new ImageIcon("images/down.png");
    229.         right = new ImageIcon("images/right.png");
    230.         left = new ImageIcon("images/left.png");
    231.         body = new ImageIcon("images/body.png");
    232.         food = new ImageIcon("images/food.png");
    233.         title = new ImageIcon("images/title.jpg");
    234.     }
    235.     
    236.     private boolean eat() {
    237.         if(localList.get(0)[0] == x && localList.get(0)[1] == y) {//如果当前蛇头吃到了豆子
    238.             System.out.println("eat");
    239.             num++;
    240.             if(foodType == 0) {
    241.                 score += 10;
    242.             }else {
    243.                 score += (rand.nextInt(5) * 10 + 10);
    244.             }
    245.             int last = localList.size() - 1;//蛇尾            
    246.             //在蛇尾后面添加一节身体
    247.             localList.add(new int[] {localList.get(last)[0],localList.get(last)[1]});
    248.             return true;
    249.         }
    250.         return false;
    251.     }
    252.  
    253.     //移动方法
    254.     public void move() {
    255.         //判断是否游戏结束
    256.         if(isbody()) {
    257.             System.out.println("game over");
    258.             start = false;//结束游戏移动
    259.             JOptionPane.showMessageDialog(null,"游戏已结束!");
    260.             time.stop();
    261.             init();        
    262.         }
    263.             
    264.         if(flag && localList != null) {//如果长度不为空且游戏未结束                
    265.             int last = localList.size() - 1;//记录蛇尾
    266.             
    267.             for (int i = last; i > 0; i--) {//从蛇尾开始,每节身体移动到前一节身体的位置上
    268.                 localList.set(i,new int[] {localList.get(i - 1)[0],localList.get(i - 1)[1]});
    269.             }
    270.             
    271.             //记录头位置
    272.             int[] local = localList.get(0);
    273.             //判断当前方向,并进行模拟移动,判断是否与边界重合
    274.             if(direction.equals("R")) {
    275.                 if(local[0] >= 850) {
    276.                     local[0] = 25;
    277.                 }else {
    278.                     local[0] += 25;
    279.                 }
    280.                 
    281.             }else if(direction.equals("L")) {
    282.                 if(local[0] <= 25) {
    283.                     local[0] = 850;
    284.                 }else {
    285.                     local[0] -= 25;
    286.                 }
    287.                 
    288.             }else if(direction.equals("U")) {
    289.                 
    290.                 if(local[1] <= 75) {
    291.                     local[1] = 650;
    292.                 }else {
    293.                     local[1] -= 25;
    294.                 }
    295.                 
    296.             }else if(direction.equals("D")) {
    297.                 if(local[1] >= 650) {
    298.                     local[1] = 75;
    299.                 }else {
    300.                     local[1] += 25;
    301.                 }
    302.                 
    303.             }            
    304.                         
    305.             //更改头的位置
    306.             localList.set(0, local);        
    307.         }    
    308.     }
    309.     
    310.     //判断下一步是否为蛇身
    311.     private boolean isbody() {
    312.         // TODO 自动生成的方法存根
    313.         //记录头位置
    314.         int x = localList.get(0)[0];
    315.         int y = localList.get(0)[1];
    316.  
    317.         //判断当前方向,并进行模拟移动,判断是否与边界重合
    318.         if(direction.equals("R")) {
    319.             x += 25;
    320.         }else if(direction.equals("L")) {
    321.             x -= 25;
    322.         }else if(direction.equals("U")) {
    323.             y -= 25;
    324.         }else if(direction.equals("D")) {
    325.             y += 25;
    326.         }            
    327.         
    328.         for (int i = 1; i < localList.size(); i++) {
    329.             if(localList.get(i)[0] == x && localList.get(i)[1] == y) {
    330.                 return true;
    331.             }
    332.         }
    333.         return false;
    334.  
    335.     }
    336.     
    337. //    //判断下一步是否为边界
    338. //    private boolean isborder() {
    339. //        // TODO 自动生成的方法存根
    340. //        //记录头位置
    341. //        // TODO 自动生成的方法存根
    342. //        //记录头位置
    343. //        int x = localList.get(0)[0];
    344. //        int y = localList.get(0)[1];
    345. //
    346. //        //判断当前方向,并进行模拟移动,判断是否与边界重合
    347. //        if(direction.equals("R")) {
    348. //            x += 25;
    349. //        }else if(direction.equals("L")) {
    350. //            x -= 25;
    351. //        }else if(direction.equals("U")) {
    352. //            y -= 25;
    353. //        }else if(direction.equals("D")) {
    354. //            y += 25;
    355. //        }    
    356. //                
    357. //        if(x < 25 || x > (33 * 25 + 25)) {
    358. //            return true;//当x坐标超出边界,则返回true
    359. //        }
    360. //        if(y < 105 || y > (23 * 25 + 105)) {
    361. //            return true;//当y坐标超出边界,则返回true
    362. //        }
    363. //        return false;//蛇头移动后未超出边界,返回false
    364. //        
    365. //    }
    366.  
    367.     /**
    368.      * Create the frame.
    369.      */
    370.     public SnakeJPanel(int speed) {
    371.         
    372.         this.speed = speed; //初始化速度
    373.         
    374.         //初始化游戏面板的基本信息
    375.         this.setSize(900, 700);
    376.         this.setLocation(0, 30);
    377.         this.setFocusable(true);
    378.         
    379.         init();//初始化界面
    380.         drawImage();//绘制图片
    381.         moveByKey();//给界面添加一个键盘监听
    382.                 
    383.     }
    384.  
    385.     /*
    386.      * 键盘监听
    387.      * 通过键盘输入上下左右来控制当前蛇头移动的方向
    388.      * 先判断当前蛇头方向,再来改变引导方向
    389.      * 当进行绘制时再修改蛇的方向
    390.      * 保证不会因为在短时间内快速变换方向导致蛇头逆向转向
    391.      */
    392.     private void moveByKey() {
    393.         addKeyListener(new KeyAdapter() {
    394.             @Override
    395.             public void keyPressed(KeyEvent e) {
    396.                 int key = e.getKeyCode();
    397.                 //边界值判断
    398.                 switch(key) {
    399.                 case 65:
    400.                 case 37:{//向左走
    401.                     if(!direction.equals("R")) {
    402.                         direction2 = "L";
    403.                         
    404.                     }
    405.                     break;
    406.                 }                
    407.                 case 87:
    408.                 case 38:{//向上走
    409.                     if(!direction.equals("D")) {
    410.                         direction2 = "U";
    411.                     }                
    412.                     break;
    413.                 }                
    414.                 case 68:
    415.                 case 39:{//向右走
    416.                     if(!direction.equals("L")) {
    417.                         direction2 = "R";
    418.                     }
    419.                     break;
    420.                 }
    421.                 case 83:
    422.                 case 40:{//向下走
    423.                     if(!direction.equals("U")) {
    424.                         direction2 = "D";
    425.                     }                    
    426.                     break;
    427.                 }
    428.                 case KeyEvent.VK_SPACE:{//如果当前键盘输入为空格
    429.                     start = !start;//调整游戏状态
    430.                     System.out.println("暂停/开始");
    431.                     repaint();//重绘
    432.                 }
    433.                 }
    434.                 
    435.                 //任意键开始
    436.                 if(!start && key != KeyEvent.VK_SPACE) {//如果当前状态为暂停状态,且键盘输入不是空格
    437.                     start = true;
    438.                     repaint();//重绘
    439.                     
    440.                 }                
    441.             }
    442.         });
    443.     }
    444.  
    445.     /**
    446.      * 初始化游戏基本信息
    447.      */
    448.     private void init() {
    449.         start = false;
    450.  
    451.         exist = true;
    452.  
    453.         direction2 = "U";
    454.  
    455.         flag = true;
    456.  
    457.         localList = new ArrayList<int[]>();
    458.  
    459.         localList.add(0,new int[] {75,125});//蛇头
    460.         localList.add(1,new int[] {75,150});//蛇身1
    461.         localList.add(2,new int[] {75,175});//蛇身2
    462.  
    463.         //创建第一个食物的位置
    464.         //通过循环保证当前生成的食物不在身体所在的坐标上
    465.         boolean isProduce = true;
    466.         while(isProduce) {//循环生成食物坐标
    467.             isProduce = false;//结束本次循环
    468.             x = rand.nextInt(33) * 25 + 25;        
    469.             y = rand.nextInt(23) * 25 + 75;            
    470.             for (int[] arr:localList) {//循环遍历蛇头及蛇身的坐标
    471.                 if(x == arr[0] && y == arr[1]) {//如果食物坐标和蛇的某一节坐标重合
    472.                     isProduce = true;//跳转循环状态,继续下一次食物生成
    473.                     break;    
    474.                 }
    475.             }
    476.             //蛇身遍历完成,没有重合坐标,结束食物坐标生成
    477.                             
    478.         }
    479.  
    480.         time = new Timer(speed, this);
    481.         setLayout(null);
    482.  
    483.         score = 0;
    484.  
    485.         num = 0;
    486.         
    487.         foodType = 0;
    488.         
    489. //        repaint();
    490.         
    491.     }
    492.  
    493.  
    494. }

    三、构造启动类

    1. package com.snake.controller;
    2.  
    3. import javax.swing.JFrame;
    4. import javax.swing.JOptionPane;
    5.  
    6. import com.snake.view.SnakeJPanel;
    7.  
    8. public class SnakeStart {
    9.     public static void main(String[] args) {
    10.         
    11.         int speed = 0;
    12.         String showInputDialog = null;//初始化时间
    13.         //得到速度
    14.         while(true) {
    15.             showInputDialog = JOptionPane.showInputDialog("蛇移动速度(1 - 5)","3");
    16.             
    17.             if(showInputDialog == null) {
    18.                 showInputDialog = "3";//默认速度
    19.                 break;
    20.             }
    21.             if(showInputDialog.length() > 1) {
    22.                 continue;
    23.             }
    24.             char[] a = showInputDialog.toCharArray();
    25.             if(a[0] >= '1' && a[0] <= '5') {
    26.                 break;
    27.             }
    28.         }
    29.             
    30.         speed = Integer.parseInt(showInputDialog) * 50;
    31.         
    32.         
    33.         SnakeJPanel snakeJPanel = new SnakeJPanel(speed);
    34.         
    35.         //创建一个JFrame窗口,将游戏面板添加进行窗口中
    36.         JFrame jFrame = new JFrame();
    37.         //设置窗口的某些属性
    38.         jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    39.         jFrame.setSize(920, 750);
    40.         jFrame.add(snakeJPanel);
    41.         jFrame.setLocationRelativeTo(null);
    42.         jFrame.setVisible(true);
    43.     }
    44.  
    45. }

    四、游戏启动
    设置游戏速度

    游戏界面

  • 相关阅读:
    VFP用Foxjson玩转JSON,超简单的教程
    Acwing 3302. 表达式求值
    字节架构师: Kafka 的消费者客户端详解
    webgl未使用独立显卡报告
    机器学习笔记(吴恩达老师)
    编译原理10:算符优先、FIRSTVT和LASTVT集合、最左素短语
    使用LibreOffice pdf批量转换为jpg
    JDK中常见的设计模式
    C++:回调函数的应用
    【linux】VMware虚拟机外网访问配置以及本机ssh连接vmware虚拟机
  • 原文地址:https://blog.csdn.net/weixin_74859670/article/details/134493726