Search code examples
javaswingpaintcomponent

How Do I Paint My 2D Objects Within A Timer


I am trying to make the snake game using OOP. OOP is a new concept to me, as I typically use functional programming.

I have a timer and when it executes it should paint the snake and the food.

@Override
protected void paintComponent(Graphics g) {

    Timer timer = new Timer();

    TimerTask timerTask = new TimerTask() {

        @Override
        public void run() {

            //SNAKE AND FOOD DO NOT SHOW UP HERE

            g.setColor(Color.GREEN);
            drawSnake(g, snek);
            drawFood(g, foodPack);  
            snek.changeYDir(10);
            snek.moveHeadY();
            System.out.println("Timer is running");
            System.out.println(snek.getHeadY());
            System.out.println(snek.getChunkX(0));

        }
    };
    timer.scheduleAtFixedRate(timerTask, 0, 1000);



    //If code in timer is put here, snake and food show up


}

**A few clues I've deduced!

  1. The timer is running
  2. the head is moving - shown in the console
  3. The snake body exists - shown to exist in the console

CONSOLE LOG:

Timer is running
340
130

Here is a link to the whole project. https://files.fm/u/b8kvgebq


Solution

  • Start by having a read of Painting in AWT and Swing and Performing Custom Painting to gain a better understanding of how painting in Swing actually works.

    Next, have a read of Concurrency in Swing to understanding how threading works in Swing.

    paintComponent should only paint the current state of the component, it should never perform any operation which may trigger a new paint pass.

    Since paintComponent can be called at any time, for any number of reasons, many which you don't control, attempting to schedule a new TimerTask within it is a very, very, very bad idea. You could end up with any number of timers attempting to make changes to the UI, which will degrade performance and potentially conflict with each other.

    Because Graphics itself is also transient, you should never maintain a reference to it beyond the scope of the paint methods.

    First, take a look at How to use Swing Timers for better base solution. You should create a single Timer which updates the state of the component and calls repaint, which will, indirectly, call your paintComponent at which time you paint the current state of component.

    For example see SnakeGame how to make the tail follow the head?