Question

I am making an animation by drawing different images dozens of milliseconds after the former one. something like this:

 drawimage(image1,0,0,null);

 try {

   Thread.sleep(100);

 }catch(Exception e){//To do something

 }

 drawimage(image2,0,0,null);

But the first image doesn't show until the second one appear. That means they appear at the same time.

My question is why does this happen?

Was it helpful?

Solution

Me: Where exactly is this code? Is it in a paint/paintComponent method?

OP: it is in paintComponent. I use it to make animation, but I am not sure it is a good way.

You're right it isn't a good way. Don't ever call Thread.sleep in the paintComponent method. I would avoid the Thread.sleep all together and use a javax.swing.Timer. See more How to Use Swing Timers

See examples here and here and here and here.

You could...

Use a list of Images and every iteration firing of the Timer event, add another Image to the List<Image> and call repaint()

You could...

Have an MyImage object class that has an Image field and a boolean draw field. In the Timer, loop through the MyImage objects and do something like

    for (MyImage image: images) {
        if (!image.isDraw()) {
            image.setDraw(true);
            break;
        }
    }
    repaint();

For the MyImage List just loop through them in the paintComponent method and call it drawImage method, that you create.


Run this exmaple, showing the first option

enter image description here

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class AnimateImages extends JPanel {
    private static final int IMAGE_ROWS = 10;
    private static final int IMAGE_COLS = 10;
    private static final int IMAGE_SIZE = 50;
    private static final int DIM_WIDTH = IMAGE_COLS * IMAGE_SIZE;
            
    private final List<MyImage> images;
    private Image image;
    private int currX = -IMAGE_SIZE;
    private int currY;
    
    public AnimateImages() {
        try {
            image = ImageIO.read(new URL("http://swoo.co.uk/content/images/icons/stackoverflow.png"));
        } catch (IOException ex) {
            Logger.getLogger(AnimateImages.class.getName()).log(Level.SEVERE, null, ex);
        }
        images = createImages();
        
        Timer timer = new Timer(100, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                for (MyImage image : images) {
                    if (!image.isDraw()) {
                        image.setDraw(true);
                        break;
                    }
                    repaint();
                }
            }
        });
        timer.start();
    }
    
    private List<MyImage> createImages() {
        List<MyImage> list = new ArrayList<>();
        for (int i = 0; i < IMAGE_ROWS * IMAGE_COLS; i++) {
            if (currX >= DIM_WIDTH) {
                currX = 0;
                currY += IMAGE_SIZE;
            } else {
                currX += IMAGE_SIZE;
            }
            list.add(new MyImage(image, currX, currY));
            
        }
        return list;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        for (MyImage img : images) {
            img.draw(g);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(IMAGE_SIZE * IMAGE_COLS, IMAGE_SIZE * IMAGE_ROWS);        
    }

    public class MyImage {

        Image image;
        int x, y;
        boolean draw = false;

        public MyImage(Image image, int x, int y) {
            this.image = image;
            this.x = x;
            this.y = y;
        }

        public void setDraw(boolean draw) {
            this.draw = draw;
        }

        public boolean isDraw() {
            return draw;
        }

        public void draw(Graphics g) {
            if (draw) {
                g.drawImage(image, x, y, IMAGE_SIZE, IMAGE_SIZE, AnimateImages.this);
            }
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new AnimateImages());
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}

OTHER TIPS

The program, in its current state, draws two images at the same time because there are two draw methods in the loop (if you are indeed using a loop).

In order to solve this issue, you should use only a single draw method and leave the delay in the same place. However, in order to cycle through all of the different image variables (if you have them named as numbers 1,2,3,4...etc in an array) you can use a for loop to draw them:

for (int i = 0; i<  *however many images you have*; i++){
     drawimage(image[i],0,0,null);

     try {

       Thread.sleep(100);

     }catch(Exception e){//To do something

     }
}

Edit

You do not use delays inside the paintComponent. Since you are, that is what is likely causing the issues. Move the delay into the main method of your program.

Your answer

You're getting this result because it is printing image1, waiting .1 seconds and then printing image2. Then it doesn't wait at all, the game is immediately updated and then prints image1. Your display time for image2 is microscopic and you're likely only seeing image1 as a result.

Furthermore, the images are never deleted or replaced and so you are constantly drawing images on top of each other which is causing a memory leak, and is likely the cause of you seeing both images.

Considerations

This is a handy little animation class that I got, and still use, from this tutorial. This class is pretty handy and you can add the animated sprites necessary for each animation.

With this class, when you do a drawImage() you can use object.getImage() to extract the current frame of the animation.

Just make sure that you're calling the animation's update() method in your game's main loop so that the animations are constantly being updated.

import java.awt.Image;
import java.util.ArrayList;

public class Animation {

    private ArrayList frames;
    private int currentFrame;
    private long animTime;
    private long totalDuration;

    public Animation() {
        frames = new ArrayList();
        totalDuration = 0;

        synchronized (this) {
            animTime = 0;
            currentFrame = 0;
        }
    }

    public synchronized void addFrame(Image image, long duration) {
        totalDuration += duration;
        frames.add(new AnimFrame(image, totalDuration));
    }

    public synchronized void update(long elapsedTime) {
        if (frames.size() > 1) {
            animTime += elapsedTime;
            if (animTime >= totalDuration) {
                animTime = animTime % totalDuration;
                currentFrame = 0;
            }
            while (animTime > getFrame(currentFrame).endTime) {
                currentFrame++;
            }
        }
    }

    public synchronized Image getImage() {
        if (frames.size() == 0) {
            return null;
        } else {
            return getFrame(currentFrame).image;
        }
    }

    private AnimFrame getFrame(int i) {
        return (AnimFrame) frames.get(i);
    }

    private class AnimFrame {

        Image image;
        long endTime;

        public AnimFrame(Image image, long endTime) {
            this.image = image;
            this.endTime = endTime;
        }
    }

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top