简体   繁体   中英

Java rendering a game map in a SwingWorker does not work properly

I am currently developing a game which requires a grid-like map being drawn on a JPanel (or other container). To achieve this in a reasonable amount of time and to keep my UI responsive i pass the container's graphics object to a map renderer which is a subclass of SwingWorker. Then i call the execute method of the map renderer which starts the actual drawing of the map. When the drawing is done a message is printed. All this is starts when i press the M key.

The actual problem is that drawing the map is successful only the first time. When i press the M key repeatedly i never get any other done messages. Also, when i ALT-TAB out of my application (which runs in fullscreen exclusive mode) and back in, my map is not visible anymore.

I suspect the problem somehow lies in not directly drawing in the EDT thread or not correctly passing on that my map renderer is ready.

This is my map renderer class:

    public class MapRenderer extends SwingWorker<Void, Void> {

private Graphics2D g;
private WorldMap map;
private int x, y, w, h, l;


@Override
protected Void doInBackground() throws Exception {
    drawMap();
    return null;
}

@Override
protected void done() {
    super.done();
    System.out.println("Done");
}

private void drawMap() {
    int rw = w + 1;
    int rh = h + 1;

    if (rw >= map.getSize())
        rw = map.getSize();
    if (rh >= map.getSize())
        rh = map.getSize();

    for (int i = 0; i < rw; i++) {
        for (int j = 0; j < rh; j++) {
            g.setColor(map.getTiles()[x + i][y + j].getColor());
            g.fillRect(i * l, j * l, l, l);
            if (l > 2) {
                g.setColor(Color.DARK_GRAY);
                g.drawRect(i * l, j * l, l, l); 
            }
        }
    }
}

public void setGraphics(Graphics2D g) {
    this.g = g;
}

public void setMap(WorldMap map) {
    this.map = map;
}

public void setViewPoint(Rectangle viewPoint, int squareSize) {
    this.x = (int) viewPoint.getX();
    this.y = (int) viewPoint.getY();
    this.w = (int) viewPoint.getWidth();
    this.h = (int) viewPoint.getHeight();
    this.l = squareSize;
}
    }

This is where the execute() method is called:

private void initializeKeyBindings() { 
    this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("M"), "M");
    this.getActionMap().put("M", new AbstractAction() {
        private static final long serialVersionUID = 1L;
        @Override
        public void actionPerformed(ActionEvent e) {
            drawMap(getGraphics());
        }
    });
}

public void drawMap(Graphics g) {
    mapRenderer.setGraphics((Graphics2D) g);
    mapRenderer.setViewPoint(new Rectangle(0, 0, 1600 / 4, 900 / 4), 4);
    mapRenderer.execute();
}

I apologize for any sloppy coding, i promise i will clean it up in the future.

One instance of a class implementing SwingWorker can only run once. So, you should create a new instance each time you execute the code. From the code you have supplied, it looks as if you are using the same instance. Thus it would work the first time, but not on consecutive calls:

public void drawMap(Graphics g) {
    mapRenderer.setGraphics((Graphics2D) g);
    mapRenderer.setViewPoint(new Rectangle(0, 0, 1600 / 4, 900 / 4), 4);
    mapRenderer.execute();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM