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 agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.