problems with animation

Hello,

Probably this question is too easy to most of you, but I feel like stuck.

I’ve got everything ok with moving the image, but when I draw different images (from array), I see some flashing.

You can try the applet here:
http://asphaltgalaxy.com/test2/animtest.html

Press space to see that unwanted effect or press left or right to move.

Shortly about my animation loop:

I’ve got JPanel:

public class Board extends JPanel implements ActionListener, Runnable {

and there is I think pretty standard run method in it:


    public void run () {

        long beforeTime, timeDiff, sleep;

        beforeTime = System.currentTimeMillis();

        while (true) {

            UpdateGame();
            repaint();

            timeDiff = System.currentTimeMillis() - beforeTime;
            sleep = DELAY - timeDiff;

            if (sleep < 0) sleep = 2;
            try {
                Thread.sleep(sleep);
            }
            catch (InterruptedException e) {
                System.out.println("interrupted");
            }

            beforeTime = System.currentTimeMillis();
        }
    }

My paint method is like this:


    public void paint(Graphics g) {
        super.paint(g);
        switch (gamestate) {
            case RUNNING: {
                DrawRunning (g);
             }
        }
        Toolkit.getDefaultToolkit().sync();
        g.dispose();
    }

And finally DrawRunning runs this method of Paddle class:


    public void Draw (Graphics g, JPanel p) {
        
        switch (state) {
            case GET_NARROWER:
                g.drawImage(narrower[narrowerframe], x, y, p);
                break;
            case GET_WIDER:
                g.drawImage(wider[widerframe], x, y, p);
                 break;
            default:
                g.drawImage(paddle, x, y, p);
                break;
        }
    }

where wider and narrower arrays of images:


    Image[] wider = new Image[WIDER_FRAMES];
    Image[] narrower = new Image[NARROWER_FRAMES];

And arrays are filled in Paddle class when you press space (I’ve got one image which I resize frame by frame):


    public void Wider () {
        if (state == State.NORMAL) {
            state = State.GET_WIDER;
            sizechange = WIDER_SIZE / WIDER_FRAMES;
            widerframe = 0;
             for (int i = 0; i < WIDER_FRAMES; i++) {
                wider[i] = iid.getImage().getScaledInstance(w + sizechange * (i + 1), H, 0);
            }
            effectdelay = EFFECT_DELAY;
        }
    }

Any help needed, what and where I am doing wrong?

If I didnt provided some important code, please, let me know… Just didnt want to paste a lot of it to make main logic as clear as possible.

Unable to check the applet but I’m fairly sure the problem is that you’re not using double buffering. Luckily its fairly easy to implement

I don’t see any flickering in the test applet :slight_smile:

Oh I hit space again and I noticed the flashing. It doesn’t always happen, most of the time it works fine. If it was flashing all the time then that would be a problem with either your animation or with how you resize, but since it works sometimes, the only place there might be a problem is how you do the animation.

EDIT: Could you post the entire class, I think I have a hunch onto why this is happening.

Thanks for answering!

Yeah, I forgot to mention that I do use doublebuffering provided by JPanel:

setDoubleBuffered(true);

Maybe these are roots of evil? :slight_smile:

For me it almost always flashes (those few times I think I saw that it didn’t flash I can address to my imagination :slight_smile: )

Anyway, here is full Paddle class listing:


public class Paddle {
    
    enum State {NORMAL, WIDE, GET_WIDER, GET_NARROWER};
    State state = State.NORMAL;
    
    public enum Direction {LEFT, RIGHT, NONE};
    int dm = 0, w;
    Direction direction = Direction.NONE;
    
    Image paddle;
    
    ImageIcon iid = new ImageIcon(this.getClass().getResource("paddle.png"));
    
    final int STARTX = 200, STARTY = Board.BOARD_Y_TO - 40, SPEED = 15, SPEEDD = 7, W = 80, H = 15;
    final int XLEFT = BrickBoard.X, XRIGHT = BrickBoard.X + BrickBoard.W;
    final int EFFECT_DELAY = 50, WIDER_SIZE = 40, WIDER_FRAMES = 10, NARROWER_FRAMES = 10;
    int widerframe, narrowerframe, sizechange;
    int x, xprev, y, speed, effectdelay;
    
    Image[] wider = new Image[WIDER_FRAMES];
    Image[] narrower = new Image[NARROWER_FRAMES];
    
    public Paddle () {
        x = STARTX;
        y = STARTY;
        speed = 0;
        w = W;
        
        paddle = iid.getImage().getScaledInstance(W, H, 0);

    }
    
    public void Move (Direction d) {
        direction = d;
        speed = SPEED;
        switch (direction) {
            case LEFT: {
                dm = -1;
                break;
            }
            case RIGHT: {
                dm = 1;
                break;
            }    
        }
    }
    public void Stop () {
        direction = Direction.NONE;
    }
    public void Wider () {
        if (state == State.NORMAL) {
            state = State.GET_WIDER;
            sizechange = WIDER_SIZE / WIDER_FRAMES;
            widerframe = 0;
            xprev = x;
            for (int i = 0; i < WIDER_FRAMES; i++) {
                wider[i] = iid.getImage().getScaledInstance(w + sizechange * (i + 1), H, 0);
            }
            effectdelay = EFFECT_DELAY;
        }
    }
    
    public void Update () {
        
        switch (state) {
            case WIDE:
                if (--effectdelay == 0) {
                    state = State.GET_NARROWER;
                    sizechange = WIDER_SIZE / NARROWER_FRAMES;
                    narrowerframe = 0;
                    
                    for (int i = 0; i < NARROWER_FRAMES; i++) {
                        narrower[i] = iid.getImage().getScaledInstance(w - sizechange * (i + 1), H, 0);
                    }
                }
                break;
            case GET_WIDER:

                    w = W + sizechange * (widerframe + 1);
                    xprev = x;
                    x -= sizechange / 2;
                    widerframe++;
                    if (widerframe == WIDER_FRAMES) {
                        paddle = iid.getImage().getScaledInstance(W + WIDER_SIZE, H, 0);
                        state = State.WIDE;
                    }
                break;
                
            case GET_NARROWER:
                w = W - sizechange * (widerframe + 1);
                x += sizechange / 2;
                narrowerframe++;
                if (narrowerframe == NARROWER_FRAMES) {
                    paddle = iid.getImage().getScaledInstance(W, H, 0);
                    state = State.NORMAL;
                }                
                break;
        }
        
        if (direction == Direction.NONE) {
            speed -= SPEEDD;
            if (speed < 0) {
                speed = 0;
            }            
        }
      
        int dx = x + speed * dm;
        if (dx + w > XRIGHT) {
            x = XRIGHT - w;
        }
        else if (dx < XLEFT) {
            x = XLEFT;
        }
        else {
            x = dx;
        }    
                
    }
    
    public void Draw (Graphics g, JPanel p) {
        
        switch (state) {
            case GET_NARROWER:
                g.drawImage(narrower[narrowerframe], x, y, p);
                break;
            case GET_WIDER:
                g.drawImage(wider[widerframe], x, y, p);
                 break;
            default:
                g.drawImage(paddle, x, y, p);
                break;
        }
    }
}

When you hit Space Wider is called, and Update and Draw according to main game loop are executed.

Waiting for any info :slight_smile:

I can’t really comment on the update method, but I know one thing: what you’re trying to do is interpolating the scale of your paddle from a narrow size to a wider size, and vice-versa.

Instead of playing with state-machines and frames, I would recommend you to have a look to this library (it’s not self-advertising, the lib is free, it’s just that it could be a nice use-case ;D).

For instance, your Wider() method would be as follows:

    public void Wider () {
        myManager.killTarget(this, SCALE_X);
        Tween.to(this, SCALE_X, 500).target(2).ease(Quart.INOUT).start(myManager);
        Tween.to(this, SCALE_X, 500).target(1).ease(Quart.INOUT).delay(EFFECT_DELAY).start(myManager);
    }

line 1: Stops any running animation affecting the scale of our paddle,
line 2: Animates the scale of the paddle from its current value to twice its size (with a nice “quart” easing) during 500ms,
line 3: Wait for EFFECT_DELAY milliseconds, then animates the scale of the paddle from its current value to its original size (with a nice “quart” easing) during 500ms.

Moreover, your update() method would be heavily trimmed, since you wouldn’t need your state machine anymore.

public void Update () {
        // ------------------------------------------------
        // Your whole state machine would be replaced by:

        int deltaMillis = <compute_delta_here>;
        tweenManager.update(deltaMillis);

        // ------------------------------------------------
        
        if (direction == Direction.NONE) {
            speed -= SPEEDD;
            if (speed < 0) {
                speed = 0;
            }            
        }
      
        int dx = x + speed * dm;
        if (dx + w > XRIGHT) {
            x = XRIGHT - w;
        }
        else if (dx < XLEFT) {
            x = XLEFT;
        }
        else {
            x = dx;
        }    
                
    }

Simple enough?

Yep, you’re right. And thanks for link. When using library code is simplier, thats true. Library seems really useful. But I’ve got some goals here:

  1. Learn Java actually :slight_smile:
  2. Learn and feel (that it is more important for me) graphics programming and it’s concepts and capabilities
  3. Understand actually what went wrong here, cause till now everything went quite smooth. :slight_smile:

To add more knowthebetter suggestions :wink:

Try not to write the code “propperly” as learned in programming class.
Messie, linear code can help to get things done, and keep motivation up.

So dont structure your code too much yet.

Aha thanks :slight_smile: You think this will help me to solve paddle flashing on resize problem? :wink:

First, try to extend the paddle very slowly, and check if it still flickers.
So you know if its related to missing double buffering.

Delayed game loop to 2 FPS and removed background - paddle still flickers on resize but not every frame (randomly actually). When moving paddle - everything ok, like before.

I see that you regenerate the animation array every single time you call Wider(). It is best to generate these arrays when the class is first initialized, so then all you have to do is modify the “currentFrame” variables.

Actually moving this image generation to constructor didnt help. And as I understand shouldnt keeping in mind 2 FPS game speed for generation 10 images of size 80x15 pixels :slight_smile:

Started to debug and noticed, that my Draw method was called more often than game loop. Passing null as image observer stopped that.

Also I noticed, that image is not drawn for the first time! That probably caused flickering! So when I in my Draw method at the beginnig drew all images from array, images from this array were drawn correctly without flashing (when paddle was getting wider).


    public void Draw (Graphics g, JPanel p) {
        for (int i = 0; i < WIDER_FRAMES; i++) {
           g.drawImage(wider[i], 60, STARTY - 20 * (i + 2), null);
        }
        switch (state) {

            case GET_NARROWER:
                g.drawImage(narrower[narrowerframe], x, y, null);
                break;
            case GET_WIDER:
                g.drawImage(wider[widerframe], x, y, null);
                 break;
            default:
                g.drawImage(paddle, x, y, null);
                break;
        }
    }

Without this loop in the beginning and passing null as image observer I saw no paddle image at all when paddle was getting wider. Also putting same drawImage twice was not helping:


            case GET_WIDER:
                g.drawImage(wider[widerframe], x, y, null);
                g.drawImage(wider[widerframe], x, y, null);
                break;

I see, that I dont understand what happening :slight_smile: But maybe somebody could explain me?

My thoughts are like:

  1. I am using wrong game loop (posted in first message).
  2. I do not handle image drawing correctly.

Which one, or maybe both? Or something else? Where is truth? :slight_smile:

It seems I figured it out.

As found in getScaledInstance documentation: “…The new Image object may be loaded asynchronously even if the original source image has already been loaded completely…”

So I should use ImageIcon again when scaling my paddle. And code may look like that:


                    for (int i = 0; i < NARROWER_FRAMES; i++) {
                        ImageIcon ii = new ImageIcon (iid.getImage().getScaledInstance(w - sizechange * (i + 1), H, 0));
                        narrower[i] = ii.getImage();
                    }

Now it’s smooth like before :slight_smile: