欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-03-09 08:13:18
Greedy-Snake先看效果图吧,开始游戏界面点击 开始 按钮后的效果上代码啦先定义一个启动程序开始我们的代码好吧。// StartMyGreedySnake.javapublic class StartMyGreedySnake { public static void main(String[] args) { new StartView();// 启动游戏界面 }}首先设计一个进入游戏的窗口用 StartView 类实现//...

Greedy-Snake

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

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

点击 开始 按钮后的效果

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

上代码啦

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

// 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 的贪吃蛇小游戏,望读者尽力闭坑,不过这里面的很多东西还是值得加以学习的

本文地址:https://blog.csdn.net/weixin_48033173/article/details/110941866