Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
179 views
in Technique[技术] by (71.8m points)

java - SnakeGame how to make the tail follow the head?

I am making a snake game, and I am stuck at where making the tails follow the head. And I heard using an add and remove on the head and tails could make that happen, but I have no idea where to start with that.

Here's my code so far:

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);
    }
}

Now this code would only make the tails flip to the horizontal or vertical, is it possible to make the tails follow the head by using this code? or I need to change my code?

Thank you

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The basic idea is, you need some kind of List which contains ALL the points of the snake. Conceptually, the List would contain virtual coordinates, that is 1x1 would represent a coordinate in virtual space, which presented a place on a virtual board (which would have some wide and height).

You could then translate that to the screen, so this would allow each part of the snake to be larger then a single pixel. So, if each part was 5x5 pixels, then 1x1 would actually be 5x5 in the screen.

Each time the snake moves, you add a new value to the head and remove the last value from tail (assuming it's not growing). When you needed to paint the snake, you would simply iterate over the List, painting each point of the snake.

The following is a simple example, which uses a LinkedList, which pushes a new Point onto the List, making a new head, and removing the last element (the tail) on each cycle.

Which basically boils down to...

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

As a runnable concept

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;
            }

        }

    }

}

Now, this has no collision detection or other functionality, but you can move the snake around and it will follow itself


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...