animation problem

Hey guys, I have already spent too many hours (actually nights…) in this problem and could not found a solution for it. I really need your help. It seems to be very simple but I could not solve it

When I have 5 sprites on screen the animation is very smooth but when I got 60 sprites for example the animation got too fast. See it:

http://luisoft.o-f.com/tmp/5aliens.jnlp

and

http://luisoft.o-f.com/tmp/60aliens.jnlp

I could not figured out what is happining

Here is the source code that I have made (I have put all in a single class to let you analize it better). The code is based on Brackeen’s book.

http://luisoft.o-f.com/tmp/Problem.java

or

http://luisoft.o-f.com/tmp/60aliens.jar

I really need your help. Thanks


package game;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.CropImageFilter;
import java.awt.image.FilteredImageSource;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Vector;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Problem extends Canvas {

    private BufferStrategy strategy;

    private ArrayList sprites = new ArrayList();

    private HashMap animations = new HashMap();

    public Problem() throws Exception {

        JFrame frame = new JFrame("Space Shooter");

        JPanel panel = (JPanel) frame.getContentPane();
        panel.setPreferredSize(new Dimension(800, 600));
        panel.setLayout(null);

        this.setBounds(0, 0, 800, 600);
        panel.add(this);

        this.setIgnoreRepaint(true);

        frame.pack();
        frame.setResizable(false);
        frame.setVisible(true);

        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        this.createBufferStrategy(2);
        this.strategy = this.getBufferStrategy();
        this.initSprites();
        this.gameLoop();
    }

    private void initSprites() throws Exception {
        URL url = this.getClass().getClassLoader().getResource("gfx/enemy.png");

        if (url == null) {
            System.out.println("error");
            System.exit(0);
        }
        BufferedImage image = ImageIO.read(url);

        Image[] tmp = this.cropImage(image, 32, 32);
        Image[] animationImage = new Image[tmp.length];

        GraphicsConfiguration gc = GraphicsEnvironment
                .getLocalGraphicsEnvironment().getDefaultScreenDevice()
                .getDefaultConfiguration();

        Animation animation = new Animation();
        for (int i = 0; i < tmp.length; i++) {
            animationImage[i] = gc.createCompatibleImage(32, 32,
                    Transparency.BITMASK);

            animationImage[i].getGraphics().drawImage(tmp[i], 0, 0, null);
            animation.addFrame(animationImage[i], 250);
        }
        animation.start();
        this.animations.put("gfx/enemy.png", animation);

        for (int j = 0; j < 5; j++) {
            for (int i = 0; i < 1; i++) {
                Sprite alien = new Sprite((Animation) this.animations
                        .get("gfx/enemy.png"), 100 + (i * 50), 50 + (j * 30));
                this.sprites.add(alien);
            }
        }

    }

    private void update(long elapsedTime) {
        for (int i = 0; i < this.sprites.size(); i++) {
            Sprite sprite = (Sprite) sprites.get(i);
            sprite.update(elapsedTime);
        }

    }

    private void draw(Graphics g) {
        g.setColor(Color.black);
        g.fillRect(0, 0, 800, 600);

        for (int i = 0; i < this.sprites.size(); i++) {
            Sprite sprite = (Sprite) sprites.get(i);
            sprite.draw(g);
        }

    }

    private void gameLoop() {
        long startTime = System.currentTimeMillis();
        long currTime = startTime;

        while (true) {

            long elapsedTime = System.currentTimeMillis() - currTime;
            currTime += elapsedTime;

            update(elapsedTime);

            Graphics g = this.strategy.getDrawGraphics();
            draw((Graphics2D) g);
            g.dispose();
            this.strategy.show();
            Toolkit.getDefaultToolkit().sync();
        }
    }

    class Sprite {
        protected Animation animation;

        protected double x;

        protected double y;

        public Sprite(Animation pAnimation, int pX, int pY) {
            this.animation = pAnimation;
            this.x = pX;
            this.y = pY;
        }

        public void update(long pDelta) {
            this.animation.update(pDelta);
        }

        public void draw(Graphics g) {
            this.animation.draw(g, (int) x, (int) y);
        }

    }

    class Animation {
        /** Array for holding the frames of the animation. */
        private ArrayList frames = new ArrayList();

        /** Indicates current frame. */
        private int currFrameIndex;

        /** Current animation time. */
        private long animTime;

        /** Total time of the animation. */
        private long totalDuration;

        private long lastFrameChange;

        /**
         * Adds an image to the animation with the specified duration (time to
         * display the image).
         * 
         * @param pImage
         *            Image of the frame.
         * @param pDuration
         *            Amount of time to display the frame.
         */
        public synchronized void addFrame(Image pImage, long pDuration) {
            this.totalDuration += pDuration;
            this.frames.add(new Frame(pImage, this.totalDuration, pDuration));
        }

        /**
         * Starts this animation over from the beginning.
         */
        public synchronized void start() {
            this.animTime = 0;
            this.currFrameIndex = 0;
        }

        /**
         * Updates this animation's current image (frame), if neccesary.
         * 
         * @param pElapsedTime
         *            Elapsed time since last update.
         */
        public synchronized void update(long pElapsedTime) {
            // We need only to update if the animation has more than 1 frame.
            if (this.frames.size() > 1) {
                this.animTime += pElapsedTime;

                if (this.animTime >= this.totalDuration) {
                    this.animTime = this.animTime % this.totalDuration;
                    this.currFrameIndex = 0;
                }

                while (this.animTime > this.getFrame(this.currFrameIndex).endTime) {
                    this.currFrameIndex++;
                }
            }
        }
        
        private Frame getFrame(int index) {
            return (Frame) frames.get(index);
        }

        public void draw(Graphics g, int pX, int pY) {
            g.drawImage(((Frame) frames.get(currFrameIndex)).image, pX, pY, null);
        }
    }

    /**
     * Represents a frame of an animation.
     */
    class Frame {
        private Image image;
        private long endTime;
        private long duration;

        public Frame(Image pImage, long pEndTime, long pDuration) {
            this.image = pImage;
            this.endTime = pEndTime;
            this.duration = pDuration;
        }

    }

    /**
     * Breaks an image in tiles (subimages).
     * 
     * @param pImage
     *            Image to be cropped.
     * @param pCropWidth
     *            Tile width.
     * @param pCropHeight
     *            Tile Height.
     * @return Array of Image.
     */
    private Image[] cropImage(Image pImage, int pCropWidth, int pCropHeight) {
        int i = 0;
        int j = 0;
        Vector imgs = new Vector();
        boolean finished = false;

        while (!finished) {
            if ((i + pCropWidth) > pImage.getWidth(null)) {
                i = 0;
                j += pCropHeight;
            }

            if ((j + pCropHeight) <= pImage.getHeight(null)) {
                Image image1;
                CropImageFilter cropimagefilter = new CropImageFilter(i, j,
                        pCropWidth, pCropHeight);
                FilteredImageSource filteredimagesource = new FilteredImageSource(
                        pImage.getSource(), cropimagefilter);
                image1 = Toolkit.getDefaultToolkit().createImage(
                        filteredimagesource);
                imgs.add(image1);
                i += pCropWidth;
            } else {
                pImage.flush();
                finished = true;
            }
        }

        Image[] tmp = new Image[imgs.size()];

        for (int e = 0; e < tmp.length; e++) {
            tmp[e] = (Image) imgs.get(e);
        }

        return tmp;
    }

    public static void main(String[] args) throws Exception {
        new Problem();
    }

}

Here is the part of the code to add more sprites:


for (int j = 0; j < 5; j++) {
  for (int i = 0; i < 1; i++) {
      Sprite alien = new Sprite((Animation) this.animations
    .get("gfx/enemy.png"), 100 + (i * 50), 50 + (j * 30));
      this.sprites.add(alien);
  }
   } 

For adding 60 for example you can do:


for (int j = 0; j < 5; j++) {
  for (int i = 0; i < 12; i++) {
      Sprite alien = new Sprite((Animation) this.animations
    .get("gfx/enemy.png"), 100 + (i * 50), 50 + (j * 30));
      this.sprites.add(alien);
  }
   } 

One last thing…sorry :-[ You may need to copy and paste the url link in your browser address bar to call the webstart file. Do no click directly using the mouse! ::slight_smile:

does anybody tried the source? :’(

ok, I think that I have solved my problem by myself! 8) The problem was that I was sharing the animations will all sprites. The solution was very simple, I have just created a new method in Animation class for cloning the object. I don’t know if it was the best solution for this kind of situation but it is working much better now.

tks anyway for the help :smiley: