SnakeGame如何让尾巴跟着头?

时间:2015-07-20 04:35:32

标签: 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);
    }
}

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

谢谢

2 个答案:

答案 0 :(得分:3)

基本的想法是,你需要某种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;
            }

        }

    }

}

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

答案 1 :(得分:1)

对于蛇式运动,您可以从尾部到头部将每个BodyPart位置移动到前面BodyPart的位置。对于头部而言,前面没有任何部分,因此您必须编写决策代码,无论是简单地移动与之前的部分相同的方向还是基于输入的新方向。然后更新屏幕。