2021-04-24

用java实现贪吃蛇游戏(源码+详细注释)

(素材图片在文末)

一、文件结构

2021-04-24

二、实现代码

  1. StartGame
在这里插入代package com.nanguadeng.eatsnakes;

import javax.swing.*;
import java.awt.*;

public class StartGame {
    public static void main(String[] args) {



        //创建一个窗体:
        JFrame jf = new JFrame();
        //给窗体设计标题:
        jf.setTitle("贪吃蛇小游戏");
        //获得屏幕宽和高
        int width = Toolkit.getDefaultToolkit().getScreenSize().width;
        int height = Toolkit.getDefaultToolkit().getScreenSize().height;
        //设置窗体弹出对应坐标及对应窗体宽和高
        jf.setBounds((width-800)/2,(height-800)/2,800,800);
        //设置窗体大小不可调节
        jf.setResizable(false);
        //关闭窗口时程序就关闭:
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //创建面版:
        GamePanel gp = new GamePanel();
        //将面板放入窗体中:
        jf.add(gp);
        //默认情况下窗体是隐藏的,所以必须将窗体进行显现:
        jf.setVisible(true);//显现方法最好放最后,因为中间有一些设置
    }
}码片
  1. Images
在这里插入代package com.nanguadeng.eatsnakes;

import javax.swing.*;
import java.awt.*;

public class StartGame {
    public static void main(String[] args) {



        //创建一个窗体:
        JFrame jf = new JFrame();
        //给窗体设计标题:
        jf.setTitle("贪吃蛇小游戏");
        //获得屏幕宽和高
        int width = Toolkit.getDefaultToolkit().getScreenSize().width;
        int height = Toolkit.getDefaultToolkit().getScreenSize().height;
        //设置窗体弹出对应坐标及对应窗体宽和高
        jf.setBounds((width-800)/2,(height-800)/2,800,800);
        //设置窗体大小不可调节
        jf.setResizable(false);
        //关闭窗口时程序就关闭:
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //创建面版:
        GamePanel gp = new GamePanpackage com.nanguadeng.eatsnakes;

import java.net.URL;
import javax.swing.*;

//Image类专门用来获取游戏中所涉及的图片
public class Images {
    /*因为是面向对象的语言
      所以要将图片封装为一个对象
      这样在程序中才可以通过操纵这个对象来操纵图片 */

    //将图片路径封装为一个对象:
    public static URL bodyURL = Images.class.getResource("/images/body.png");
    //那一个斜杠表示的是相对路径,比如现在的路径file:/D:/java成品/组件及事件处理/eatsnakes/out/production/eatsnakes/
    //将图片封装为一个对象:
    public static ImageIcon bodyImg = new ImageIcon(bodyURL);

    public static URL downURL = Images.class.getResource("/images/down.png");
    public static ImageIcon downImg = new ImageIcon(downURL);

    public static URL foodURL = Images.class.getResource("/images/food.png");
    public static ImageIcon foodImg = new ImageIcon(foodURL);

    public static URL headerURL = Images.class.getResource("/images/header.png");
    public static ImageIcon headerImg = new ImageIcon(headerURL);

    public static URL leftURL = Images.class.getResource("/images/left.png");
    public static ImageIcon leftImg = new ImageIcon(leftURL);

    public static URL upURL = Images.class.getResource("/images/up.png");
    public static ImageIcon upImg = new ImageIcon(upURL);

    public static URL rightURL = Images.class.getResource("/images/right.png");
    public static ImageIcon rightImg = new ImageIcon(rightURL);



}


  1. GamePanel
在这里插入代码片package com.nanguadeng.eatsnakes;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;

//继承了JPanel就是面板了
public class GamePanel extends JPanel {
    //蛇的长度:
    int length;
    //定义两个数组
    //一个专门存储X轴坐标
    int[] snakeX = new int[200];
    //一个专门存储Y轴坐标
    int [] snakeY = new int[200];
    //游戏只有两个状态,开始,暂停:
    boolean isStart = false;//默认暂停
    //加入一个定时器:
    Timer timer;
    //定义蛇的行走方向:
    String direction;
    //定义食物XY坐标
    int foodX;
    int foodY;
    //定义一个积分:
    int score;
    //加入一个变量,判断小蛇死亡状态:
    boolean isDie = false;
    public void init(){
        //初始化蛇的长度:
        length = 3;
        //初始化蛇头坐标:
        snakeX[0] = 175;
        snakeY[0] = 275;
        //初始化第一节身子坐标:
        snakeX[1] = 150;
        snakeY[1] = 275;
        //初始化第二节身子坐标:
        snakeX[2] = 125;
        snakeY[2] = 275;
        //初始化蛇头方向
        direction = "R";//之后将用U D R L表示方向
        //初始化食物坐标:
        foodX = 300;
        foodY = 200;
    }
    public GamePanel(){
        init ();
        //将焦点定位在当前操做面板上:
        this.setFocusable(true);
        //加入监听:
        this.addKeyListener(new KeyAdapter() {//监听按下操做
            public void keyPressed(KeyEvent e) {//监听键盘按键的按下操作
                super.keyPressed(e);
                int keyCode = e.getKeyCode();
                if(keyCode == KeyEvent.VK_SPACE){//监听格键
                    if(isDie){
                        //全部恢复到初始态
                        init();
                        isDie = false;
                    }else {
                        isStart = !isStart;//监听到按下空格后,开始状态变暂停状态,暂停状态变开始状态
                        repaint();//重绘动作
                    }
                }
                //监听向上箭头:
                if(keyCode == KeyEvent.VK_UP){
                    direction = "U";//改变方向
                }
                //监听向下箭头:
                if(keyCode == KeyEvent.VK_DOWN){
                    direction = "D";//改变方向
                }
                //监听向左箭头:
                if(keyCode == KeyEvent.VK_LEFT){
                    direction = "L";//改变方向
                }
                //监听向右箭头:
                if(keyCode == KeyEvent.VK_RIGHT){
                    direction = "R";//改变方向
                }
            }
        });
        //对定时器进行初始化动作:
        timer = new Timer(100, new ActionListener() {
            /*
            ActionListener是 事件监听
            相当于每100毫秒监听一下是否发生了一个动作
            具体动作放入ActionPerforemed
             */
            public void actionPerformed(ActionEvent e) {
                if(isStart && isDie == false){//游戏是开始状态并且不是死亡状态才会动
                    //后一节身子到前一节身子的位置上
                    for(int i = length - 1;i > 0;i--){
                        snakeX[i] = snakeX[i - 1];
                        snakeY[i] = snakeY[i - 1];
                    }
                    //动头:
                    if("R".equals(direction)){
                        snakeX[0] += 25;
                    }
                    if("L".equals(direction)){
                        snakeX[0] -= 25;
                    }
                    if("U".equals(direction)){
                        snakeY[0] -= 25;
                    }
                    if("D".equals(direction)){
                        snakeY[0] += 25;
                    }
                    //防止蛇飞出边界:
                    //向右边界:
                    if(snakeX[0] > 750){
                        snakeX[0] = 25;
                    }
                    //向上边界:
                    if(snakeY[0] < 75 ){
                        snakeY[0] = 725;
                    }
                    //向左边界:
                    if(snakeX[0] < 25){
                        snakeX[0] = 750;
                    }
                    //向下边界:
                    if(snakeY[0] > 725){
                        snakeY[0] = 75;
                    }
                    //检测碰撞的动作:(食物和蛇头坐标相同)
                    if (snakeX[0] == foodX && snakeY[0] ==foodY) {
                        //蛇长加一
                        length++;
                        //食物坐标改变:(随机生成坐标,且必须是25的倍数,不能超过矩形):
                        /*要一个[1,30]的随机数用来乘25
                        Math.random() --> [0.0,1.0)
                        Math.random() * 30 --> [0.0,30.0)
                        (int)(Math.random() * 30) --> [0,29]
                        (int)(Math.random() * 30) + 1 --> [1,30]
                         */
                        foodX = ((int)(Math.random() * 30) + 1) * 25;//[25,750]
                        /*要一个[3,29]的随机数乘25
                        [3,29] --> [0,26] + 3
                        要[0,26]的随机数
                        new Random().nextInt(27) --> [0,27) --> [0,26]
                         */
                        foodY = (new Random().nextInt(27) + 3) * 25;//[75,725]
                        //吃上食物后加10分:
                        score += 10;
                    }
                    //死亡判定:
                    for (int i = 1;i < length;i++){
                        if(snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]){
                            //将死亡状态改为true
                            isDie = true;
                        }
                    }
                    repaint();//刷新,重绘
                }
            }
        });
        //定时器要启动
        timer.start();
    }




    //paintComponent这个方法比较特殊,这个方法属于图形版的main方法,虚拟机自己调用
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        //填充背景颜色
        this.setBackground(new Color(2, 76, 76, 67));
        //画头部图片(当前窗口,G画笔工具,在-60,0处画)
        Images.headerImg.paintIcon(this,g,-60,0);
        //调节画笔颜色:
        g.setColor(new Color(205, 205, 205));
        //画一个矩形:
        g.fillRect(9,70,770,685);

        //画小蛇:
        //画蛇头
        if("R".equals(direction)){
            Images.rightImg.paintIcon(this,g,snakeX[0],snakeY[0]);
        }
        if("L".equals(direction)){
            Images.leftImg.paintIcon(this,g,snakeX[0],snakeY[0]);
        }
        if("U".equals(direction)){
            Images.upImg.paintIcon(this,g,snakeX[0],snakeY[0]);
        }
        if("D".equals(direction)){
            Images.downImg.paintIcon(this,g,snakeX[0],snakeY[0]);
        }

        //循环画蛇身:
        for(int i = 1;i<length;i++){
            Images.bodyImg.paintIcon(this,g,snakeX[i],snakeY[i]);
        }

        //如果游戏暂停,界面中间就应该有提示语:
        if(isStart == false){
            //调颜色
            g.setColor(new Color(172, 113, 222));
            //三个参数:字体,加粗,字号
            g.setFont(new Font("楷体",Font.BOLD,40));
            //画一文字:内容,X轴坐标,Y轴坐标
            g.drawString("点击空格开始游戏",250,330);
        }
        //画食物:
        Images.foodImg.paintIcon(this,g,foodX,foodY);
        //画积分:
        g.setColor(new Color(0, 47, 78));
        g.setFont(new Font("微软雅黑",Font.BOLD,20));
        g.drawString("积分:" + score,620,40);

        //画入死亡状态:
        if(isDie){
            g.setColor(new Color(199, 64, 80));
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("你的蛇死了,按下空格重生",175,330);
        }
    }
}

三、效果实现

2021-04-24
2021-04-24
2021-04-24
2021-04-24

四、素材获得

https://pan.baidu.com/s/1YdUlQ2TFsx_Xwb_OEbcZkA
提取码:y5td

上一篇:YAML基础知识


下一篇:基于java开发贪吃蛇小游戏