Search code examples
javaswingappletswingworker

SwingerWorker for Character Animation in an Applet?


With my current code I have a character that loads in a sprite sheet for the animation. My problem arises when I run the code. The game runs fine except that the player is loading/cycling sprites way too fast. I believe this stems from the overall game timing/update. I should state that the game is working through an applet. I gathered some information on SwingerWorkers, but I don't know how to manipulate it to my needs. If someone could take a look at my code and let me know what I could do, I'd appreciate it.

Player constructor:

public Player(Image pic, ImageObserver IO){

    positionX=200;
    positionY=200;
    destinationX=(int)positionX;
    destinationY=(int)positionY;
    speed = 2.3;

    girlImage=pic;
    this.io=IO;
    width=120;
    height=100;
    //this.playerImage=playerImage;

    // Gets each sprite
    BufferedImage img = createImage();

    for(int j=0;j<4;j++)
    {
        for(int i = 0; i < frameCount; i++) {
            girlAll[j][i] = girl[j].getSubimage(imgWidth*i, 0, imgWidth, imgHeight);
        }//end of for   
    }//end of for
}

    // Image loader
private BufferedImage createImage(){
    //BufferedImage bufferedImage;
    try {
        girl[0] = ImageIO.read(new File("C:/Users/blutuu/Documents/Marlin/UD/Workspace S13/MoD/src/female_player_northeast_walk.png"));
        girl[1] = ImageIO.read(new File("C:/Users/blutuu/Documents/Marlin/UD/Workspace S13/MoD/src/female_player_northwest.png"));
        girl[2] = ImageIO.read(new File("C:/Users/blutuu/Documents/Marlin/UD/Workspace S13/MoD/src/female_player_southeast.png"));
        girl[3] = ImageIO.read(new File("C:/Users/blutuu/Documents/Marlin/UD/Workspace S13/MoD/src/female_player_southwest.png"));

    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

Draw method:

public void draw(Graphics g){
    //g.drawImage(girlImage, (int)positionX,(int) positionY,io);

    picNum = (picNum + 1) % frameCount;
    pics = girlAll[x];
    if (x==0)
        g.drawImage(pics[picNum], (int) (positionX), (int) (positionY), io);
    if (x==1)
        g.drawImage(pics[picNum], (int) (positionX), (int) (positionY), io);
    if (x==2)
        g.drawImage(pics[picNum], (int) (positionX), (int) (positionY), io);
    if (x==3)
        g.drawImage(pics[picNum], (int) (positionX), (int) (positionY), io);
}

SwingWorker attempt:

SwingWorker worker = new SwingWorker<BufferedImage[], Void>() {

    public BufferedImage[] doInBackground(){
        //BufferedImage bufferedImage;
        try {
            girl[0] = ImageIO.read(new File("C:/Users/blutuu/Documents/Marlin/UD/Workspace S13/MoD/src/female_player_northeast_walk.png"));
            girl[1] = ImageIO.read(new File("C:/Users/blutuu/Documents/Marlin/UD/Workspace S13/MoD/src/female_player_northwest.png"));
            girl[2] = ImageIO.read(new File("C:/Users/blutuu/Documents/Marlin/UD/Workspace S13/MoD/src/female_player_southeast.png"));
            girl[3] = ImageIO.read(new File("C:/Users/blutuu/Documents/Marlin/UD/Workspace S13/MoD/src/female_player_southwest.png"));

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void loadImg() {

        doInBackground();

        for(int j=0;j<4;j++)
        {
            for(int i = 0; i < frameCount; i++) {
                girlAll[j][i] = girl[j].getSubimage(imgWidth*i, 0, imgWidth, imgHeight);
            }//end of for   
        }//end of for

    }

};

}

Runs game (from 'Game' class):

public void init(){
    //Execute a job on the event-dispatching thread:
    //creating this applet's GUI.
    try {
        SwingUtilities.invokeAndWait(new Runnable() {
            public void run() {
                createGUI();
            }
        });
    } catch (Exception e) { 
        System.err.println("createGUI didn't successfully complete");
    }       

}

Thread handling:

private void gameStuff(){
    try {Thread.sleep(sleepTime);}
    catch (InterruptedException e) {}

    mouseClicked=false;
}

Game rendering:

public void update (Graphics g)
{
//      initialize buffer
    if (dbImage == null)
    {
        dbImage = createImage (this.getSize().width,    this.getSize().height);
        dbg = dbImage.getGraphics ();

    }

//      clear screen in background
    dbg.setColor (getBackground ());
    dbg.fillRect (0, 0, this.getSize().width, this.getSize().height);

//      draw elements in background
    //dbg.setColor (getForeground());
    paint (dbg);

//      draw image on the screen
    g.drawImage (dbImage, 0, 0, this);

} 

Another rendering method:

private void hud(Graphics g){
    g.setColor(Color.orange);
    g.fillRect(300, 0, 500, 80);
    g.fillRect(200, 0, 100, 80);

    //SHOVEL IMAGE AND BUTTON
    g.drawImage(shovelImage,      520,0,this);
    g.setColor(Color.black);
    g.drawRect(520, 0, 60, 80);//show boundaries
    if(mouseClicked && new Rectangle(520, 0, 60, 80).contains(mx,my))
        toolSelected=1; //sets tool selected to shovel

    //ROCKS IMAGE AND BUTTON
    g.drawImage(rocksImage,       600,0, this);
    g.drawRect(600, 0, 60, 80);//show boundaries
    if(mouseClicked && new Rectangle(600, 0, 60, 80).contains(mx,my))
        toolSelected=2; //sets tool selected to shovel

    //JACKHAMMER IMAGE AND BUTTON
    g.drawImage(jackHammerImage,  680,0, this);
    g.drawRect(680, 0, 60, 80);//show boundaries
    if(mouseClicked && new Rectangle(680, 0, 60, 80).contains(mx,my))
        toolSelected=3; //sets tool selected to shovel

    if(timer.getAngle()<360){
    timer.draw(g);
    }
    if(timer.getAngle()==360){
        raining=true;
        timer.reset();
        addDrops();
        roundNum++;
        puddles();

    }
    drawProgressBar(g);

    if(toolSelected == 1)
    {
        Message = "Native Delaware plants";
        Message2 = "are good";
    }
    else if(toolSelected == 2)
    {
        Message = "Rocks with Runoff";
        Message2 = " ";
    }
    else if(toolSelected == 3)
    {
        Message = "Permeated Driveways";
        Message2 = "help with excesse water";
    }
    else{
        Message = "Play On";
        Message2 = " ";
    }
    g.drawString("Round "+roundNum, 350, 30);
    g.drawString(Message, 205, 40);
    g.drawString(Message2, 205, 55);

    drawTiles(g);

}

Solution

    • Before using a SwingWorker, if all you want to do is a time-controlled simple game loop, use a Swing Timer instead.
    • If I were swapping sprite images, I'd consider swapping Icons in a JLabel as the easiest first thing I'd try.
    • Don't override update(Graphics g) for Swing GUI's. That was used for AWT, but for Swing is not appropriate. The graphics tutorials will tell you this.
    • Don't call paint(...) or paintComponent(...) directly. For more details, check out the basic Swing drawing tutorial and also a more advanced article on Painting in AWT and Swing.

    For example:

    import java.awt.BorderLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    import javax.imageio.ImageIO;
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class SpriteAnimationApplet extends JApplet {
       private static final String SPRITE_SHEET_SPEC = "http://www.funorb.com/img/images/game/"
             + "central/dev_diary/sprite_sheet_full.gif";
       private static final int SPRITE_ROWS = 8; // an 8 x 8 sprite sheet
    
       @Override
       public void init() {
          try {
             final Icon[] icons = SpriteIO.getSprites(SPRITE_SHEET_SPEC, SPRITE_ROWS);
             SwingUtilities.invokeAndWait(new Runnable() {
                public void run() {
                   SpriteAnimationPanel spritePanel = new SpriteAnimationPanel(icons);
                   getContentPane().add(spritePanel);
                   spritePanel.startAnimation();
                }
             });
          } catch (InvocationTargetException e) {
             e.printStackTrace();
             System.exit(-1);
          } catch (InterruptedException e) {
             e.printStackTrace();
             System.exit(-1);
          } catch (MalformedURLException e) {
             e.printStackTrace();
             System.exit(-1);
          } catch (IOException e) {
             e.printStackTrace();
             System.exit(-1);
          }
       }
    
    }
    
    class SpriteIO {
       public static Icon[] getSprites(String spriteSheetSpec, int spriteRows)
             throws MalformedURLException, IOException {
          Icon[] icons = new Icon[spriteRows * spriteRows];
          URL spriteSheetUrl = new URL(spriteSheetSpec);
          BufferedImage spriteSheet = ImageIO.read(spriteSheetUrl);
          double wD = (double) spriteSheet.getWidth() / spriteRows;
          double hD = (double) spriteSheet.getHeight() / spriteRows;
          int w = (int) wD;
          int h = (int) hD;
          for (int i = 0; i < spriteRows; i++) {
             for (int j = 0; j < spriteRows; j++) {
                int x = (int) (i * wD);
                int y = (int) (j * hD);
                BufferedImage img = spriteSheet.getSubimage(x, y, w, h);
    
                icons[j * spriteRows + i] = new ImageIcon(img);
             }
          }
          return icons;
       }
    }
    
    @SuppressWarnings("serial")
    class SpriteAnimationPanel extends JPanel {
       private static final int TIMER_DELAY = 200;
       private Icon[] icons;
       private JLabel animationLabel = new JLabel();
    
       public SpriteAnimationPanel(Icon[] icons) {
          this.icons = icons;
          setLayout(new BorderLayout());
          add(animationLabel );
       }
    
       public void startAnimation() {
          Timer spriteTimer = new Timer(TIMER_DELAY, new ActionListener() {
             private int iconIndex = 0;
    
             @Override
             public void actionPerformed(ActionEvent arg0) {
                animationLabel.setIcon(icons[iconIndex]);
                iconIndex++;
                iconIndex %= icons.length;
             }
          });
          spriteTimer.start();
       }
    }