Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop flickering in swing when i repaint too much

I am making an RPG with a tilemap. To generate the tilemap i loop through a 2 dimensional array but that means that when I repaint I have to do that each time. If I repaint too much the screen flickers how could I stop this.

package sexyCyborgFromAnOtherDimension;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JPanel;

@SuppressWarnings("serial")
public class Game extends JPanel
{
    KeyLis listener;
    int mapX = 20;
    int mapY = 20;
    boolean up = false;
    boolean down = false;
    boolean right = false;
    boolean left = false;
    String[][] map;

    public Game()
    {
        super();
        try 
        {
            map = load("/maps/map1.txt");
        } 

        catch (IOException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        listener = new KeyLis();
        this.setFocusable(true);
        this.requestFocus();
        this.addKeyListener(listener);

        Timer timer = new Timer();
        TimerTask task = new TimerTask() 
        {
            @Override
            public void run() 
            {
                if(up)
                {
                    mapY++;
                    repaint();
                }

                if(down)
                {
                    mapY--;
                    repaint();
                }

                if(right)
                {
                    mapX--;
                    repaint();
                }

                if(left)
                {
                    mapX++;
                    repaint();
                }
            }
        };
        timer.scheduleAtFixedRate(task, 0, 10);
    }

    public void paint(Graphics g) 
    {
        super.paintComponent(g);
        setDoubleBuffered(true);
        Graphics2D g2 = (Graphics2D)g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        for (int x = 0; x < map.length; x++) 
        {
            for (int y = 0; y < map[x].length; y++) 
            {
                switch(map[x][y])
                {
                case "0":
                    g.setColor(Color.GREEN);
                    break;
                case "1":
                    g.setColor(Color.GRAY);
                    break;
                }

                g.fillRect(y*20+mapX, x*20+mapY, 20, 20);
            }
        }
        g.setColor(Color.BLACK);
        g.fillRect(400, 400, 20, 20);
    }

    String[][] load(String file) throws IOException
    {
        BufferedReader br = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(file)));
        int lines = 1;
        int length = br.readLine().split(" ").length;
        while (br.readLine() != null) lines++;
        br.close();
        BufferedReader br1 = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(file)));
        String[][] map = new String[lines][length];
        for (int i = 0; i < lines; i++)
        {
            String line = br1.readLine();
            String[] parts = line.split(" ");
            for (int y = 0; y < length; y++)
            {
                map[i][y] = parts[y];
            }
        }
        br1.close();
        return map;
    }

    private class KeyLis extends KeyAdapter 
    {   
        @Override
        public void keyPressed(KeyEvent e) 
        {
            switch (e.getKeyCode())
            {
            case KeyEvent.VK_UP:
                up = true;
                break;
            case KeyEvent.VK_DOWN:
                down = true;
                break;
            case KeyEvent.VK_LEFT:
                left = true;
                break;
            case KeyEvent.VK_RIGHT:
                right = true;
                break;
            }
        }

        @Override
        public void keyReleased(KeyEvent e) 
        {
            switch (e.getKeyCode())
            {
            case KeyEvent.VK_UP:
                up = false;
                break;
            case KeyEvent.VK_DOWN:
                down = false;
                break;
            case KeyEvent.VK_LEFT:
                left = false;
                break;
            case KeyEvent.VK_RIGHT:
                right = false;
                break;
            }
        }
    }
}

Thank you for your help

Edit

Using javax.swing.Timer removes all flickering even with a 10 ms delay.

like image 726
sanchixx Avatar asked Jul 31 '13 08:07

sanchixx


People also ask

How do I stop flickering in Java?

Pre-installed code in JAVA can be used to eliminate this problem by taking advantage of "Buffering" and "Page Flipping". These functions permit you to delay the composition of a new screen until it is finished, so the user does not see flickering.

What is repaint swing?

repaint(): This method tells Swing that an area of the window is dirty. revalidate(): This method tells the layout manager to recalculate the layout that is necessary when adding components.

How do you use a swing timer?

Swing timers are very easy to use. When you create the timer, you specify an action listener to be notified when the timer "goes off". The actionPerformed method in this listener should contain the code for whatever task you need to be performed.


1 Answers

A number of small things jump out at me.

Firstly. You might be better using javax.swing.Timer instead of java.util.Timer, this will at least allow the events to flow a little better.

Secondly, 10 milliseconds is to short a time period, seriously, you don't need 100fps, 60fps is about 17 milliseconds, I normally use 40 milliseconds for 25fps. This might give the EDT some breathing room to actually respond to the repaint requests.

Thirdly, you should be using paintComponent instead of paint. It's low enough in the call chain to guaranteed to be double buffered

Fourthly, you should avoid calling any method that might reschedule a repaint (like setDoubleBuffered for example, to this in the constructor if you must, but, Swing components are double buffered by default)

Fifthly, where possible, paint all "static" or slow changing content to a backing buffer and paint that instead. This will increase the speed at which paint can work as it doesn't get stuck in a lot of small loops.

You may want to take a look at Painting in AWT and Swing for more details about the paint process

Some additional examples...

  • Swing animation running extremely slow - was able to go from 500 elements up to 4500 elements moving on the screen at one time - talks about resource management as well
  • Java Bouncing Ball
  • How to make line animation smoother?
  • the images are not loading

And because kleo scares me (and it's also a good idea), you should also take a look at How to use Key Bindings, which will solve your focus issues with the KeyListener

like image 193
MadProgrammer Avatar answered Oct 05 '22 10:10

MadProgrammer