java - 贪吃蛇游戏如何让尾部跟随头部?

标签 java swing

我正在制作一个贪吃蛇游戏,但我陷入了让尾部跟随头部的位置。我听说在头部和尾部使用添加和删除可以实现这一点,但我不知道从哪里开始。

这是我到目前为止的代码:

Screen.java

public class Screen extends JPanel implements ActionListener, KeyListener {
    public static final JLabel statusbar = new JLabel("Default");
    public static final int WIDTH = 800, HEIGHT = 800;
    Timer t = new Timer(100, this);
    int x = 400;
    int y = 400;
    int size = 5; //increase size if eat 
    private boolean right = false, left = false, up = false, down = false;

    int head = 0;

    private LinkedList<BodyPart> snake = new LinkedList<BodyPart>();
    private BodyPart b;

    public Screen(){

        initSnake();

        t.start();
        addKeyListener(this);
        setFocusable(true);
        setFocusTraversalKeysEnabled(false);
    }

    public void update(){

    }

    public void direction(){
        if(right) x+=10;
        if(left) x-=10;
        if(up) y-=10;
        if(down) y+=10;
    }

    public void trackOutBound(){
        if(x < 0 || x > 800 || y < 0 || y > 800) {
            x = 400;
            y = 400;
        }
    }

    public void initSnake(){
        if(snake.size() == 0){
            b = new BodyPart(x, y);
            for(int i = 0; i < size; i++) {
                snake.add(b);
            }
            System.out.println(snake);
        }
    }

    public static void main(String[] args) {

    }

    @Override
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        g.setColor(new Color(10, 50, 0));
        g.fillRect(0, 0, WIDTH, HEIGHT);

        g.setColor(Color.BLACK);
        for(int i = 0; i < WIDTH / 10; i++) {
            g.drawLine(i * 10, 0, i * 10, HEIGHT);
        }

        for(int i = 0; i < HEIGHT / 10; i++) {
            g.drawLine(0, i * 10, WIDTH, i * 10);
        }

        int tempx = 0, tempy = 0;
        int temp = 0;
        for(int i = 0; i < size; i++){
            if(i == head) {
                snake.get(i).x = x;
                snake.get(i).y = y;
                snake.get(i).draw(g);
                g.setColor(Color.blue);
                g.fillRect(x, y, 10, 10);
                g.setColor(Color.white);
                g.drawRect(x, y, 10, 10);
            } else if(i > 0 && up) {
                snake.get(i).x = x;
                snake.get(i).y = y + temp;
                snake.get(i).draw(g);
            } else if(i > 0 && down) {
                snake.get(i).x = x;
                snake.get(i).y = y - temp;
                snake.get(i).draw(g);
            } else if(i > 0 && left) {
                snake.get(i).x = x + temp;
                snake.get(i).y = y;
                snake.get(i).draw(g);
            } else if(i > 0 && right) {
                snake.get(i).x = x - temp;
                snake.get(i).y = y;
                snake.get(i).draw(g);
            }
            temp += 10;
        }

        /*
        if(snake.size() == 5){
            snake.add(b);
            size += 1;
        }
                */

    }

    @Override
    public void actionPerformed(ActionEvent e) {
        direction();
        trackOutBound();
        repaint();        
       // System.out.println(snake);
        statusbar.setText("(" + x + " , " + y + ")");
    }

    @Override
    public void keyTyped(KeyEvent e) {}

    @Override
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();
        if(key == KeyEvent.VK_RIGHT && !left) { 
            up = false;
            down = false;
            right = true;
        }
        if(key == KeyEvent.VK_LEFT && !right) { 
                up = false;
                down = false;
                left = true;
        }
        if(key == KeyEvent.VK_UP && !down) {
                left = false;
                right = false;
                up = true;
        }
        if(key == KeyEvent.VK_DOWN && !up) {
                left = false;
                right = false;
                down = true;
        } 
    }

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

BodyPart.java

public class BodyPart {

    int x;
    int y;

    public BodyPart(int x, int y) {
        this.x = x;
        this.y = y;
    }
    public void draw(Graphics g) {
        this.x = x;
        this.y = y;
    g.setColor(Color.red);
        g.fillRect(x, y, 10, 10);
        g.setColor(Color.white);
        g.drawRect(x, y, 10, 10);
    }
}

Frame.java

public class Frame extends JPanel {
    private static JLabel statusbar = new JLabel("Default");

    public void statusbar(){
        statusbar = Screen.statusbar;
    }

    public static void main(String[] args) {
        JFrame f = new JFrame();
        Screen s = new Screen();
        f.add(s);

        f.add(statusbar, BorderLayout.SOUTH);
        f.setSize(800, 800);
        f.setVisible(true);
        f.setLocationRelativeTo(null);
        f.setResizable(false);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

现在这段代码只会使尾部翻转到水平或垂直,是否可以使用这段代码使尾部跟随头部?或者我需要更改我的代码?

谢谢

最佳答案

基本思想是,您需要某种包含蛇的所有点的List。从概念上讲,List 将包含虚拟坐标,即 1x1 将表示虚拟空间中的坐标,该坐标表示虚拟板上的一个位置(该位置具有一定的宽度和高度) )。

然后您可以将其转换到屏幕上,这样蛇的每个部分都比单个像素大。因此,如果每个部分都是 5x5 像素,那么屏幕中的 1x1 实际上就是 5x5

每次蛇移动时,您都会向头部添加一个新值,并从尾部删除最后一个值(假设它没有增长)。当您需要绘制蛇时,您只需迭代List,绘制蛇的每个点即可。

下面是一个简单的例子,它使用一个LinkedList,它将一个新的Point推送到List上,创建一个新的头,并删除每个周期的最后一个元素(尾部)。

这基本上可以归结为......

snakeBody.removeLast();
snakeBody.push(new Point(xPos, yPos));

作为一个可运行的概念

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.LinkedList;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Snake {

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

    public Snake() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static class TestPane extends JPanel {

        public enum Direction {

            UP, DOWN, LEFT, RIGHT
        }

        private int xPos, yPos;

        private Direction direction = Direction.UP;

        private LinkedList<Point> snakeBody = new LinkedList<>();

        public TestPane() {
            xPos = 100;
            yPos = 100;

            for (int index = 0; index < 50; index++) {
                snakeBody.add(new Point(xPos, yPos));
            }

            bindKeyStrokeTo("up.pressed", KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), new MoveAction(Direction.UP));
            bindKeyStrokeTo("down.pressed", KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), new MoveAction(Direction.DOWN));

            bindKeyStrokeTo("left.pressed", KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), new MoveAction(Direction.LEFT));
            bindKeyStrokeTo("right.pressed", KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), new MoveAction(Direction.RIGHT));

            Timer timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    switch (direction) {
                        case UP:
                            yPos--;
                            break;
                        case DOWN:
                            yPos++;
                            break;
                        case LEFT:
                            xPos--;
                            break;
                        case RIGHT:
                            xPos++;
                            break;
                    }
                    if (yPos < 0) {
                        yPos--;
                    } else if (yPos > getHeight() - 1) {
                        yPos = getHeight() - 1;
                    }
                    if (xPos < 0) {
                        xPos--;
                    } else if (xPos > getWidth() - 1) {
                        xPos = getWidth() - 1;
                    }

                    snakeBody.removeLast();
                    snakeBody.push(new Point(xPos, yPos));
                    repaint();
                }
            });
            timer.start();
        }

        public void bindKeyStrokeTo(String name, KeyStroke keyStroke, Action action) {
            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = getActionMap();

            im.put(keyStroke, name);
            am.put(name, action);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(Color.RED);
            for (Point p : snakeBody) {
                g2d.drawLine(p.x, p.y, p.x, p.y);
            }
            g2d.dispose();
        }

        public class MoveAction extends AbstractAction {

            private Direction moveIn;

            public MoveAction(Direction direction) {
                this.moveIn = direction;
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                direction = this.moveIn;
            }

        }

    }

}

现在,它没有碰撞检测或其他功能,但你可以移动蛇,它会跟随自己

关于java - 贪吃蛇游戏如何让尾部跟随头部?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31508913/

相关文章:

java - JOptionPane 的多项选择

java - 在子类中调用父类方法时的stackoverflow

java - 如何将 Java 编译器错误捕获到文件中?

java - 直接从生成的声音阵列在 Kotlin/Java 中播放声音

java - 使用命令行执行类时出现异常

java - 在 Java Swing 中如何判断两个 JList 中哪个 JList 具有光标焦点?

java - 如何访问本地 .properties 变量? Java Swing 变量的正确使用

java - 如何使用 Java 将 PDF 中表格的特定单元格内的文本设为粗体

具有观察者模式的 Java MVC 不同步不同 View

java - 无法传递在 "public void actionPerformed(ActionEvent event)"中声明的变量