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:
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.