java 贪吃蛇之我的实现

java 贪吃蛇之我的实现

一、实现原理

  1. 地图分成等份的点
  2. 蛇身由很多点组成
  3. 蛇的移动可以看成头部加了一个点,尾部少了一个点

二、实现细节

  1. 新建一张600*560的地图,X轴与Y轴各自以10像素为单位等分,分成60*56=3360个点,每个点均为10*10大小
  2. 用二维数组存放所有的点(或者用LinkedList)
  3. 细节化点类
  4. 细节化蛇类
  5. 添加蛇运动的线程,添加监听器,控制蛇的移动

三、细节优化

  1. 食物点可以直接画一个矩形,蛇应该怎样画。如果蛇身的点也画成矩形,会造成并排的蛇身之间没有空隙,美感度太差
  2. 监听器的几种实现方式,各自的优缺点以及bug解决

四、解决方案

  1. 给点Node加一个属性:direction,初始值为0。当蛇头吃掉某个点后,这个点的direction属性就变成蛇前进的方向(上下左右),当作为蛇尾移除出蛇身后,direction重归于0
  2. 蛇类也有自己的direction属性,代表蛇下一次移动的方向,需要注意的是,这个direction和蛇头点的direction不一定相同(当不同的时候就说明蛇转弯了嘛)
  3. 监听的方式大致分两种,一种是按下方向键后蛇立即移动一次,这样做的好处是永远不会出bug,而且还可以实现按住不放加速的效果,但是也会造成转弯速度过快的缺点。二是按下方向键只改变蛇的移动方向,蛇本身还是匀速运动。
  4. 蛇的画法,蛇身采用每个点的方向,以及它上一个点的方向,来确定画10*10区域的哪些地方,食物就直接填满10*10

五、感悟
永远不要小瞧任何一个project,贪吃蛇最简单的实现只需要100多行代码而已,但是在美观上,在逻辑上,实现的方法可以说海量,如何选取最高效,内存占比最低,最美观的实现方案,这是每一次编写程序的时候需要反复斟酌的。

六、上代码

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.LinkedList;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class RetroSnaker extends JFrame implements Runnable {
    private static final long serialVersionUID = 1L;
    private JLayeredPane rootpane;
    private JLabel label_restart;
    private JLabel label_score;
    private SnakerGame snaker;

    public static void main(String[] args) {
        new RetroSnaker();
    }

    public RetroSnaker() {
        init();
    }

    private void init() {
        setTitle("Retro Snaker");
        setSize(670, 680);
        setBackground(Color.BLACK);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);
        setLayout(null);

        rootpane = new JLayeredPane();
        rootpane.setBackground(Color.BLACK);

        snaker = new SnakerGame();
        snaker.setLocation(35, 60);
        snaker.setDoubleBuffered(true);
        rootpane.add(snaker);

        label_restart = new JLabel("Restart");
        label_restart.setBounds(400, 15, 150, 40);
        label_restart.setFont(new Font("Times New Roman", Font.PLAIN, 30));
        label_restart.setForeground(Color.LIGHT_GRAY);
        rootpane.add(label_restart);

        label_score = new JLabel("score: 0");
        label_score.setFont(new Font("Times New Roman", Font.PLAIN, 30));
        label_score.setForeground(Color.WHITE);
        label_score.setBounds(200, 15, 150, 40);
        rootpane.add(label_score);

        setLayeredPane(rootpane);
        setVisible(true);

        new Thread(this).start();

        addKeyListener(snaker);

        label_restart.addMouseListener(new MouseListener() {
            public void mouseReleased(MouseEvent e) {
            }

            public void mousePressed(MouseEvent e) {
            }

            public void mouseExited(MouseEvent e) {
                label_restart.setForeground(Color.LIGHT_GRAY);
                setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }

            public void mouseEntered(MouseEvent e) {
                label_restart.setForeground(Color.RED);
                setCursor(new Cursor(Cursor.HAND_CURSOR));
            }

            public void mouseClicked(MouseEvent e) {
                dispose();
                new RetroSnaker();
            }
        });

    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            label_score.setText("score: " + snaker.snake.size() * 10);
            snaker.repaint();
            int res = snaker.move();
            if (res == -1) {
                JOptionPane.showMessageDialog(RetroSnaker.this, "Game Over\n本次得分:" + label_score.getText());
                break;
            }
        }
    }
}

class Node {
    public int x, y, direction;
    public final int UP = 1, DOWN = 2, LEFT = 3, RIGHT = 4;

    public Node(int x, int y, int direction) {
        this.x = x;
        this.y = y;
        this.direction = direction;
    }

    public Node reDirection(){
        this.direction = 0;
        return this;
    }

    public void draw(Graphics g) {
        if (direction == LEFT || direction == RIGHT) {
            g.fillRect(x, y + 1, 10, 8);
        } else {
            g.fillRect(x + 1, y, 8, 10);
        }
    }

    public void drawFood(Graphics g) {
        g.setColor(Color.RED);
        g.fillRect(x, y, 10, 10);
    }

    public boolean isSame(Node node) {
        if (x == node.x && y == node.y)
            return true;
        return false;
    }
}

class SnakerGame extends JPanel implements KeyListener {
    private static final long serialVersionUID = 1L;
    private LinkedList<Node> nodes = new LinkedList<>();
    public LinkedList<Node> snake = new LinkedList<>();
    public static final int UP = 1, DOWN = 2, LEFT = 3, RIGHT = 4;
    public int direction = RIGHT;
    private Node food;
    private Random random = new Random();

    public SnakerGame() {
        setSize(601, 561);
        for (int i = 0; i < 600; i += 10) {
            for (int j = 60; j < 560; j += 10) {
                nodes.add(new Node(i, j, 0));
            }
        }
        food = nodes.remove(random.nextInt(nodes.size()));
        Node node = nodes.remove(random.nextInt(nodes.size()));
        node.direction = RIGHT;
        snake.addFirst(node);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        setOpaque(false);
        g.setColor(Color.WHITE);
        g.drawRect(0, 0, 600, 560);

        for (int i = 0; i < snake.size(); i++) {
            Node node = snake.get(i);
            if (i != 0) {
                Node n = snake.get(i - 1);
                if (node.direction == UP) {
                    if (n.direction == LEFT) {
                        g.fillRect(node.x, node.y + 1, 9, 9);
                        continue;
                    }
                    if (n.direction == RIGHT) {
                        g.fillRect(node.x + 1, node.y + 1, 9, 9);
                        continue;
                    }
                }
                if (node.direction == DOWN) {
                    if (n.direction == LEFT) {
                        g.fillRect(node.x, node.y, 9, 9);
                        continue;
                    }
                    if (n.direction == RIGHT) {
                        g.fillRect(node.x + 1, node.y, 9, 9);
                        continue;
                    }
                }
                if (node.direction == LEFT) {
                    if (n.direction == UP) {
                        g.fillRect(node.x + 1, node.y, 9, 9);
                        continue;
                    }
                    if (n.direction == DOWN) {
                        g.fillRect(node.x + 1, node.y + 1, 9, 9);
                        continue;
                    }
                }
                if (node.direction == RIGHT) {
                    if (n.direction == UP) {
                        g.fillRect(node.x, node.y, 9, 9);
                        continue;
                    }
                    if (n.direction == DOWN) {
                        g.fillRect(node.x, node.y + 1, 9, 9);
                        continue;
                    }
                }
            }
            node.draw(g);
        }

        food.drawFood(g);
    }

    public int move() {
        Node node = snake.getFirst();
        Node n = null;
        switch (direction) {
        case UP:
            n = new Node(node.x, node.y - 10, UP);
            if (n.x < 0 || n.x > 600 || n.y < 0 || n.y > 560)
                return -1;
            snake.addFirst(n);
            nodes.remove(new Node(node.x, node.y - 10, 0));
            if (!node.isSame(food))
                nodes.add(snake.removeLast().reDirection());
            else {
                food = nodes.remove(random.nextInt(nodes.size()));
                repaint();
            }
            break;
        case DOWN:
            n = new Node(node.x, node.y + 10, DOWN);
            if (n.x < 0 || n.x > 600 || n.y < 0 || n.y > 560)
                return -1;
            snake.addFirst(n);
            nodes.remove(new Node(node.x, node.y + 10, 0));
            if (!node.isSame(food))
                nodes.add(snake.removeLast().reDirection());
            else {
                food = nodes.remove(random.nextInt(nodes.size()));
                repaint();
            }
            break;
        case LEFT:
            n = new Node(node.x - 10, node.y, LEFT);
            if (n.x < 0 || n.x > 600 || n.y < 0 || n.y > 560)
                return -1;
            snake.addFirst(n);
            nodes.remove(new Node(node.x - 10, node.y, 0));
            if (!node.isSame(food))
                nodes.add(snake.removeLast().reDirection());
            else {
                food = nodes.remove(random.nextInt(nodes.size()));
                repaint();
            }
            break;
        case RIGHT:
            n = new Node(node.x + 10, node.y, RIGHT);
            if (n.x < 0 || n.x > 590 || n.y < 0 || n.y > 550)
                return -1;
            snake.addFirst(n);
            nodes.remove(new Node(node.x + 10, node.y, 0));
            if (!node.isSame(food))
                nodes.add(snake.removeLast().reDirection());
            else {
                food = nodes.remove(random.nextInt(nodes.size()));
                repaint();
            }
            break;
        }

        int i = 0;
        for (Node s : snake) {
            if (s.isSame(n))
                i++;
        }
        if (i >= 2)
            return -1;

        return 0;
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyPressed(KeyEvent e) {
        switch (e.getKeyCode()) {
        case KeyEvent.VK_UP:
            if (snake.getFirst().direction != DOWN)
                direction = UP;
            break;
        case KeyEvent.VK_DOWN:
            if (snake.getFirst().direction != UP)
                direction = DOWN;
            break;
        case KeyEvent.VK_LEFT:
            if (snake.getFirst().direction != RIGHT)
                direction = LEFT;
            break;
        case KeyEvent.VK_RIGHT:
            if (snake.getFirst().direction != LEFT)
                direction = RIGHT;
            break;
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }
}

Retro Snaker 一种实现方式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值