3

I'm trying to make the game Snake, although I'm currently having trouble constantly repainting the snake. I'm using a timer, and when I set the interval to 150 milliseconds, it works, although when I set it to something faster like 30 milliseconds, nothing shows on the screen. The timer w/ repaint() call are in the Game class. Thanks!

Game Class

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Panel;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.*;
import java.util.*;
import java.util.Timer;
import java.util.TimerTask;

public class Game extends JFrame{

    JPanel mainPanel = new JPanel();

    BorderLayout layout = new BorderLayout();
    //Game game;
    Snake snake = new Snake(this);

    public Game(){

        super("Snake by Alex 2017");
        setSize((int)(Toolkit.getDefaultToolkit().getScreenSize().getWidth()),(int)(Toolkit.getDefaultToolkit().getScreenSize().getHeight()));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);

        mainPanel.setLayout(null);
        mainPanel.setSize((int)(Toolkit.getDefaultToolkit().getScreenSize().getWidth()),(int)(Toolkit.getDefaultToolkit().getScreenSize().getHeight()));
        mainPanel.setBackground(Color.BLACK);
        add(mainPanel);
        mainPanel.setVisible(true);

        start();

        addKeyListener(new KeyListener() {

            @Override
            public void keyTyped(KeyEvent e) {
            }

            @Override
            public void keyReleased(KeyEvent e) {
            }

            @Override
            public void keyPressed(KeyEvent e) {
                snake.keyPressed(e);

            }
        });
        setFocusable(true);



    }

    public void paint(Graphics g){
        super.paint(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        snake.paint(g2d);

        //System.out.println("hello");

    }

    Timer timer = new Timer();
    TimerTask task = new TimerTask() {
        public void run() {
            try {
                move();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    };
    TimerTask update = new TimerTask() {

        public void run() {
            repaint();

        }
    };
    public void start(){
        timer.scheduleAtFixedRate(task, 0, 250);
        timer.schedule(update, 0, 30);

    }

    public void move() throws InterruptedException{
        snake.move();

    }

    public static void main(String[] args) throws InterruptedException{
        Game game = new Game();


    }

}

Snake Class

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;

import javax.swing.RepaintManager;

public class Snake {

    public int x = 60;
    public int y = 80;
    public int xa = 0;
    public int ya = 0;
    public int speed = 20;
    private Game game;

    public Snake(Game game){
        this.game = game;
    }

    public void move(){
        x += xa;
        y += ya;
    }

    public void keyPressed(KeyEvent e){
        //RIGHT
        if(e.getKeyCode() == 39){
            xa = speed;
            ya = 0;
        }
        //LEFT
        if(e.getKeyCode() == 37){
            xa = -speed;
            ya = 0;
        }
        //UP
        if(e.getKeyCode() == 38){
            ya = -speed;
            xa = 0;
        }
        //DOWN
        if(e.getKeyCode() == 40){
            ya = speed;
            xa = 0;
        }
        //move();
        //game.repaint();

    }
    public Rectangle getBounds(){
        return new Rectangle(x, y, 20, 20);
    }
    public void paint(Graphics2D g2d){
        g2d.setColor(new Color(48, 255, 55));
        g2d.fillRect(x, y, 20, 20);

    }

}
Alex
  • 31
  • 4
  • 3
    You should use a Swing timer (`javax.swing.Timer`) instead of `java.util.Timer`. The Swing timer is designed to work with Swing programs and executes on the Swing thread. https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html – Radiodef Jun 06 '17 at 17:34
  • I tried swing timers and I still can't set the delay fast enough for a constant repaint – Alex Jun 06 '17 at 19:03
  • There's some other issue then. Swing can most certainly handle a pretty high repaint rate, even with passive rendering such as calling `repaint()` from a timer. In my experience, a simple 2D game like Snake can yield a frame rate as high as around 200 or 300FPS. (The limitation is how fast the Swing event queue can keep up with the constant event postings, and whether it will coalesce repetitive paint events.) – Radiodef Jun 06 '17 at 19:08
  • You should take a look through the code from [this recent answer of mine](https://stackoverflow.com/a/44371593/2891664) which shows a simple game with e.g. animation using a timer and key bindings for movement. – Radiodef Jun 06 '17 at 19:09

2 Answers2

0

What you have to do is to create a Thread that loops all the time and renders your game, like:

public void run() {
    while (true) {
        render();
    }
}

your run method is correct, but you also need to extends Thread to repaint in an extra thread over and over again see this post for multithreading.

For all the details concerning BufferStrategy and more follow this Tutorial. I think it is really good explained, and I used it my self.

pfaehlfd
  • 130
  • 1
  • 12
  • Nope, I've been trying for the last few days and I can't get it to work. For some reason I can't get it to paint when the timer is faster that 150 milliseconds or so. The while loop doesn't work either – Alex Jun 11 '17 at 01:25
0

Try to use javax.swing.Timer; as described in https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html Implement ActionListener and put there repaint(), then create Timer like this

Timer timer = new Timer(5, this);
        timer.start();

This will invoke actionPerformed() where you have repaint()

Pavlo Kovalov
  • 91
  • 1
  • 10