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
Using javax.swing.Timer
removes all flickering even with a 10 ms delay.
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.
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.
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.
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...
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With