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.
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
You have set double buffering on the panel, but the things you are drawing on it yourself are not being double buffered. Calling setDoubleBuffered(true)
only applies buffering for child components of the container.
You need manually buffer stuff you draw yourself.
I would suggest moving away from a JPanel
and use a Canvas
instead. Have a look at how a Canvas
is used with a buffer strategy in this class, where it can handle hundreds of things being drawn on screen with perfect smoothness and no flickering (the game runs on a 10ms sleep loop too).
Not really sure if it solves your problem, but you can consider to use a 1dimensional array.
when int width;
is the width of your screen/tile/whatever you loop and int height
is the height you coud do the following:
instead of, what you do:
for(int x = 0; x < width; x++)
{
for(int y = 0; y < height; y++)
{
// dosomething with your array
myarray[x][y];
}
}
you could go through this way
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++)
{
// dosomething with your array
myarray[x + y * width];
}
}
来源:https://stackoverflow.com/questions/17965850/stop-flickering-in-swing-when-i-repaint-too-much