JAVA的GUI练手项目,制作小游戏----贪吃蛇游戏(源自“跟狂神学java”)


前言

在这里我会用JAVA写一个贪吃蛇的小游戏,这个的制作过程及创意都是来自于“秦疆老师,他的java课程在www.kuangstudy.com"有兴趣的同学可以去看看,也可以在哔哩哔哩上找到他的视频,关注“遇见狂神说”即可。


现在,我们开始了!!

一、前期的准备工作

  • 设置窗口及图形的大小

    • 最外层窗口设置为:900X720
    • 内部游戏运行的面板设置为:850X600
    • 蛇的头部、身体、食物设置为 25X25 大小的png图片。(这个可以用画图工具自己编辑一个的。)
    • 画布上方自己弄一个图片,在这个图片上后期会加载吃食物的个数及获得的分数,这个图片的大小为600*40
  • 以下是我自己弄得图片你们可以参考一下
    向右的蛇脑袋
    向下的蛇脑袋
    向左的蛇脑袋
    向上的蛇脑袋
    蛇的身体
    食物

    头部的图片

二、设置包及代码的架构及具体代码

1.在项目中建立一个snake的包,文件结构如下:在这里插入图片描述

这里说明一下包内的东西哈

  • 首先static文件夹中存放的是图片,body.png是蛇的身体,down.png,up.png,right.png,left.png这几个是蛇头的四个方向所对应的图片,food.png是吃的食物的图片,header.png是游戏区域上方的图片,后期可以在这个图片上添加文字及得分,时间等信息。
  • Data文件是一个数据类,主要是构造了一个读取图片文件的功能,后期可以通过这个类直接获取图片信息,当然不用这个也可以,直接在代码中通过设置变量获取都可以,不过这样放的好处是方便后期维护,也算是一个写代码的好习惯吧
  • GamePanel 中存放的是具体的运行代码
package snake;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

/**
 * @author marker_sun
 * @version 1.0
 * @since 1.8
 */
public class GamePanel extends JPanel implements KeyListener, ActionListener {
    // 定义常量
    /** 蛇的方向 {@value}*/
    private final char RIGHT = 'r';
    private final char LEFT = 'l';
    private final char UP = 'u';
    private final char DOWN = 'd';

    // 定义蛇的数据结构
    /** 这是蛇的长度{@value}*/
    int length;
    /** 蛇的X坐标系{@value}*/
    int[] snakeX = new int[600];
    /** 蛇的Y坐标系{@value}*/
    int[] snakeY = new int[600];
    char fx;

    /** 食物的坐标系{@value}*/
    int foodX;
    int foodY;

    /**
     * 计分
     */
    short score;

    /**
     * 获取一个随机数
     */
    Random random = new Random();

    /**
     *判断游戏状态: 1:开始  2:暂停  3:死亡 0:初始化
    */
    byte isStart = 0;

    /**
     * 定时器 // 每100毫秒刷新一次
     */
    Timer timer = new Timer(100, this);


    /**
     * 一个构造器
      */
    public GamePanel() {
        init();
        // 获得焦点和键盘事件
        // 获得焦点事件
        this.setFocusable(true);
        // 获得键盘监听事件
        this.addKeyListener(this);
        // 游戏开始定时器启动
        timer.start();
    }


    /**
     * 初始化值
     */
    public void init() {
        length = 3;
        // 蛇脑袋的初始位置
        snakeX[0] = 100;
        snakeY[0] = 100;
        // 蛇第一节身体的初始位置
        snakeX[1] = 75;
        snakeY[1] = 100;
        // 蛇第二节身体的初始位置
        snakeX[2] = 50;
        snakeY[2] = 100;

        // 食物随机分布在界面上
        foodX = 25 + 25 * random.nextInt(34);
        foodY = 75 + 25 * random.nextInt(24);

        isStart = 0;

        fx = RIGHT;

        score = 0;
    }


    /**
     * 设置一个画笔,接下来的所有内容都是用这个画笔来完成绘画
      */
    @Override
    protected void paintComponent(Graphics g) {
        // 这个代码的作用是清屏,如果没有这个会导致画面闪烁
        super.paintComponent(g);
        this.setBackground(Color.WHITE);
        // 将静态的图片绘制到面板上
        Data.header.paintIcon(this, g, 25, 10);
        // 默认的游戏界面
        g.fillRect(25, 75, 850, 600);

        //画食物
        Data.food.paintIcon(this, g, foodX, foodY);

        //画分数
        g.setColor(Color.black);
        g.setFont(new Font("微软雅黑", Font.BOLD, 15));
        g.drawString("长度为:"+length,650,35);
        g.drawString("分数为:"+ score,650,55);

        // 把初始的蛇画到面板上
        // 画蛇的头
        switch (fx) {
            case RIGHT:
                Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
                break;
            case LEFT:
                Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
                break;
            case UP:
                Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
                break;
            case DOWN:
                Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
                break;
            default:
        }
        // 蛇的身体
        for (int i = 1; i < length; i++) {
            Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);
        }


        // 游戏状态
        if (isStart==0 || isStart == 2) {
            g.setColor(Color.white);
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("按下空格,开始游戏!", 300, 300);
        }

        // 死亡状态
        if (isStart == 3){
            g.setColor(Color.red);
            g.setFont(new Font("微软雅黑", Font.BOLD, 35));
            g.drawString("您已失败,请按Enter键重新开始!!",150,300);
        }
    }

    /**
     *   键盘监听事件
     */
    @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        if (keyCode == KeyEvent.VK_SPACE) {
            switch (isStart){
                case 0:isStart = 1;
                break;
                case 1: isStart = 2;
                break;
                case 2: isStart = 1;
                default:
            }
            // 重新绘制
            repaint();
        }

        if (keyCode == KeyEvent.VK_ENTER) {
            this.init();
            repaint();
        }
        // 小蛇移动
        switch (keyCode) {
            case KeyEvent.VK_UP:
                if (fx != DOWN) {
                    fx = UP;
                }
                break;
            case KeyEvent.VK_DOWN:
                if (fx != UP) {
                    fx = DOWN;
                }
                break;
            case KeyEvent.VK_LEFT:
                if (fx != RIGHT) {
                    fx = LEFT;
                }
                break;
            case KeyEvent.VK_RIGHT:
                if (fx != LEFT) {
                    fx = RIGHT;
                }
                break;
            default:
        }

    }

    /**
     * 事件监听 -- 需要通过固定事件来刷新, 1秒10次
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        int boundaryLeft = 25;
        int boundaryRight = 850;
        int boundaryUp = 75;
        int boundaryDown = 650;

        if (isStart==1) {
            // 吃食物
            if (snakeX[0] == foodX && snakeY[0] == foodY) {
                //长度加1
                length++;
                // 分数加10分
                score += 10;
                // 再次随机食物
                foodX = 25 + 25 * random.nextInt(34);
                foodY = 75 + 25 * random.nextInt(24);


            }

            // 这里注意,要是头写到身体上面,身体就会覆盖头
            // 移动身体

            for (int i = length - 1; i > 0; i--) {
                snakeX[i] = snakeX[i - 1];
                snakeY[i] = snakeY[i - 1];
                }


            //头转向
            switch (fx) {
                case RIGHT:
                    snakeX[0] += 25;
                    if (snakeX[0] > boundaryRight) {
                        snakeX[0] = boundaryLeft;
                    }// 边界判断
                    break;
                case LEFT:
                    snakeX[0] -= 25;
                    if (snakeX[0] < boundaryLeft) {
                        snakeX[0] = boundaryRight;
                    }// 边界判断
                    break;
                case UP:
                    snakeY[0] -= 25;
                    if (snakeY[0] < boundaryUp) {
                        snakeY[0] = boundaryDown;
                    }// 边界判断
                    break;
                case DOWN:
                    snakeY[0] += 25;
                    if (snakeY[0] > boundaryDown) {
                        snakeY[0] = boundaryUp;
                    }// 边界判断
                    break;
                default:
            }


            repaint(); //重画画面
        }
        // 死亡判断
        if(isStart != 3) {
            for (int i = length - 1; i > 0; i--) {
                // 死亡判定
                if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
                    isStart = 3;
                }
            }
        }
        timer.start(); //定时器开启
    }


    @Override
    public void keyReleased(KeyEvent e) {

    }

    @Override
    public void keyTyped(KeyEvent e) {

    }


}

  • GameStart这个是游戏的启动文件,具体代码如下
package snake;

import javax.swing.*;


/**
 * @author marker_sun
 * @version 1.0
 * @since 1.8
 */
public class GameStart {
    public static void main(String[] args) {
        JFrame frame = new JFrame();


        // 将面板放置到主窗体中
        frame.add(new GamePanel());

        frame.setTitle("贪吃蛇");
        // 设置外窗体的大小及初始位置
        frame.setBounds(20,20,900,750);
        // 设置窗口大小不可变
        frame.setResizable(false);
        // 点击窗口的关闭按钮可以关闭窗口
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        // 使窗口调整为可见状态
        frame.setVisible(true);

    }
}

2、项目运行起来的样子如下:

  • 开始的样子在这里插入图片描述
  • 结束的样子在这里插入图片描述

总结

至此,这个小项目就完成了。
说明:

  1. GamePanel中的代码我用阿里巴巴的规范进行了调整,可能不太好,大家可以参考一下,我觉得大家平时写代码的时候就用这个规范去书写,对我们自身的帮助还是很大的。
  2. GamePanel中狂神对转向用的是 if 语法,我改成了swith…case…我觉得在这种地方,用swith会更加好一点,如果我的这个改动不好请大家告诉我,我好改正。
  3. 最后,谢谢大家观看我写的这个项目,初次写,有不对及不好的地方欢迎大家批评指正。感谢大家
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值