Search code examples
javamacosswingeditlag

Java AWT Initial drawString() Call Takes Long


The issue I am having is very simple-I am using Java AWT (and Swing) to make a game, but the initial Graphics.drawString(String str, int x, int y) call takes way too long. I am not setting a custom font or anything. I am not setting any rendering hints. I am using Mac Sierra 10.12.4 in a MacBook Air-I didn't get to test my program in Windows yet. If that matters at all.

In case you don't want to spend time reading the minimal example, I have a class called GamePanel that extends JPanel (and implements runnable) that I add to a JFrame in the main method. I initialize the game thread in the constructor and start the game loop in the run() method. The game loop then repeats itself using Swing Timers. In the game loop, I call paintComponent(Graphics g) through redraw()-the text is drawn in the paintComponent method. This isn't really necessary information since the problem is reproducible without a game loop, just with the game thread and the paintComponent method, but I put this here just incase.

Any help is appreciated.

Game class (JFrame)

public class Game {
    public static JFrame frame;
    public static JPanel gamePanel;

    public static void main(String[] args) {
        frame = new JFrame("");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setLayout(new BorderLayout());
        gamePanel = new GamePanel();
        frame.add(gamePanel, BorderLayout.CENTER);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

GamePanel class (Panel)

public class GamePanel() extends JPanel implements Runnable {
    public static Thread thread;

    public GamePanel() {
        super();

        setPreferredSize(new Dimension(640, 480));

        setFocusable(true);
        requestFocus();

        if (thread == null) {
            thread = new Thread(this);
            thread.start();
        }
    }

    public void run() {
        while (! Game.frame.isVisible()) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        EventQueue.invokeLater(new Runnable() {
            public void run() {gameLoop();}
        });
    }

    public void GameLoop() {
        repaint();

        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        gameLoop();
    }

    public void paintComponent(Graphics g) {
        super.paintComponent();
        g.drawText("Hello World", 10, 10);
    }
}

I know the game loop in the minimal example's speed would be ever-changing (and thats exactly the opposite of what a good game loop should do). But what does that matter in a minimal example?


Solution

  • I had the same problem. My solution is to save the fonts I use in a resource folder and then load the font. In Eclips you create a folder most easily through "New / Source Folder". The name of the folder does not matter if you follow my example. My font that I have saved in the resource folder is called "DroidSans-Bold.ttf"

        Font font = null;
    
        try {
           font = Font.createFont(Font.TRUETYPE_FONT, new File(getClass().getResource("/DroidSans-Bold.ttf").getFile()));
           font = font.deriveFont(32f); // Set size to 32
        } catch (FontFormatException | IOException e) {
           e.printStackTrace();
        }
    
        JFrame jf = new JFrame();
        jf.setSize(800, 600);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setVisible(true);
    
        Graphics2D g = (Graphics2D)jf.getGraphics();
    
        g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g.setFont(font);
        g.drawString("Hello", 100, 100);