1
\$\begingroup\$

I'm developing a simple Snake game in Java using Swing. So far I made the snake animation which is troubling me: when the snakes moves it seems like it starts to lag for no reason, but when I move it around with the arrow keys it goes smooth.

How could I make it move smooth also when it goes on a straight line?

GameFrame

public class GameFrame extends JFrame {
    private GamePanel gamePanel;
    private JLabel scoreLabel;

    public GameFrame() throws HeadlessException {
        this.setTitle("Snake");
        this.setBounds(100, 100, 400, 400);
        this.setLocationRelativeTo(null);
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        this.setResizable(false);
        getContentPane().setLayout(null);
        getContentPane().setBackground(Color.BLACK);

        scoreLabel = new JLabel("0000");
        scoreLabel.setBounds(0, 0, 70, 20);
        scoreLabel.setForeground(Color.WHITE);
        getContentPane().add(scoreLabel);

        gamePanel = new GamePanel();
        gamePanel.setBounds(20, 20, 360, 350);
        this.add(gamePanel);

        this.setVisible(true);
    }
}

GamePanel

public class GamePanel extends JPanel implements KeyListener, ActionListener {
    private GameManager gameManager;
    private Timer timer;
    private final int DELAY = 150;

    GamePanel() {
        this.setOpaque(true);
        this.setBackground(new Color(51, 51, 51));
        this.addKeyListener(this);
        this.setFocusable(true);
        this.requestFocus();
        gameManager = new GameManager(this);
        timer = new Timer(DELAY, this);
        timer.start();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        gameManager.renderSnake(g);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        gameManager.gameLoop();
    }

    @Override
    public void keyPressed(KeyEvent e) {
        switch (e.getKeyCode()) {
            case KeyEvent.VK_UP:
                gameManager.moveSnakeUp();
                break;
            case KeyEvent.VK_DOWN:
                gameManager.moveSnakeDown();
                break;
            case KeyEvent.VK_RIGHT:
                gameManager.moveSnakeRight();
                break;
            case KeyEvent.VK_LEFT:
                gameManager.moveSnakeLeft();
                break;
        }
    }

    @Override
    public void keyTyped(KeyEvent e) { }

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

GameManager

public class GameManager {
    private Color snakeColor;
    private Snake snake;
    private GamePanel gamePanel;
    private boolean running;

    public GameManager(GamePanel gamePanel) {
        this.gamePanel = gamePanel;
        snakeColor = Color.GREEN;
        snake = new Snake();
        running = true;
    }

    public void gameLoop() {
        update();
        draw();
    }

    public void renderSnake(Graphics graphics) {
        Graphics2D graphics2D = (Graphics2D) graphics;
        graphics2D.setColor(snakeColor);
        for (Rectangle tile : snake.getBody())
            graphics2D.fillRect(tile.x, tile.y, tile.width, tile.height);
    }

    public void update() {
        snake.move();
    }

    public boolean isRunning() {
        return running;
    }

    public void draw() {
        gamePanel.repaint();
    }

    public void moveSnakeUp() {
        snake.setUpDirection();
    }

    public void moveSnakeDown() {
        snake.setDownDirection();
    }

    public void moveSnakeRight() {
        snake.setRightDirection();
    }

    public void moveSnakeLeft() {
        snake.setLeftDirection();
    }
}

Snake

public class Snake {
    private final int SNAKE_SPEED = 10;
    private final int BODY_WIDTH = 10;

    private LinkedList<Rectangle> body;
    private Directions direction;

    public Snake() {
        direction = Directions.RIGHT;
        body = new LinkedList<>();
        body.add(new Rectangle(50, 20, BODY_WIDTH, BODY_WIDTH));
        body.add(new Rectangle(40, 20, BODY_WIDTH, BODY_WIDTH));
        body.add(new Rectangle(30, 20, BODY_WIDTH, BODY_WIDTH));
        body.add(new Rectangle(20, 20, BODY_WIDTH, BODY_WIDTH));
        body.add(new Rectangle(10, 20, BODY_WIDTH, BODY_WIDTH));
    }

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

    public void setUpDirection() {
        if (direction != Directions.UP)
            direction = Directions.UP;
    }

    public void setDownDirection() {
        if (direction != Directions.DOWN)
            direction = Directions.DOWN;
    }

    public void setRightDirection() {
        if (direction != Directions.RIGHT)
            direction = Directions.RIGHT;
    }

    public void setLeftDirection() {
        if (direction != Directions.LEFT)
            direction = Directions.LEFT;
    }

    public void move() {
        Rectangle newHead = body.removeLast();
        newHead.x = body.peek().x;
        newHead.y = body.peek().y;

        switch (direction) {
            case UP:
                newHead.y -= SNAKE_SPEED;
                break;
            case DOWN:
                newHead.y += SNAKE_SPEED;
                break;
            case RIGHT:
                newHead.x += SNAKE_SPEED;
                break;
            case LEFT:
                newHead.x -= SNAKE_SPEED;
                break;
        }

        body.addFirst(newHead);
    }
}

I have tried using Thread, and different values for delay time but no avail. How could this be improved?

EDIT

I found a solution using BufferedImage which really makes a lot of difference, simple but effective:

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    BufferedImage bufferedImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics bufferedGraphics = bufferedImage.getGraphics();

    gameManager.renderSnake(bufferedGraphics);
    gameManager.renderFruit(bufferedGraphics);

    g.drawImage(bufferedImage, 0, 0, this);
}

I also managed to have a decent animation even without using Timer from java.Swing, but using Thread instead. It works well for me.

For those interested here is the code.

\$\endgroup\$
  • \$\begingroup\$ can you share your game loop with us (while(running){...}) ? \$\endgroup\$ – Martin Frank Feb 8 at 5:50

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Browse other questions tagged or ask your own question.