While I was making a 2D Game in Java, a (atleast to me) very strange problem occured. I've provided a runnable, shortened example which reproduces my problem. When the x-coordinate of the red square is between 100 and 120 it should draw the string "Sample Text" above the square. However, you will see if you run the code, the window freezes completely for a few seconds. After the lag you can go over the area without problems, and the text will be shown. This problem only occurs when the program draws a String above the square. If I change the code so only another square appears above the red one, there's no lag. (I commented that in my code)
Any help would be appreciated.
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JApplet;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.KeyStroke;
public class MyExample extends JApplet {
int x = 10;
int y = 150;
public void init() {
setFocusable(true);
requestFocus();
Action right = new moveRight();
Action left = new moveLeft();
JRootPane rootPane = getRootPane();
rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "right");
rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "left");
rootPane.getActionMap().put("right", right);
rootPane.getActionMap().put("left", left);
getContentPane().add(new Paint());
}
protected class moveRight extends AbstractAction {
public void actionPerformed(ActionEvent e) {
x+=3;
repaint();
}
}
protected class moveLeft extends AbstractAction {
public void actionPerformed(ActionEvent e) {
x-=3;
repaint();
}
}
class Paint extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.RED);
g.fillRect(x,y,10,10);
g.setColor(Color.BLACK);
g.drawLine(100,100,100,200);
g.drawLine(129,100,129,200);
if(x>100&&x<120) {
g.setFont(new Font("TimesRoman", Font.PLAIN, 15));
g.setColor(Color.BLACK);
g.drawString("Sample Text",x-30,y-25);
//g.fillRect(x,y-15,10,10); - This work fine if you remove the g.setFont and the drawString
}
}
}
}
This has to do with the fact that you are trying to load the font within the paintComponent
method AND the underlying API trying to load the font and it's details before it can paint them.
I did think you could just pre-cache the font using something like...
class Paint extends JPanel {
private Font paintFont;
public Paint() {
paintFont = new Font("TimesRoman", Font.PLAIN, 15);
setFont(paintFont);
}
But in my testing, this still didn't work, what I actually ended up doing was adding a call to getFontMetrics
, which seems to force the API to load the font and it's properties into memory, making it render immediately, for example
class Paint extends JPanel {
private Font paintFont;
public Paint() {
paintFont = new Font("TimesRoman", Font.PLAIN, 15);
setFont(paintFont);
getFontMetrics(paintFont);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(x, y, 10, 10);
g.setColor(Color.BLACK);
g.drawLine(100, 100, 100, 200);
g.drawLine(129, 100, 129, 200);
if (x > 100 && x < 120) {
System.out.println("...");
//g.setFont(paintFont);
g.setColor(Color.BLACK);
g.drawString("Sample Text", x - 30, y - 25);
//g.fillRect(x,y-15,10,10); - This work fine if you remove the g.setFont and the drawString
}
}
}
Now, this will make your application load slightly slower, but will allow it to run faster as you've moved the loading of the font out of the paint cycle