2

I have a third part JPanel that basically creates an ascii roguelike terminal for my game. Using this, I want to create a game, but I need to be able to draw on top of the panel with normal graphics. My problem is when I try to do this, the ascii looks fine, but the thing drawn on top flickers and sometimes doesn't show up.

This is my display code

public class Display {
    private static final char TRANSPARENT_CHARACTER = ' ';

    // Might break if the character is the same as TRANSPARENT_CHARACTER
    private static final AsciiCharacterData HIGHLIGHTER = new AsciiCharacterData(
            'H', new Color(255, 255, 0), new Color(255, 255, 0));

    private final AsciiPanel displayPanel;
    private final int widthInCharacters, heightInCharacters;

    private final static int Z_LEVELS = DrawingLayer.values().length;

    private final AsciiCharacterData[][][] characterMap;

    public Display(final AsciiPanel panel) {
        displayPanel = panel;
        widthInCharacters = panel.getWidthInCharacters();
        heightInCharacters = panel.getHeightInCharacters();

        characterMap = new AsciiCharacterData[widthInCharacters][heightInCharacters][Z_LEVELS];
        for (int x = 0; x < widthInCharacters; x++) {
            for (int y = 0; y < heightInCharacters; y++) {
                for (int z = 0; z < Z_LEVELS; z++) {
                    characterMap[x][y][z] = new AsciiCharacterData(
                            TRANSPARENT_CHARACTER,
                            displayPanel.getDefaultForegroundColor(),
                            displayPanel.getDefaultBackgroundColor());
                }
            }
        }
    }

    public void setCharacterAt(final int x, final int y, final DrawingLayer z,
            final AsciiCharacterData c) {
        characterMap[x][y][z.layer] = c;
        // if z is not the top level
        if (z.layer != Z_LEVELS - 1) {
            // check all levels above
            for (int i = z.layer + 1; i < Z_LEVELS; i++) {
                // if there is an opaque character
                if (characterMap[x][y][i].character != TRANSPARENT_CHARACTER)
                    // we dont need to draw anything
                    return;
            }
        }

        if (c.character == TRANSPARENT_CHARACTER) {
            // loop through all characters under the transparent character
            for (int i = z.layer - 1; i >= 0; i--) {
                // if we find a non transparent character
                if (characterMap[x][y][i].character != TRANSPARENT_CHARACTER) {
                    // display that one instead
                    displayPanel.write(characterMap[x][y][i].character, x, y,
                            characterMap[x][y][i].foregroundColor,
                            characterMap[x][y][i].backgroundColor);
                    return;
                }
            }
            // if there were no non trasparent characters
            displayPanel.write(TRANSPARENT_CHARACTER, x, y);
            // if we are a highlighter, we draw the below character and then
            // just draw on top
        } else {
            displayPanel.write(c.character, x, y, c.foregroundColor,
                    c.backgroundColor);
        }
        displayPanel.repaint();
    }

    public void highlightOnScreen(final int x, final int y) {
        final Graphics g = displayPanel.getGraphics();
        g.setColor(HIGHLIGHTER.backgroundColor);
        g.fillRect(x * AsciiPanel.getCharWidth(),
                y * AsciiPanel.getCharHeight(), AsciiPanel.getCharWidth(),
                AsciiPanel.getCharHeight());
    }

    public AsciiCharacterData getCharacterAt(final int x, final int y,
            final DrawingLayer z) {
        return characterMap[x][y][z.layer];
    }

    public int getWidth() {
        return widthInCharacters;
    }

    public int getHeight() {
        return heightInCharacters;
    }
}

This is the draw method in the GameMap class:

public void draw(final Display display) {
        for (int x = getViewportX(); x < getViewportX() + viewportWidthInTiles; x++) {
            for (int y = viewportY; y < viewportY + viewportHeightInTiles; y++) {
                final char character = background[x][y].getCharacter();
                final Color foreground = background[x][y].getForeground();
                final Color backgroundColor = background[x][y].getBackground();
                final AsciiCharacterData data = new AsciiCharacterData(
                        character, foreground, backgroundColor);
                display.setCharacterAt(x - getViewportX(), y - viewportY,
                        background[x][y].getDrawingLayer(), data);
            }
        }
        for (int i = 0; i < entities.size(); i++) {
            final Entity e = entities.get(i);

            final char character = e.getCharacter();
            final Color foreground = e.getForeground();
            final Color backgroundColor = e.getBackground();
            final AsciiCharacterData data = new AsciiCharacterData(character,
                    foreground, backgroundColor);
            display.setCharacterAt(e.getX() - getViewportX(), e.getY()
                    - viewportY, e.getDrawingLayer(), data);
        }
    }

and this is the draw method in the Game Class

@Override
public void draw() {
    final int x = map.mapTileXToDisplayTileX(0);
    final int y = map.mapTileYToDisplayTileY(0);

    display.highlightOnScreen(x, y);

    map.draw(display);
}

How can I make it so that it draws every frame and doesn't flicker?

Kyranstar
  • 1,650
  • 2
  • 14
  • 35
  • 1
    `AsciiPanel` us using a really good example of how not to do painting in Swing. Instead of overriding `update` or `paint`, it should be overriding `paintComponent` and calling `super.paintComponent`. Swing components are already double buffered, so all the manually double buffering is pointless. It also means if `AsciiPanel` is made transparent, you will end up with no end of paint artifacts... – MadProgrammer Sep 23 '14 at 01:39

1 Answers1

3

See How to Use Root Panes.

enter image description here

It is the glass pane which is of interest:

Hidden, by default. If you make the glass pane visible, then it's like a sheet of glass over all the other parts of the root pane. It's completely transparent unless you implement the glass pane's paintComponent method so that it does something, ..

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433