Java实现贪吃蛇小游戏项目分析和源代码(多线程,双向链表)

“现实会告诉你,不努力就会被生活给踩死。无需找什么借口,一无所有,就是拼的理由。”

01.游戏整体设计

游戏应具有的对象:

1.蛇类

2.食物类

3.游戏控制类

4.节点类

5.游戏界面类

效果:
在这里插入图片描述

02.蛇类设计(Snake)

1.属性:

蛇身:用双向链表表示蛇身每一个节点的逻辑坐标,和对应节点的颜色。节点的数量也就代表了蛇的长度。

方向:蛇头的方向。

生长标记:生长标记,用于判断蛇是否吃到食物,判断其是否生长。

2.方法:

方法方法名和参数实现方法
初始化pulic Snake(int length)
移动public void move()加头去尾
生长public void grow()设置成长标记,如果为生长,则不去尾部
转向public void
判断蛇是否吃到食物public boolean eatFood(Food food)
判断蛇是否吃到自己public boolean eatSelf()
判断蛇是否碰到墙public boolean meetWall(Grid grid)
代码:
package HomeWork.GreedySnake;

import java.awt.*;
import java.util.LinkedList;

public class Snake {
    public final static  byte UP = 1;
    public final static  byte DOWN = -1;
    public final static  byte  LEFT= 2;
    public final static  byte RIGHT = -2;
    private LinkedList<Node> body;//蛇身(节点的集合)
    private byte direction;//蛇头当前移动的方向
    private static boolean growFlag = false;

    /**
     * 构造函数,初始化一条指定长度,并且向右移动的蛇
     * @param length 蛇的长度
     */
    public Snake(int length) {
        body = new LinkedList<>();
        for(int col = length-1;col>=0;col--){
            Color color = col==length-1?Color.YELLOW:Color.BLUE;
            Node node = new Node(0,col,color);
            body.add(node);
        }
        direction = RIGHT;
    }

    /**
     * 移动
     */
    public void move(){
        //思路加头去尾

        //加头(1)复制头(2)根据方向调整位置 (3)加入蛇身
        Node head = body.getFirst();
        Node newHead = new Node(head);
        head.color = Color.BLUE;
        switch (direction){
            case UP:
                newHead.row--;break;
            case DOWN:
                newHead.row++;break;
            case LEFT:
                newHead.col--;break;
            case RIGHT:
                newHead.col++;break;

        }
        body.addFirst(newHead);
        if(growFlag) {//根据生长标记,判断是否生长
           growFlag = false;//将生长标记回置,让蛇只长一次
        } else {
            body.removeLast();//去尾
        }
    }

    /**
     * 生长
     */
    public void grow(){
        /**
         * 思路:头部加可能越界
         * 尾部加又无法确定三种情形到底是哪一种
         * 所以考虑设置生长标记,当有生长标记时,我们只加头不去尾
         * 当没有生长标记时,加头去尾
         */
        growFlag = true;
    }

    /**
     * 转向
     * @param d 新的方向
     */
    public void turn(byte d){
        if(direction != -d) {
            direction = d;
        }
    }


    /**
     * 判断蛇是否吃到食物
     * @param food 食物
     * @return true吃到了,false没有吃到
     */
    public boolean eatFood(Food food){
        Node head = body.getFirst();
        Node node = food.getNode();
        return head.equals(node);
    }
    /**
     * 判断蛇是否吃到自己
     * @return true吃到了,false没有吃到
     */
    public boolean eatSelf(){
        Node head = body.getFirst();

        for (int i=1;i<body.size();i++){
            Node node = body.get(i);
            if(node.equals(head)){
                return true;
            }
        }
        return false;
    }


    /**
     * 判断蛇是否碰到墙
     * @param grid 网格对象
     * @return true碰到了,false没有碰到
     */
    public boolean meetWall(Grid grid){
       /* int rows = grid.getRows();
        int cols = grid.getCols();
        Node head = body.getFirst();
        return head.row<0||head.row>=rows||head.col<0||head.col>=cols;*/
        return grid.outOfBound(body.getFirst());
    }

    public LinkedList<Node> getBody() {
        return body;
    }
}

03.食物类设计(Food)

属性:食物用一个节点表示。

方法:食物随机初始化。

程序代码:

package HomeWork.GreedySnake;

import java.awt.*;
import java.util.List;

public class Food {
    private Node node;//节点

    /**
     * 食物的随机初始化
     * @param maxRow 最大行号
     * @param maxCol 最大列号
     * @param nodes 不能出现的节点集合(蛇的节点)
     */
    public Food(int maxRow, int maxCol, List<Node> nodes){
        boolean flag = true;
        while(flag){
            int row = (int)(Math.random()*(maxRow+1));
            int col = (int)Math.random()*(maxCol+1);
            node =new Node(row,col, Color.RED);

            flag = false;
            for(Node node1:nodes){
                if (node.equals(node1)){//如果食物出现的位置在蛇身的位置
                    flag = true;
                    break;
                }
            }
        }
    }

    public Node getNode() {
        return node;
    }
}

04.节点类(Node)

属性属性名
行号row
列号col
颜色color

方法:
重写equals方法

程序代码:

package HomeWork.GreedySnake;

import java.awt.*;
import java.util.Objects;

public class Node {
    public int row;//行号
    public int col;//列号
    public Color color;//颜色

    public Node(int row, int col, Color color) {
        this.row = row;
        this.col = col;
        this.color = color;
    }

    /**
     * 拷贝构造函数
     * @param node 节点
     */
    public Node (Node node){
        this.row = node.row;
        this.col = node.col;
        this.color = node.color;

    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Node node = (Node) o;
        return row == node.row &&
                col == node.col;
    }

    @Override
    public int hashCode() {
        return Objects.hash(row, col, color);
    }

    public int getRow() {
        return row;
    }

    public void setRow(int row) {
        this.row = row;
    }

    public int getCol() {
        return col;
    }

    public void setCol(int col) {
        this.col = col;
    }

    public Color getColor() {
        return color;
    }

    public void setColor(Color color) {
        this.color = color;
    }
}

05.游戏界面类(Grid)

属性属性名
行数rows
列数cols
格子大小size
边距margin
package HomeWork.GreedySnake;

public class Grid {
    private int rows;//行数
    private int cols;//列数
    private int size;//格子大小


    public Grid(int rows, int cols, int size) {
        this.rows = rows;
        this.cols = cols;
        this.size = size;
    }

    public int getRows() {
        return rows;
    }

    public void setRows(int rows) {
        this.rows = rows;
    }

    public int getCols() {
        return cols;
    }

    public void setCols(int cols) {
        this.cols = cols;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public boolean outOfBound(Node head) {
        return head.row<0||head.row>=rows||head.col<0||head.col>=cols;
    }

    public int getWidth() {
        return size *cols;
    }

    public int getHight() {
        return size*rows;
    }
}

06.游戏控制类(Game)

游戏类继承Thread类

属性属性名
snake
食物food
网格grid
窗口frame
画布canvas
绘图对象graphics
按钮button
创建一个线程,如果只有一个线程,当蛇移动,我们是通过重画网格,食物,蛇来实现的,这样会导致闪频。所以需要新建一个线程。
方法方法名
绘制网格drawGrid()
绘制蛇drawSnake()
绘制食物drawFood()
绘制节点drawNode()
线程方法run()
因为蛇和食物都是节点组成的,所以画蛇和食物可以通过画节点完成,减少了代码的冗余。 **程序代码:**
在这里插入代码片package HomeWork.GreedySnake;


import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.LinkedList;

public class Game extends Thread{
    private Snake snake;//蛇
    private Food food;//食物
    private Grid grid;//网格

    private static Frame frame;//窗体
    private static Canvas canvas;//画布
    private Graphics graphics;//绘图对象
    private Button button;//按钮

    public static void main(String[] args) {
        Game game = new Game(30,30,25,5);
    }

    @Override
    public void run() {
        while (true){
            snake.move();
            drawGrid();
            drawFood();
            drawSnake();

            if(snake.eatFood(food)){//吃食物
                snake.grow();
                food = new Food(grid.getRows()-1,grid.getCols()-1,snake.getBody());
            }

            if(snake.eatSelf()||snake.meetWall(grid)){
                JOptionPane.showMessageDialog(null, "游戏结束!", "游戏结束",JOptionPane.INFORMATION_MESSAGE);
                System.exit(0);
                //结束
                break;

            }
            try {
                int scores = snake.getBody().size();
                switch (scores){
                    case 7:Thread.sleep(100);break;
                    case 10:Thread.sleep(50);break;
                    case 15:Thread.sleep(30);break;
                    case 25:Thread.sleep(20);break;
                    case 35:Thread.sleep(10);break;
                }
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 游戏构造函数
     * @param rows 行数
     * @param cols 列数
     * @param size 格子大小
     * @param length 初始长度
     */
    public Game(int rows, int cols,int size,int length) {
        grid = new Grid(rows,cols,size);
        snake = new Snake(length);
        food = new Food(rows-1,cols-1,snake.getBody());

        //初始化窗口
        int width = grid.getWidth();
        int height = grid.getHight();
        frame = new Frame("贪吃蛇");
        frame.setSize(width,height+100);
        frame.setLayout(new FlowLayout());
        frame.setVisible(true);//显示窗体




        //准备画布
        canvas = new Canvas();
        canvas.setSize(width,height);//指定尺寸
        frame.add(canvas);


        //监听按键
        canvas.addKeyListener(new KeyAdapter() {

            @Override
            public void keyPressed(KeyEvent e) {
                int keyCode = e.getKeyCode();//获取按键编码
                switch (keyCode){
                    case KeyEvent.VK_UP:snake.turn(Snake.UP);break;
                    case KeyEvent.VK_DOWN:snake.turn(Snake.DOWN);break;
                    case KeyEvent.VK_LEFT:snake.turn(Snake.LEFT);break;
                    case KeyEvent.VK_RIGHT:snake.turn(Snake.RIGHT);break;

                }
                super.keyPressed(e);
            }
        });

        //增加按钮
        button = new Button("Start Game");
        button.setSize(50,25);
        frame.add(button);

        //响应按钮
        button.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent mouseEvent) {
                graphics = canvas.getGraphics();
                canvas.requestFocus();//画布获取焦点

                drawGrid();//绘制界面
                drawSnake();//绘制蛇
                drawFood();//绘制食物
                start();//开启线程
            }
        });

        //增加按钮
        button = new Button("Exit Game");
        button.setSize(50,25);
        frame.add(button);

        //响应按钮
        button.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent mouseEvent) {
                graphics = canvas.getGraphics();
                frame.setVisible(false);//退出窗体
                System.exit(0);//退出程序
                super.mouseClicked(mouseEvent);
            }
        });
    }

    /**
     * 开始游戏
     */


    /**
     * 绘制游戏网格
     */
    public void drawGrid(){
        graphics.setColor(Color.gray);
        graphics.fillRect(0,0,grid.getWidth(),grid.getHight());
    }

    /**
     * 绘制蛇
     */
    public void drawSnake(){
        LinkedList<Node> nodes = snake.getBody();
        for(Node node:nodes){
            drawNode(node);
        }
    }

    private void drawNode(Node node){
        int size = grid.getSize();
        int x  = node.col*size;
        int y = node.row*size;
        graphics.setColor(node.color);

        graphics.fillRect(x,y,size,size);
    }

    /**
     * 绘制食物
     */
    public void drawFood(){
        drawNode(food.getNode());
    }
}

“把别人的嘲讽当成你起飞的跑道,传奇,总是从平凡开始的!我要飞,不管你信或者不信!”

在这里插入图片描述
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值