问题描述:

I have a small, simple program I've started and have a strange bug I haven't been able to figure out or find the cause for online.

All my program does right now is build a JFrame, build a JPanel, and draw an image onto the JPanel from a thread. The issue is, the image does not display unless I click in the Frame, left click, right click, or even click with my mouse wheel makes the image display, but resizing the frame doesn't make the image display. I've tried validate() on the panel and frames both, and I've tried putting repaint() in the code called by the thread but the image flickers when repaint() is in the code.

I put a print statement in the code that actually draws the image, but the statement never shows up until I click in the frame. I've walked through with the debugger and the code seems to be getting called correctly though. If I put a print statement anywhere in code being called from the main thread's update() function, the image displays correctly without having to click.

Here is my code, any suggestions would be appreciated:

The Frame

public class GameFrame extends JFrame{

int w = 1300;

int h = 740;

public GameFrame() {

this.setSize(w, h);

setTitle("Frame");

GamePanel panel = new GamePanel(w, h);

this.add(panel);

this.setVisible(true);

this.setDefaultCloseOperation(EXIT_ON_CLOSE);

}

public static void main(String[] args) {

GameFrame gameFrame = new GameFrame();

}

}

The Panel

public class GamePanel extends JPanel implements Runnable{

//core classes

GameRenderer renderer;

GameManager manager;

GameLoader loader;

//graphics stuff

private Graphics dbg;

private Image dbImage = null;

private int panelWidth;

private int panelHeight;

//game updating stuff

Thread animator;

boolean running;

public GamePanel(int w, int h) {

this.setSize(w, h);

this.setVisible(true);

this.setFocusable(true);

this.panelWidth = w;

this.panelHeight = h;

renderer = new GameRenderer();

manager = new GameManager();

loader = new GameLoader();

startGame();

}

private void startGame() {

animator = new Thread(this, "Animator");

animator.start();

running = true;

}

public void run() {

while(running) {

gameAction();

}

}

//everything that needs to be updated continuously

private void gameAction() {

//updateGame(); // Need code here to update everything

gameRender(); // Draw to the double buffer.

paintScreen(); // Draw double buffer to screen.

}

/**

* Draws the game image to the buffer.

*

*/

private void gameRender() {

if(dbImage == null) {

dbImage = createImage(this.panelWidth, this.panelHeight);

return;

}

dbg = dbImage.getGraphics();

renderer.draw((Graphics2D) dbg, panelWidth, panelHeight);

}

/**

* Draws the game image to the screen by drawing the buffer.

*/

private void paintScreen() {

Graphics g;

try {

g = this.getGraphics();

if ((g != null) && (dbImage != null)) {

g.drawImage(dbImage, 0, 0, null);

g.dispose();

}

} catch (Exception e) { System.out.println("Graphics context error: " + e); }

}

}

Image Renderer

public class GameRenderer {

ImageManipulator im = new ImageManipulator();

/**

* Actually draw everything that needs to be drawn

* Layering also happens here

*/

public void draw(Graphics2D g,int screenWidth, int screenHeight) {

BufferedImage tileImg = im.loadImage("Images/tile.png");

Tile tile = new Tile(tileImg, 30, 30);

tile.draw(g);

}

}

Class with image I'm trying to draw

public class Tile {

BufferedImage tileImg;

//x and y values based on tile grid - z based on height up

int tileX;

int tileY;

int tileZ;

//actual coordinate of top left corner of tile *image*

int pixelX;

int pixelY;

public Tile(BufferedImage img, int pixelX, int pixelY) {

this.pixelX = pixelX;

this.pixelY = pixelY;

this.tileImg = img;

}

public void draw(Graphics g) {

g.drawImage(tileImg, pixelX, pixelY, null);

}

}

Again, any suggestions or ideas would help. I'm out of ideas of what might be causing this.

Also, does anybody know why this might be working with a print statement included but not without one?

网友答案:

You have misunderstood how to do custom painting in a Swing component. It is not correct to call this.getGraphics(). The correct way to paint a Swing component is to override its paintComponent method. Note that the first line in that method must call super.paintComponent:

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    if (dbImage != null) {
        g.drawImage(dbImage, 0, 0, this);
    }
}

Also note that you must not attempt to dispose the Graphics object, since you didn't create it and it is under Swing's control, not yours.

For full details, see the Painting in AWT and Swing tutorial.

When you want your game to repaint, simply call the panel's repaint method. Thus, your gameAction method should replace paintScreen() with repaint(), which will automatically call your panel's paintComponent method (and some other methods; as that tutorial explains, painting isn't a simple thing).

You will need to put a short sleep call inside the loop in your run() method. If you try to update and repaint the game constantly, the program won't have time to actually draw anything. For instance, if you want the game to update five times per second (that is, every 200 milliseconds):

try {
    while (running) {
        gameAction();
        Thread.sleep(200);
    }
} catch (InterruptedException e) {
    e.printStackTrace();
}
网友答案:

I have two guesses. The first one is the startGame method in GamePanel. Put the line "running = true;" before you call "start()" on your thread.

If that doesn't work, try this in your run() method in GamePanel:

public void run() { gameAction(); }

If it renders, then the problem is the while loop.

相关阅读:
Top