GUI实现贪吃蛇小游戏

写在前面:这是一个学习JavaGUI以及监视器的一个小程序,纯学习娱乐使用.

参考自:狂神说一小时写一个贪吃蛇小游戏

一,绘制一个静态窗口

        // 1.绘制静态窗口 JFrame
        JFrame frame = new JFrame("贪吃蛇");
        // 设置界面大小
        frame.setBounds(10, 10, 900, 720);
        // 设置窗口大小不可改变
        frame.setResizable(false);
        // 设置关闭事件,游戏可关闭;
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 2.面板
        frame.add(new GamePanel());

        // 展现窗口
        frame.setVisible(true);

二.绘制游戏界面和贪吃蛇

1,创建一个面板类,继承JPanel,重写paintComponent(Graphics g)方法

2,将面板类添加到窗口上.如上代码所示

3.引入静态资源

    // 头部的图片 URL
    public static URL headerUrl = Data.class.getResource("/static/header.png");
    public static ImageIcon header = new ImageIcon(headerUrl);

    // 蛇头的图片 URL
    public static URL upUrl = Data.class.getResource("/static/up.png");
    public static ImageIcon up = new ImageIcon(upUrl);
    public static URL leftUrl = Data.class.getResource("/static/left.png");
    public static ImageIcon left = new ImageIcon(leftUrl);
    public static URL rightUrl = Data.class.getResource("/static/right.png");
    public static ImageIcon right = new ImageIcon(rightUrl);
    public static URL downUrl = Data.class.getResource("/static/down.png");
    public static ImageIcon down = new ImageIcon(downUrl);

    // 蛇身体的图片 URL
    public static URL bodyUrl = Data.class.getResource("/static/body.png");
    public static ImageIcon body = new ImageIcon(bodyUrl);

    // 食物的图片
    public static URL foodUrl = Data.class.getResource("/static/food.png");
    public static ImageIcon food = new ImageIcon(foodUrl);

4,画一条静态的小蛇

ImageIcon类的paintIcon(Component c, Graphics g, int x, int y)方法可以将我们的图片资源画到游戏界面上.其中Component为容器,这里就是我们的面板,Graphics是画笔,x是横坐标,y是纵坐标
        // 根据方向控制蛇的方向
        if (fx == "U") {
            Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx == "D") {
            Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx == "L") {
            Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx == "R") {
            Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
        }

        for (int i = 1; i < length; i++) {
            // 蛇的身体长度通过length来控制
            Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);
        }

 5,在游戏界面上显示一串提示文字

// 画一个文字,String
// 设置画笔的颜色
g.setColor(Color.white);
// 设置字体
g.setFont(new Font("微软雅黑", Font.BOLD, 40));
g.drawString("按下空格开始游戏", 300, 300);

通过对画笔Graphics设置不同的属性,可以实现在JPanel上显示我们想要的文字以及文字样式

既然都显示了通过空格来控制游戏的开始了,那么就添加一个键盘监听器,来实现这个功能吧

①让我们的GamePanel实现KeyListener接口,重写keyPressed(KeyEvent e) 方法

    // 键盘监听
    // 键盘压下事件
    @Override
    public void keyPressed(KeyEvent e) {

        // 获取键盘按下的是哪一个键
        int keyCode = e.getKeyCode();

        // 如果按下的是空格建
        if (keyCode == KeyEvent.VK_SPACE) {
            isStart = !isStart;
            // 刷新界面
            repaint();
        }
    }

②将监听器添加到GamePanel类的构造器中

    // 构造器
    public GamePanel() {
        init();

        // 获取键盘的监听事件
        // 获取键盘的焦点
        this.setFocusable(true);
        this.addKeyListener(this);
    }

 6,让小蛇动起来,通过定时器来实现

首先在键盘监听中添加方向控制

       // 键盘控制走向
        if (keyCode == KeyEvent.VK_LEFT) {
            fx = "L";
        } else if (keyCode == KeyEvent.VK_RIGHT) {
            fx = "R";
        } else if (keyCode == KeyEvent.VK_UP) {
            fx = "U";
        } else if (keyCode == KeyEvent.VK_DOWN) {
            fx = "D";
        }

然后添加定时器

①在GamePanel添加timer属性

Timer timer = new Timer(100, this);
定时器的构造方法Timer(int delay, ActionListener listener)中delay为刷新间隔100即是一秒钟刷新十次;listener即监听对象.

②GamePanel类实现ActionListener接口,重写actionPerformed(ActionEvent e)方法

    // 定时器,监听时间,帧: 执行定时操作
    @Override
    public void actionPerformed(ActionEvent e) {
        // 如果游戏是开始状态,并且游戏没有结束
        if (isStart && isFailed == false) {
            // 身体移动
            for (int i = length - 1; i > 0; i--) {// 除了脑袋,身体都向前移动
                snakeX[i] = snakeX[i - 1];
                snakeY[i] = snakeY[i - 1];
            }

            // 通过方向让头部移动
            if (fx.equals("R")) {
                // 头部移动
                snakeX[0] = snakeX[0] + 25;
                // 边界判断
                if (snakeX[0] > 850) {
                    snakeX[0] = 25;
                }
            } else if (fx.equals("L")) {
                // 头部移动
                snakeX[0] = snakeX[0] - 25;
                // 边界判断
                if (snakeX[0] < 25) {
                    snakeX[0] = 850;
                }
            } else if (fx.equals("U")) {
                // 头部移动
                snakeY[0] = snakeY[0] - 25;
                // 边界判断
                if (snakeY[0] < 75) {
                    snakeY[0] = 650;
                }
            } else if (fx.equals("D")) {
                // 头部移动
                snakeY[0] = snakeY[0] + 25;
                // 边界判断
                if (snakeY[0] > 650) {
                    snakeY[0] = 75;
                }
            }

            // 刷新界面
            repaint();
        }

        // 让时间动起来
        timer.start();
    }

另外别忘记了在GamePanel的构造器中也加上

timer.start();

要不然定时器就起不到定时的效果了.

到现在为止,小蛇已经可以动起来了.接下来就添加一些功能就好了.

三,添加吃食物会小蛇变长,和积分功能.

1,定义食物和积分的属性

    // 定义食物
    int foodX;
    int foodY;
    Random random = new Random();


    // 积分
    int score;

2,初始化食物和积分

        // 初始化食物
        foodX = 25 + 25 * random.nextInt(34);
        foodY = 75 + 25 * random.nextInt(24);


        // 初始化积分
        score = 0;

3,在面板上画出食物和积分

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

        // 画积分
        // 画一个文字,String
        // 设置画笔的颜色
        g.setColor(Color.white);
        // 设置字体
        g.setFont(new Font("微软雅黑", Font.BOLD, 18));
        g.drawString("长度: " + length, 750, 35);
        g.drawString("分数: " + score, 750, 50);

4,在时间监听中添加吃食物会变长,积分增长的功能

            // 如果小蛇的头,和食物的坐标重合了
            if (snakeX[0] == foodX && snakeY[0] == foodY) {
                // 长度加一
                length++;

                // 分数增加
                score = score + 10;

                // 重新生成食物
                foodX = 25 + 25 * random.nextInt(34);
                foodY = 75 + 25 * random.nextInt(24);
            }

四,添加游戏结束的功能

1,添加失败判断属性

    // 失败判断
    boolean isFailed;

2,初始化失败判断

        // 游戏失败判断初始化
        isFailed = false;

3,界面上显示提示信息

        // 游戏提示:游戏失败
        if (isFailed) {
            // 画一个文字,String
            // 设置画笔的颜色
            g.setColor(Color.white);
            // 设置字体
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("游戏失败,按下空格开始游戏", 200, 300);
        }

4,键盘监听器添加重新开始的逻辑

        // 如果按下的是空格建
        if (keyCode == KeyEvent.VK_SPACE) {
            if (isFailed) {
                init();
            } else {
                isStart = !isStart;
            }
            // 刷新界面
            repaint();
        }

5,时间监视中添加游戏失败逻辑

        // 如果游戏是开始状态,并且游戏没有结束
        if (isStart && isFailed == false) {
            // 身体移动
            for (int i = length - 1; i > 0; i--) {// 除了脑袋,身体都向前移动
                snakeX[i] = snakeX[i - 1];
                snakeY[i] = snakeY[i - 1];
            }

            // 通过方向让头部移动
            if (fx.equals("R")) {
                // 头部移动
                snakeX[0] = snakeX[0] + 25;
                // 边界判断
                if (snakeX[0] > 850) {
                    snakeX[0] = 25;
                }
            } else if (fx.equals("L")) {
                // 头部移动
                snakeX[0] = snakeX[0] - 25;
                // 边界判断
                if (snakeX[0] < 25) {
                    snakeX[0] = 850;
                }
            } else if (fx.equals("U")) {
                // 头部移动
                snakeY[0] = snakeY[0] - 25;
                // 边界判断
                if (snakeY[0] < 75) {
                    snakeY[0] = 650;
                }
            } else if (fx.equals("D")) {
                // 头部移动
                snakeY[0] = snakeY[0] + 25;
                // 边界判断
                if (snakeY[0] > 650) {
                    snakeY[0] = 75;
                }
            }

            // 如果小蛇的头,和食物的坐标重合了
            if (snakeX[0] == foodX && snakeY[0] == foodY) {
                // 长度加一
                length++;
                // 分数增加
                score = score + 10;

                // 重新生成食物
                foodX = 25 + 25 * random.nextInt(34);
                foodY = 75 + 25 * random.nextInt(24);
            }

            // 结束判断
            for (int i = 1; i < length; i++) {
                if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
                    // 游戏结束
                    isFailed = true;
                }

            }
            // 刷新界面
            repaint();
        }

        // 让时间动起来
        timer.start();
    }

最后,如果想添加其他功能,只需要按照上述添加吃食物和积分功能,以及失败判断功能,按步骤添加即可.

完整代码文件如下

StartGames
package com.liu.snake;

import javax.swing.*;

/**
 * @PackageName:com.liu.snake
 * @ClassName: StartGames
 * @Description:
 * @Author:
 * @Date:2021/8/2919:53
 */
public class StartGames {
    public static void main(String[] args) {
        // 1.绘制静态窗口 JFrame
        JFrame frame = new JFrame("贪吃蛇");
        // 设置界面大小
        frame.setBounds(10, 10, 900, 720);
        // 设置窗口大小不可改变
        frame.setResizable(false);
        // 设置关闭事件,游戏可关闭;
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 2.面板
        frame.add(new GamePanel());

        // 展现窗口
        frame.setVisible(true);
    }
}
Data
package com.liu.snake;

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

/**
 * @PackageName:com.liu.snake
 * @ClassName: Data
 * @Description: 存放外部数据
 * @Author:
 * @Date:2021/8/2920:05
 */
public class Data {
    // 头部的图片 URL
    public static URL headerUrl = Data.class.getResource("/static/header.png");
    public static ImageIcon header = new ImageIcon(headerUrl);

    // 蛇头的图片 URL
    public static URL upUrl = Data.class.getResource("/static/up.png");
    public static ImageIcon up = new ImageIcon(upUrl);
    public static URL leftUrl = Data.class.getResource("/static/left.png");
    public static ImageIcon left = new ImageIcon(leftUrl);
    public static URL rightUrl = Data.class.getResource("/static/right.png");
    public static ImageIcon right = new ImageIcon(rightUrl);
    public static URL downUrl = Data.class.getResource("/static/down.png");
    public static ImageIcon down = new ImageIcon(downUrl);

    // 蛇身体的图片 URL
    public static URL bodyUrl = Data.class.getResource("/static/body.png");
    public static ImageIcon body = new ImageIcon(bodyUrl);

    // 食物的图片
    public static URL foodUrl = Data.class.getResource("/static/food.png");
    public static ImageIcon food = new ImageIcon(foodUrl);
}
GamePanel
package com.liu.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;

/**
 * @PackageName:com.liu.snake
 * @ClassName: GamePanel
 * @Description:
 * @Author:
 * @Date:2021/8/2920:00
 */
public class GamePanel extends JPanel implements KeyListener, ActionListener {

    // 蛇的长度
    int length;
    // 蛇的坐标
    int[] snakeX = new int[600];
    int[] snakeY = new int[500];

    //方向: 上"R"下"D"左"L"右"U"
    String fx;

    // 游戏是否开始
    boolean isStart;

    // 定时器
    Timer timer = new Timer(100, this);

    // 定义食物
    int foodX;
    int foodY;
    Random random = new Random();

    // 失败判断
    boolean isFailed;

    // 积分
    int score;

    // 构造器
    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;

        // 方向
        fx = "R";

        // 游戏是否开始
        isStart = false;

        // 初始化食物
        foodX = 25 + 25 * random.nextInt(34);
        foodY = 75 + 25 * random.nextInt(24);

        // 游戏是否失败
        isFailed = false;

        // 初始化积分
        score = 0;
    }

    // 画板: 画界面,画蛇
    // Graphics: 画笔
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); // 清屏
        // 设置背景颜色
        this.setBackground(Color.white);

        // 绘制头部广告栏
        Data.header.paintIcon(this, g, 25, 11);

        // 绘制游戏区域
        g.fillRect(25, 75, 850, 600);

        // 画一条静态的小蛇
        // 根据方向控制蛇的方向
        if (fx == "U") {
            Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx == "D") {
            Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx == "L") {
            Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx == "R") {
            Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
        }

        for (int i = 1; i < length; i++) {
            // 蛇的身体长度通过length来控制
            Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);
        }

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

        // 游戏提示:是否开始
        if (isStart == false) {
            // 画一个文字,String
            // 设置画笔的颜色
            g.setColor(Color.white);
            // 设置字体
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("按下空格开始游戏", 300, 300);
        }

        // 游戏提示:游戏失败
        if (isFailed) {
            // 画一个文字,String
            // 设置画笔的颜色
            g.setColor(Color.white);
            // 设置字体
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("游戏失败,按下空格开始游戏", 200, 300);
        }

        // 画积分
        // 画一个文字,String
        // 设置画笔的颜色
        g.setColor(Color.white);
        // 设置字体
        g.setFont(new Font("微软雅黑", Font.BOLD, 18));
        g.drawString("长度: " + length, 750, 35);
        g.drawString("分数: " + score, 750, 50);
    }


    // 键盘监听
    // 键盘压下事件
    @Override
    public void keyPressed(KeyEvent e) {

        // 获取键盘按下的是哪一个键
        int keyCode = e.getKeyCode();

        // 如果按下的是空格建
        if (keyCode == KeyEvent.VK_SPACE) {
            if (isFailed) {
                init();
            } else {
                isStart = !isStart;
            }
            // 刷新界面
            repaint();
        }

        // 键盘控制走向
        if (keyCode == KeyEvent.VK_LEFT) {
            fx = "L";
        } else if (keyCode == KeyEvent.VK_RIGHT) {
            fx = "R";
        } else if (keyCode == KeyEvent.VK_UP) {
            fx = "U";
        } else if (keyCode == KeyEvent.VK_DOWN) {
            fx = "D";
        }
    }

    // 定时器,监听时间,帧: 执行定时操作
    @Override
    public void actionPerformed(ActionEvent e) {
        // 如果游戏是开始状态,并且游戏没有结束
        if (isStart && isFailed == false) {
            // 身体移动
            for (int i = length - 1; i > 0; i--) {// 除了脑袋,身体都向前移动
                snakeX[i] = snakeX[i - 1];
                snakeY[i] = snakeY[i - 1];
            }

            // 通过方向让头部移动
            if (fx.equals("R")) {
                // 头部移动
                snakeX[0] = snakeX[0] + 25;
                // 边界判断
                if (snakeX[0] > 850) {
                    snakeX[0] = 25;
                }
            } else if (fx.equals("L")) {
                // 头部移动
                snakeX[0] = snakeX[0] - 25;
                // 边界判断
                if (snakeX[0] < 25) {
                    snakeX[0] = 850;
                }
            } else if (fx.equals("U")) {
                // 头部移动
                snakeY[0] = snakeY[0] - 25;
                // 边界判断
                if (snakeY[0] < 75) {
                    snakeY[0] = 650;
                }
            } else if (fx.equals("D")) {
                // 头部移动
                snakeY[0] = snakeY[0] + 25;
                // 边界判断
                if (snakeY[0] > 650) {
                    snakeY[0] = 75;
                }
            }

            // 如果小蛇的头,和食物的坐标重合了
            if (snakeX[0] == foodX && snakeY[0] == foodY) {
                // 长度加一
                length++;
                // 分数增加
                score = score + 10;

                // 重新生成食物
                foodX = 25 + 25 * random.nextInt(34);
                foodY = 75 + 25 * random.nextInt(24);
            }

            // 结束判断
            for (int i = 1; i < length; i++) {
                if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
                    // 游戏结束
                    isFailed = true;
                }

            }
            // 刷新界面
            repaint();
        }

        // 让时间动起来
        timer.start();
    }


    // 键盘压下 + 释放 事件
    @Override
    public void keyTyped(KeyEvent e) {
    }

    // 键盘释放事件
    @Override
    public void keyReleased(KeyEvent e) {
    }

}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值