java 简单实现贪吃蛇 (Greedy-Snake)图形界面,含切换窗口

Greedy-Snake

此游戏已打包为 .exe 文件,可以直接下载先玩一下 download →要有 jre 环境噢,只要装了java的都有

先看效果图吧,开始游戏界面

在这里插入图片描述

点击 开始 按钮后的效果

在这里插入图片描述

上代码啦

先定义一个启动程序开始我们的代码好吧。

// StartMyGreedySnake.java
public class StartMyGreedySnake {
    public static void main(String[] args) {
        new StartView();	// 启动游戏界面
    }
}

首先设计一个进入游戏的窗口
StartView 类实现

// StartView.java

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class StartView implements MouseListener {
    // 创建开始游戏欢迎界面
    private Frame jf = new Frame("贪吃蛇");
    // 提示性标签
    private JLabel jl;

    // 设置进入游戏按钮
    private JButton jb;

    // 实现按钮方法
    private void setButton() {
        jf.setLayout(null);
        jb = new JButton("开始游戏");   // 按钮提示内容
        jb.setBounds(300, 500, 400, 70);
        jb.setFont(new Font("楷体_2312", Font.BOLD, 50));   // 按钮字体及大小
        jb.setBackground(new Color(177, 120, 205, 205));
        jf.add(jb);      // 加入窗口
        jb.addMouseListener(this);        // 为按钮添加监听
    }

    // 实现标签方法
    private void setLabel() {
        jf.setLayout(null);
        jl = new JLabel("欢迎来到贪吃蛇游戏");       // 标签内容
        jl.setFont(new Font("楷体_2312", Font.BOLD, 30)); // 字体
        jl.setBackground(new Color(196, 34, 169));  // 字体颜色
        jl.setBounds(380, 100, 400, 80); // 相对位置
        jf.add(jl);  // 加到窗口
    }

    StartView() {
        // 设置窗口背景颜色
        jf.setBackground(new Color(190, 44, 79));
        // 设置按钮信息
        setButton();
        // 设置标签并加入窗口
        setLabel();
        // 设置点击右上角的关闭窗口结束程序运行
        jf.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        // 设置窗口绝对位置
        jf.setLocation(500, 200);
        // 设置窗口大小
        jf.setSize(1000, 800);

        // 窗口大小不可变
        jf.setResizable(false);
        // 窗口可见
        jf.setVisible(true);
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        if (e.getSource().equals(jb)) {
            // 进入游戏窗口
            new SnakeGameView().showView();
            // 设置原窗口不可见
            jf.setVisible(false);
            // 释放原窗口资源
            jf.dispose();
        }
    }

    @Override
    public void mousePressed(MouseEvent e) {

    }

    @Override
    public void mouseReleased(MouseEvent e) {

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }
}

然后是实现游戏界面有关的代码

定义一个 Node 节点类用以设置和获得蛇头节点和蛇蛋位置

// Node.java
public class Node {

    private int nodeX;
    private int nodeY;

    public Node(int nodeX, int nodeY) {

        this.nodeX = nodeX;
        this.nodeY = nodeY;
    }

    public int getNodeX() {

        return nodeX;
    }

    public void setNodeX(int nodeX) {

        this.nodeX = nodeX;
    }

    public int getNodeY() {

        return nodeY;
    }

    public void setNodeY(int nodeY) {

        this.nodeY = nodeY;
    }

}

写一个 Snake 类,用链表表示蛇身,并实现蛇的移动,产生蛇蛋和判断蛇是否越界等。

// Snake.java

import java.util.LinkedList;
import java.util.Random;

public class Snake {

    private LinkedList<Node> snakeBody;
    private Random random;
    //Snake的方向    0上, 1 右 , 2 下 , 3 左
    private int direction = 0;

    private SnakeGameView snakeGameView;

    public Snake(int snakeHeadX, int snakeHeadY, SnakeGameView snakeGameView) {
        random = new Random();
        snakeBody = new LinkedList<>();
        snakeBody.add(new Node(snakeHeadX, snakeHeadY));
        this.snakeGameView = snakeGameView;
    }

    //获取Snake的方向
    public int getDirection() {
        return direction;
    }

    //设置Snake方向
    public void setDirection(int direction) {

        this.direction = direction;
    }

    //获取Snake头部结点
    public Node getSnakeHead() {

        return snakeBody.getFirst();
    }

    //获取Snake尾部结点
    public Node getSnakeTail() {

        return snakeBody.getLast();
    }

    //获取Snake的Body
    public LinkedList<Node> getSnakeBody() {

        return snakeBody;
    }

    //Snake移动
    public void snakeMove() {

        switch (direction) {
            case 0:
                snakeBody.addFirst(new Node(getSnakeHead().getNodeX(), getSnakeHead().getNodeY() - DrawView.VIEW_NUMBER));
                break;
            case 1:
                snakeBody.addFirst(new Node(getSnakeHead().getNodeX() + DrawView.VIEW_NUMBER, getSnakeHead().getNodeY()));
                break;
            case 2:
                snakeBody.addFirst(new Node(getSnakeHead().getNodeX(), getSnakeHead().getNodeY() + DrawView.VIEW_NUMBER));
                break;
            case 3:
                snakeBody.addFirst(new Node(getSnakeHead().getNodeX() - DrawView.VIEW_NUMBER, getSnakeHead().getNodeY()));
                break;
        }
        snakeBody.removeLast();
    }

    //Snake 吃Egg
    public void eatEgg(Node egg) {
        if (snakeBody.getFirst().getNodeX() == egg.getNodeX() && snakeBody.getFirst().getNodeY() == egg.getNodeY()) {
            snakeBody.add(egg);
            //产蛋
            snakeGameView.setEgg(random.nextInt(DrawView.VIEW_WIDTH - 1) * DrawView.VIEW_NUMBER,
                    random.nextInt(DrawView.VIEW_WIDTH - 1) * DrawView.VIEW_NUMBER);
            SnakeGameView.gameScore = SnakeGameView.gameScore + 10;
            snakeGameView.getGameScores().setText(SnakeGameView.gameScore + "");
        }
    }

    //Snake越界
    public void snakeRunInterface() {

        if (this.getSnakeHead().getNodeX() < 0 || this.getSnakeHead().getNodeY() < 0 ||
                this.getSnakeHead().getNodeX() > (DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER) ||
                this.getSnakeHead().getNodeY() > (DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER)) {
            SnakeGameView.gameState = false;
        }
    }
}

写一个 GameControl 类,用以获得键盘上方向键和 Enter、Esc 键的功能

// GameControl.java

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class GameControl implements KeyListener {

    private Snake snake;

    public GameControl(Snake snake) {

        this.snake = snake;
    }

    public void keyReleased(KeyEvent ke) {

        switch (ke.getKeyCode()) {

            case KeyEvent.VK_UP:
                snake.setDirection(0);
                break;
            case KeyEvent.VK_RIGHT:
                snake.setDirection(1);
                break;
            case KeyEvent.VK_DOWN:
                snake.setDirection(2);
                break;
            case KeyEvent.VK_LEFT:
                snake.setDirection(3);
                break;
            case KeyEvent.VK_ENTER:

                if (SnakeGameView.gameState == true)
                    SnakeGameView.gameState = false;
                else
                    SnakeGameView.gameState = true;

                break;
            case KeyEvent.VK_ESCAPE:
                System.exit(0);
                break;
        }
    }

    public void keyPressed(KeyEvent arg0) {
    }

    public void keyTyped(KeyEvent arg0) {
    }

}

写一个 DrawView 类,用以画图形界面,包括网格,蛇身,蛇蛋

// DrawView.java

import java.awt.*;

public class DrawView extends Panel {

    //画板的宽度高度
    public static final int VIEW_WIDTH = 40;
    public static final int VIEW_HEIGHT = 40;
    public static final int VIEW_NUMBER = 15;
    private Image iBuffer;
    private Graphics gBuffer;
    private Snake snake;
    private Node egg;

    public DrawView(Snake snake, Node egg) {
        this.snake = snake;
        this.egg = egg;
    }

    public void paint(Graphics g) {
        snake.snakeMove();
        this.drawGridding(g);
        this.drawSnake(g);
        this.drawEgg(g);
        snake.eatEgg(egg);
    }

    public void update(Graphics g) {
        if (iBuffer == null) {
            iBuffer = createImage(DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER + 1, DrawView.VIEW_HEIGHT * DrawView.VIEW_NUMBER + 1);
            gBuffer = iBuffer.getGraphics();
        }
        gBuffer.setColor(getBackground());
        gBuffer.fillRect(0, 0, DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER + 1, DrawView.VIEW_HEIGHT * DrawView.VIEW_NUMBER + 1);
        paint(gBuffer);
        g.drawImage(iBuffer, 0, 0, this);
    }

    //画网格
    public void drawGridding(Graphics g) {

        g.setColor(new Color(105, 84, 68));
        for (int i = 0; i < VIEW_WIDTH; i++) {
            g.drawLine(0, i * VIEW_NUMBER, VIEW_WIDTH * VIEW_NUMBER, i * VIEW_NUMBER);
        }
        for (int i = 0; i < VIEW_HEIGHT; i++) {
            g.drawLine(i * VIEW_NUMBER, 0, i * VIEW_NUMBER, VIEW_WIDTH * VIEW_NUMBER);
        }

    }

    //画Snake
    public void drawSnake(Graphics g) {

        for (int i = 0; i < snake.getSnakeBody().size(); i++) {
            g.setColor(new Color(110, 191, 64));
            if (i == 0)
                g.setColor(new Color(255,255,255));
            g.fillRect(snake.getSnakeBody().get(i).getNodeX(), snake.getSnakeBody().get(i).getNodeY(), VIEW_NUMBER, VIEW_NUMBER);
        }
    }

    //画Egg
    public void drawEgg(Graphics g) {

        g.setColor(Color.yellow);
        g.fillRect(egg.getNodeX(), egg.getNodeY(), VIEW_NUMBER, VIEW_NUMBER);
    }

}

再写一个 GameRunThread 类,用线程控制游戏开始

package greedysnake3;

public class GameRunThread implements Runnable {

    private int runTime = 200;
    private DrawView drawView;
    private Snake snake;

    public GameRunThread(DrawView drawView, Snake snake) {
        this.drawView = drawView;
        this.snake = snake;
    }

    public void run() {

        while (true) {

            snake.snakeRunInterface();

            if (SnakeGameView.gameState) {

                drawView.repaint();

                try {
                    Thread.sleep(runTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }

    }

}

最后写游戏界面类,在这个类里调用其他类的方法以实现游戏功能

// SnakeGameView.java

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Random;

public class SnakeGameView extends Frame implements MouseListener {
    // 定义游戏窗口名
    //JFrame jf = new JFrame("Greedy-Snake");
    // 声明画笔
    private DrawView drawView;
    // 声明开始,重新开始,退出游戏按钮
    JButton start, reset, exit;
    // 声明分数标签
    JLabel grade;
    // 声明分数文本框
    JTextField score;
    // 声明游戏操作方法文本域
    JTextArea hint;

    private Random random;
    public static boolean gameState = true;
    public static int gameScore = 0;
    private Snake snake;
    private Node egg;
    private GameRunThread grt;
    public static Thread thread;

    public SnakeGameView() {
        random = new Random();
        //初始化Snake
        snake = new Snake(10 * DrawView.VIEW_NUMBER + random.nextInt(19) * DrawView.VIEW_NUMBER,
                10 * DrawView.VIEW_NUMBER + random.nextInt(19) * DrawView.VIEW_NUMBER, this);
        //初始化Egg
        egg = new Node(random.nextInt(DrawView.VIEW_WIDTH - 1) * DrawView.VIEW_NUMBER,
                random.nextInt(DrawView.VIEW_WIDTH - 1) * DrawView.VIEW_NUMBER);
        //初始化画板
        drawView = new DrawView(snake, egg);
        grt = new GameRunThread(drawView, snake);
        thread = new Thread(grt);
        // 初始化画板
        setPanel();
        // 设置按钮
        setButton();
        // 设置标签
        setLabel();
        // 设置文本框
        setText();
        // 初始化文本域操作方法
        setJTA();
    }

    //设置鸡蛋
    public void setEgg(int eggX, int eggY) {

        this.egg.setNodeX(eggX);
        this.egg.setNodeY(eggY);
    }

    // 获得分数
    public JTextField getGameScores() {
        return score;
    }

    public void showView() {

        drawView.setBackground(new Color(51, 51, 51));
        drawView.setBounds(20, 50, DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER + 1, DrawView.VIEW_HEIGHT * DrawView.VIEW_NUMBER + 1);
        this.setTitle("Greedy-Snake");
        this.setSize(1000, 800);
        this.setLocation(500, 200);
        this.setLayout(null);
        this.setBackground(new Color(2, 250, 250));
        this.add(drawView);
        this.addKeyListener(new GameControl(snake));
        this.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                System.exit(0);
            }
        });
        this.setVisible(true);
    }

    // 初始化画板
    private void setPanel() {
        drawView = new DrawView(snake, egg);
        grt = new GameRunThread(drawView, snake);
        thread = new Thread(grt);
    }

    // 实现按钮方法
    private void setButton() {
        this.setLayout(null);
        // 设置开始按钮
        start = new JButton("开始");
        start.setBounds(800, 60, 180, 50);
        start.setFont(new Font("宋体", 1, 18));
        this.add(start);
        // 设置重新开始按钮
        reset = new JButton("重新开始");
        reset.setFont(new Font("宋体", 1, 18));
        reset.setBounds(800, 150, 180, 50);
        this.add(reset);
        // 设置退出按钮
        exit = new JButton("退出游戏");
        exit.setFont(new Font("宋体", 1, 18));
        exit.setBounds(800, 240, 180, 50);
        this.add(exit);
        // 初始化按钮添加监听器
        initial();
    }

    // 给各按钮添加监听器
    void initial() {
        start.addMouseListener(this);
        reset.addMouseListener(this);
        exit.addMouseListener(this);
    }

    // 设置标签的方法
    private void setLabel() {
        this.setLayout(null);
        grade = new JLabel("分数");
        grade.setFont(new Font("宋体", 1, 20));
        grade.setBounds(800, 330, 100, 50);
        this.add(grade);
    }

    // 显示分数的方法
    private void setText() {
        this.setLayout(null);
        score = new JTextField("0", JTextField.CENTER);
        score.setFont(new Font("宋体", 1, 20));
        score.setBounds(800, 420, 100, 50);
        score.setEditable(false);
        this.add(score);
    }

    // 显示操作方法
    private void setJTA() {
        this.setLayout(null);
        hint = new JTextArea("操作指导   ← 向左  → 向右  ↑ 向上  ↓ 向下  Esc 退出");
        hint.setFont(new Font("宋体", 0, 24));
        hint.setBackground(new Color(243, 5, 100));
        hint.setBounds(40, 700, 920, 60);
        hint.setEditable(false);
        this.add(hint);
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        if (e.getSource().equals(start)) {
            SnakeGameView newSnake = new SnakeGameView();
            newSnake.initial();
            this.setVisible(false);
            this.dispose();
            new SnakeGameView().showView();
            thread.start();
        }
        if (e.getSource().equals(reset)) {
            SnakeGameView newSnake = new SnakeGameView();
            newSnake.initial();
            this.setVisible(false);
            this.dispose();
            new SnakeGameView().showView();
            thread.start();
        }
        if (e.getSource().equals(exit)) {
            System.exit(0);
        }
    }

    @Override
    public void mousePressed(MouseEvent e) {

    }

    @Override
    public void mouseReleased(MouseEvent e) {

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }
}

注,核心源码摘自 链接

                      本来自己照着想写一个自己的贪吃蛇,奈何学习完后,就差一点点了,还是没能成功,所以最后在上面大佬的代码上加以改进写出了这个还是带有 bug 的贪吃蛇小游戏,望读者尽力闭坑,不过这里面的很多东西还是值得加以学习的
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值