Sprite animation dosen't work properly.

I’ve made a sprite animation, and it won’t animate to the next frame. Any ideas?

Here is the source code.
http://www.mediafire.com/?96bz06tdtyb15qo

Could you show us some code here on JGO? (nobody is gonna download a zip folder to be honest :))

sure, here is the code.

Animation


import java.awt.*;
import java.util.ArrayList;

public class Animation {

    public ArrayList animArray;
    public int f;
    public long animationTime;

    public Animation() {
        f = 0;
        animationTime = 0;
        animArray = new ArrayList();
    }

    public void addFrame(Image i, int t) {
        animArray.add(new Frame(i, t));
    }

    public void update(long time) {
        if (animArray.size() > 1) {
            if (animationTime < getFrame(f).time) {
                animationTime = time;
            } else if (animationTime > getFrame(f).time) {
                animationTime = 0;
                f++;
            }
        }
    }

    public Image getImage() {
        if (animArray.isEmpty()) {
            return null;
        } else {
            return getFrame(f).img;
        }
    }

    public Frame getFrame(int x) {
        return (Frame) animArray.get(x);
    }

    public class Frame {

        public Image img;
        public int time;

        public Frame(Image i, int t) {
            img = i;
            time = t;
        }
    }
}

AnimationTest



import javax.swing.JFrame;

public class AnimationTest {

    public AnimationTest() {
        JFrame f = new JFrame();
        f.setTitle("AnimationTest10");
        f.add(new MainConfig());
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(500, 500);
        f.setVisible(true);

    }

    public static void main(String[] args) {
        new AnimationTest();
    }
}


MainConfig


import java.awt.Graphics;
import javax.swing.JPanel;

public class MainConfig extends JPanel implements Runnable {

    public SmileyFace smiley = new SmileyFace();
    public Thread mainLoop;

    public void MainConfig() {
        this.setDoubleBuffered(true);
        mainLoop = new Thread(this);
        mainLoop.start();
    }

    @Override
    public void paint(Graphics g) {
        g.drawImage(smiley.getFrame(), 250, 250, null);
    }

    @Override
    public void run() {
        long currentTime;
        long gameTime;
        long totalTime;
        while (true) {
            currentTime = System.currentTimeMillis();
            gameTime = System.currentTimeMillis() - currentTime;
            totalTime = +gameTime;
            smiley.allAnimations();
            smiley.currentAnimation.update(totalTime);
        }
    }
}


SmileyFace


import java.awt.Image;
import javax.swing.ImageIcon;

public class SmileyFace{

    public Animation smileyAnim = new Animation();
    public Sprite currentAnimation;
    
    public SmileyFace(){
        currentAnimation = new Sprite(smileyAnimation());
    }
    private Animation smileyAnimation() {
        Image f1 = new ImageIcon(this.getClass().getResource("/animationtest/Face1.png")).getImage();
        Image f2 = new ImageIcon(this.getClass().getResource("/animationtest/Face2.png")).getImage();
        smileyAnim.addFrame(f1, 110);
        smileyAnim.addFrame(f2, 110);      
        return smileyAnim;
    }

    public void allAnimations(){
        smileyAnimation();   
    }
    
    public Sprite sprite() {
        return currentAnimation;
    }

    public Image getFrame() {
        return currentAnimation.getImage();
    }
}


Sprite


import java.awt.Image;

public class Sprite {

    public int x, y;
    public Animation a;

    public Sprite(Animation anim) {
        a = anim;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int getWidth() {
        return a.getImage().getWidth(null);
    }

    public int getHieght() {
        return a.getImage().getHeight(null);
    }
    public Image getImage(){
        return a.getImage();
    }
    public void update(long time){
        a.update(time);
    }
}


Hehe you forgot to call repaint() in MainConfig after line 30 ;D

yeah apart from that and calling


Animation smileyAnimation() { 
        Image f1 = new ImageIcon(this.getClass().getResource("/animationtest/Face1.png")).getImage(); 
        Image f2 = new ImageIcon(this.getClass().getResource("/animationtest/Face2.png")).getImage(); 
        smileyAnim.addFrame(f1, 110); 
        smileyAnim.addFrame(f2, 110);       
        return smileyAnim; 
    } 

every iteration, there is a lot else wrong… too much to list ^^

but I’m sure you naturally figure much of it out, as you work on it more

o woops, I did forget to put repaint() thanks.

But Cero, what do u mean by
“every iteration, there is a lot else wrong… too much to list ^^”

do you mean I have to many conditions that go to the else.

Or do you mean I have many things wrong in my code?

No he meant that you are calling smileyAnimation() every interation and that there are a lot of other problems with your code.
The problem with smileyAnimation() is that it is reading both images every time this method is called. This will cause your loop to slow down and it will hog a LOT of memory.

at least try to cache your image :slight_smile: try to optimize bit is not ebil.

I’ve gotten some progress,and I’ve change the source code a little bit. But the new problem, is that if I were to change the value in Smiley animation to


    private void smileyAnimation() {
        Image f1 = new ImageIcon(this.getClass().getResource("/animationtest/Face1.png")).getImage();
        Image f2 = new ImageIcon(this.getClass().getResource("/animationtest/Face2.png")).getImage();
        smileyAnim.addFrame(f1, 10);
        smileyAnim.addFrame(f2, 10);      
    }


The animation starts on the second frame and stays their. But If I changed it to


    private void smileyAnimation() {
        Image f1 = new ImageIcon(this.getClass().getResource("/animationtest/Face1.png")).getImage();
        Image f2 = new ImageIcon(this.getClass().getResource("/animationtest/Face2.png")).getImage();
        smileyAnim.addFrame(f1, 30);
        smileyAnim.addFrame(f2, 30);      
    }


It stays on the first frame of the animation. Any thoughts?

Here are the other changes I’ve made in my source code.

Smiley Animation


import java.awt.Image;
import javax.swing.ImageIcon;

public class SmileyFace{

    public Animation smileyAnim = new Animation();
    public Sprite currentAnimation;
    
    public SmileyFace(){
        smileyAnimation();
        currentAnimation = new Sprite(smileyAnim);
    }
    private void smileyAnimation() {
        Image f1 = new ImageIcon(this.getClass().getResource("/animationtest/Face1.png")).getImage();
        Image f2 = new ImageIcon(this.getClass().getResource("/animationtest/Face2.png")).getImage();
        smileyAnim.addFrame(f1, 30);
        smileyAnim.addFrame(f2, 30);      
    }

    public void allAnimations(){
        smileyAnimation();   
    }
    
    public Sprite sprite() {
        return currentAnimation;
    }

    public Image getFrame() {
        return currentAnimation.getImage();
    }
}

MainConfig


import java.awt.Graphics;
import javax.swing.JPanel;

public class MainConfig extends JPanel implements Runnable {

    public SmileyFace smiley = new SmileyFace();
    public Thread mainLoop;

    public void MainConfig() {
        setDoubleBuffered(true);
        System.out.println("hi");
    }

    public void addNotify() {
        mainLoop = new Thread(this);
        mainLoop.start();
    }

    @Override
    public void run() {
        long startTime = System.currentTimeMillis();
        long totalTime = System.currentTimeMillis() - startTime;
        while (true) {
            long timePassed = System.currentTimeMillis() - totalTime;
            timePassed += totalTime;
            smiley.allAnimations();
            smiley.currentAnimation.update(timePassed);
            repaint();
            try {
                Thread.sleep(10);
            } catch (Exception e) {
            }
        }
    }

    public void paint(Graphics g) {
        g.drawImage(smiley.getFrame(), 250, 250, null);
    }
}

Animation


import java.awt.*;
import java.util.ArrayList;

public class Animation {

    public ArrayList animArray;
    public int f;
    public long animationTime;

    public Animation() {
        f = 0;
        animationTime = 0;
        animArray = new ArrayList();
    }

    public void addFrame(Image i, int t) {
        animArray.add(new Frame(i, t));
    }

    public void update(long time) {
        if (animArray.size() > 1) {
            animationTime = time;
            if (animationTime > getFrame(f).time) {
                f++;
                animationTime = 0;
            }else if(f > animArray.size()){
                f = 0;
            }
        }
        //System.out.println(time);
    }

    public Image getImage() {
        if (animArray.isEmpty()) {
            return null;
        } else {
            return getFrame(f).img;
        }
    }

    public Frame getFrame(int x) {
        return (Frame) animArray.get(x);
    }

    public class Frame {

        public Image img;
        public int time;

        public Frame(Image i, int t) {
            img = i;
            time = t;
        }
    }
}

Don’t forget to call “super.addNotify()” in MainConfig.
Also, you are supposed to add the images once, not every frame, so move the call to smileyAnimation() to the constructor.
Your animation class is completely wrong. You want each Frame to hold the time at which it will end, not the amount of time it is shown.
Take a look at my Animation class.

What is also wierd is:

Sprite has an animation as a field
SmileyFace has a sprite and an animation

So SmileyFace has 2 animations D=

Whoa…yeah you’re right!
@OP
smiley.currentAnimation.update(timePassed) is calling the empty Animation inside Sprite, not the one that you have added the 2 images to :stuck_out_tongue:

I’m making more progress now, but it still wouldn’t animate. Any ideas?

Here is the new edited source codes.

Animation


import java.awt.*;
import java.util.ArrayList;

public class Animation {

    public ArrayList<Frame> frames;
    public int f = 0;
    public long currentTime;
    public long totalTime;

    public Animation() {
        frames = new ArrayList();
        totalTime = 0;
        start();
    }

    public synchronized void addFrame(Image i, long t) {
        totalTime += t;
        frames.add(new Frame(i, totalTime));
    }

    public synchronized void start() {
        currentTime = 0;
        f = 0;
    }

    public synchronized void update(long time) {
        if (frames.size() > 1) {
            currentTime += time;
            if (currentTime >= totalTime) {
                currentTime = 0;
                f = 0;
            }
            while (currentTime > frames.get(f).time) {
                f++;
            }
        }
        //System.out.println(time);
    }

    public synchronized Image getImage() {
        if (frames.isEmpty()) {
            return null;
        } else {
            return frames.get(f).img;
        }
    }

    public class Frame {

        public Image img;
        public long time;

        public Frame(Image i, long t) {
            img = i;
            time = t;
        }
    }
}

MainConfig


import java.awt.Graphics;
import javax.swing.JPanel;

public class MainConfig extends JPanel implements Runnable {

    public SmileyFace smiley = new SmileyFace();
    public Thread mainLoop;

    public void MainConfig() {
        setDoubleBuffered(true);
    }

    @Override
    public void addNotify() {
        super.addNotify();
        mainLoop = new Thread(this);
        mainLoop.start();
    }

    @Override
    public void run() {
        long startTime = System.currentTimeMillis();
        long totalTime = System.currentTimeMillis() - startTime;
        while (true) {
            long timePassed = System.currentTimeMillis() - totalTime;
            timePassed += totalTime;
            smiley.currentSprite.update(timePassed);
            repaint();
            try {
                Thread.sleep(10);
            } catch (Exception e) {
            }
        }
    }

    @Override
    public void paint(Graphics g) {
        g.drawImage(smiley.getFrame(), 250, 250, null);
        g.dispose();
    }
}

I just realized I’ve made a pretty careless mistake.


    public void MainConfig() {
        setDoubleBuffered(true);
    }

should be


    public MainConfig() {
        setDoubleBuffered(true);
    }

in MainConfig.

No need for that, JPanel is automatically double buffered :wink:

Thanks for your reply.

Here is the new MainConfig


import java.awt.Graphics;
import javax.swing.JPanel;

public class MainConfig extends JPanel implements Runnable {

    public SmileyFace smiley;
    public Thread mainLoop;

    public MainConfig() {
        smiley = new SmileyFace();
    }

    @Override
    public void addNotify() {
        super.addNotify();
        mainLoop = new Thread(this);
        mainLoop.start();
    }

    @Override
    public void run() {
        long startTime = System.currentTimeMillis();
        long totalTime = System.currentTimeMillis() - startTime;
        while (true) {
            long timePassed = System.currentTimeMillis() - totalTime;
            timePassed += totalTime;
            smiley.sprite().update(timePassed);
            repaint();
            try {
                Thread.sleep(10);
            } catch (Exception e) {
            }
        }

    }

    @Override
    public void paint(Graphics g) {
        g.drawImage(smiley.getFrame(), 250, 250, null);
    }
}

If you don’t mind, could you tell me why my animation isn’t working or why it isn’t animating.

Your methods seem to be different from the last SmileyFace class you posted. Could you post it again?


import java.awt.Image;
import javax.swing.ImageIcon;

public class SmileyFace{

    public Animation smileyAnim;
    public Sprite currentSprite;

    public SmileyFace() {
        smileyAnimation();
    }

    public void smileyAnimation() {
        smileyAnim = new Animation();
        Image f1 = new ImageIcon(this.getClass().getResource("/animationtest/Face1.png")).getImage();
        Image f2 = new ImageIcon(this.getClass().getResource("/animationtest/Face2.png")).getImage();
        smileyAnim.addFrame(f1, 30);
        smileyAnim.addFrame(f2, 30);    
        currentSprite = new Sprite(smileyAnim);
    }

    public Sprite sprite() {
        return currentSprite;
    }

    public Image getFrame() {
        return currentSprite.getImage();
    }
}

Ok so you call smiley.sprite().update(timePassed) each frame. That update method calls the Animation inside Sprite. So either there is something wrong with your Animation class, or the loop isn’t running, or the screen won’t update. Your Animation class seems fine, except for this line in your update method: “currentTime = 0”, that should be “currentTime %= totalTime;”
Try making sure your loop is actually running by putting a println in there. Then try making sure paint() is actually being called by also putting a println. If everything is working, try using a debugger to see why it won’t update because everything seems fine to me.

I found the problem: you not calculating timePassed correctly in your game loop. totalTime will always equal 0 so timePassed will always be System.currentTimeMillis(). The correct way of calculating timePassed:


long lastTime = System.currentTimeMillis();
while(true) {
    long now = System.currentTimeMillis();
    long timePassed = now-lastTime;
    lastTime = now;
    
    //update
    
    repaint():
    
    //Here I accounted for the amount of time it took to update and render so you can have a reliable and steady loop.
    try{
        Thread.sleep(10-(System.currentTimeMillis()-now));
    }
    catch(Exception exc) {
    }
}

Thank You! It works perfectly now! ;D