简体   繁体   中英

What is happening with JPanel in java? What am I doing wrong?

I'm having a problem with my JPanel. I tell it to repaint() at the end of the constructor and then I have animation (one sprite moving on the screen), but the JPanel doesn't paint itself until the sprite has gone through its animation and reached its new point on the screen. I pasted my code below. I've never used a JPanel for custom graphics before. What am I doing wrong?

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;

class Battle extends JPanel implements KeyListener {

    AllyParty ap;
    boolean showMenu = false;
    BufferedImage image;
    EnemyParty ep;
    Graphics screen;
    Image allyPic[], enemyPic[];
    int enemyCount;
    int mtCount = 0;
    int turn[];
    MediaTracker mt;
    Random rand;

    public Battle(AllyParty allyparty) {

        /*Initial JPanel subclass setup*/
        setSize(800, 600);
        setBackground(Color.black);

        /*Create our ally party*/
        ap = new AllyParty();
        ap = allyparty;
        ap.setCallingObject(this);

        /*Create randomizer and random number variable*/
        long seed = System.currentTimeMillis();
        rand = new Random(seed);

        /*Use rand to select a number of enemies*/
        enemyCount = rand.nextInt(12) + 1;

        enemyCount = 12; //TEMP 
        ep = new EnemyParty(enemyCount);


        /*Create the individual enemies in ep and place them*/
        for (int i = 0; i < enemyCount; i++) {
            ep.enemy[i] = new Enemy(1);
        }

        ep.setCallingObject(this);

        /*Initialize images and set them to their starting values*/
        allyPic = new Image[4];
        enemyPic = new Image[enemyCount];

        /* for (int i = 0; i < 4; i++) 
        { 
        ap.ally[i].setCurrentImage(Ally.STAND) 
        allyPic[i] = ap.ally[i].getCurrentImage(); 
        } 

        for (int i = 0; i < enemyCount; i++) 
        { 
        enemyPic[i] = ep.enemy[i].getImage(); 
         */
        /*Set battle placement*/
        ap.setPos(0);
        ep.setPos(0);

        //Create the Buffered Image 
        image = new BufferedImage(800, 600, BufferedImage.TYPE_INT_RGB);

        //Set up the Media Tracker 
        mt = new MediaTracker(this);
        for (int i = 0; i < 4; i++) {
            mt.addImage(allyPic[i], mtCount++);
        }

        for (int i = 0; i < ep.getEnemyCount(); i++) {
            mt.addImage(enemyPic[i], mtCount++);
        }

        mt.addImage(image, mtCount++);

        try {
            mt.waitForAll();
        } catch (Exception e) {
        }

        /*Temporary Section*/
        findTurns();

        repaint();
        // ap.ally[0].advance(); 
        advance();
    }

    public void findTurns() {
        int total = ep.getEnemyCount() + 4; //Enemies + allies 

        int used[] = new int[total];
        turn = new int[total];

        int next = rand.nextInt(total);

        for (int j = 0; j < total; j++) {
            used[j] = -1;
        }

        for (int j = 0; j < total; j++) {
            for (int k = 0; k < j; k++) {
                if (used[k] == next) {
                    k = -1;
                    next = rand.nextInt(total);
                }
            }
            turn[j] = next;
            used[j] = next;
        }
    }

    public void advance() {
        while (ap.ally[0].getXPos() > 350) {
            ap.ally[0].moveLeft(20);
            ap.ally[0].setCurrentImage(Ally.WALK);
            repaint();
            try {
                Thread.sleep(200);
            } catch (Exception e) {
            }
            ap.ally[0].setCurrentImage(Ally.STAND);
            repaint();
            try {
                Thread.sleep(200);
            } catch (Exception e) {
            }
        }
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        screen = image.getGraphics();

        for (int i = 0; i < 4; i++) {
            allyPic[i] = ap.ally[i].getCurrentImage();
        }

        for (int i = 0; i < ep.getEnemyCount(); i++) {
            enemyPic[i] = ep.enemy[i].getCurrentImage();
        }

        for (int i = 0; i < 4; i++) {
            screen.drawImage(allyPic[i], ap.ally[i].getXPos(),
                ap.ally[i].getYPos(), this);
        }

        for (int i = 0; i < enemyCount; i++) {
            screen.drawImage(enemyPic[i], ep.enemy[i].getXPos(),
                ep.enemy[i].getYPos(), 100, 75, this);
        }

        g.drawImage(image, 0, 0, this);
    }

    public void keyPressed(KeyEvent k) {}
    public void keyReleased(KeyEvent k) {}
    public void keyTyped(KeyEvent k) {}
}
What am I doing wrong? 

1) use KeyBindings instead of KeyListener

2) never use Thread.sleep(int) during EDT, because causing freeze Swing GUI

3) for animations / delaying event use Swing Timer

4) you can use Icon in the JLabel for displaying BufferedImage in the JPanel

In all likelyhood you are modifying your "model" (the position of your sprite) in the AWT-Thread. Repaint are events dispatched in the AWT-EventThread. Therefore, you are blocking AWT of repainting your panel as long as your operations are run inside the AWT-Thread.

Two options:

1) Move your "operations/model" outside the AWT-Thread (in a different Thread)

2) Release the AWT-Thread from time to time, allowing repaint events to be performed (for me this is not a good option)

Make sure that if you share variables between two Threads you use volatile or synchronized (depending on how you work, one should better fit your needs)

As far as I can tell, your problem is that you are calling advance() in the constructor. Your code looks probably something like this:

JFrame frame = new JFrame();
frame.add(new Battle());
frame.setVisible(true);

Here is what happens:

  1. a new JFrame is created
  2. a new Battle is created
  3. at the end of the Battle constructor repaint() is called
  4. then advance() is called
  5. advance() itself calls the repaint multiple times
  6. advance() returns
  7. the constructor returns
  8. the Battle is added to frame
  9. the frame is made visible
  10. the Battle is painted

To solve your problem you have to remove the call to advance() in the constructor and call it after your frame is shown.

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