animation help needed

I REALLY NEED HELP HERE!

I need some help with getting my animation to work correctly. I have tried every method I can find online and none of them seems to work fully. My ‘game’ so far is just the framework to get a ball to bounce on the screen. I have a problem that the ball is very jittery (not flicker). It seems as though every .5-3 seconds the image jumps instead of smoothly moving. It appears as though the system stop painting it for a short period or something. I have tried many things including double buffering, using accelerated graphics, running the animation on the main thread, running the animation on a separate thread, etc. None of these approaches seems to fix the problem. I have now tested this with my home computer (Win XP PIII 800 512MB, etc) and it is still happenening.

Here is the code for the Main Canvas


import java.awt.Canvas;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;


public class MyGame2 extends Canvas implements Runnable {
      private BufferStrategy strategy;
      private Ball theBall;
      private Thread animator;
      private boolean gameRunning = true;


      public MyGame2() {
            JFrame container = new JFrame("Chad Game");
            JPanel panel = (JPanel) container.getContentPane();
            panel.setPreferredSize(new Dimension(Vars.WIDTH,Vars.HEIGHT));
            panel.setLayout(null);
            setBounds(0,0,Vars.WIDTH,Vars.HEIGHT);
            panel.add(this);

            setIgnoreRepaint(true);
            container.pack();
            container.setResizable(false);
            container.setVisible(true);

            requestFocus();
            createBufferStrategy(2);
            strategy = getBufferStrategy();

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

            initEntities();
            startThread();
      }


        private void startThread() {
        if (animator == null || !gameRunning) {
            animator = new Thread(this);
          animator.start();
          }
      }


      private void initEntities() {
            theBall = new Ball();
             theBall.fire(400,250,10,10);
      }


      public void run() {
            double lastLoopTime = System.nanoTime();
            double nextFrameTime = lastLoopTime + 15000000;
            double currTime = lastLoopTime;

            while (gameRunning) {
                  double delta = System.nanoTime() - lastLoopTime;
                  lastLoopTime = System.nanoTime();
                  nextFrameTime = lastLoopTime + 15000000;
                  currTime = lastLoopTime;

                  Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
                  g.setColor(Color.black);
                  g.fillRect(0,0,Vars.WIDTH,Vars.HEIGHT);

                  double percent = delta/1000000000;
                  theBall.update(percent);

                  theBall.draw(g);

                  strategy.show();
                  while (currTime < nextFrameTime) {
                             Thread.yield();
                             try {
                                       Thread.sleep(1);
                             } catch (Exception e) {}
                                 currTime = System.nanoTime();
                  }
              }
      }


      public static void main(String argv[]) {
            new MyGame2();
      }
}
 

Here is the code for the ball


import javax.swing.*;
import java.awt.event.*;
import java.awt.*;


public class Ball
{
  private boolean alive;
  private double x, y;
  private double vX, vY;
  private int radius;
  private Image icon;
  private Image img;


  public Ball()
  {
    alive = false;
    radius = 10;
    icon = new ImageIcon("ball.gif").getImage();
    GraphicsConfiguration gc =             GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
    img = gc.createCompatibleImage(icon.getWidth(null),icon.getHeight(null),Transparency.BITMASK);
    img.getGraphics().drawImage(icon,0,0,null);
  }  // end of Ball()

  public void fire(double vex, double vye, int ex, int ye) {

    vX = vex;
    vY = vye;
    x = ex;
    y = ye;
    alive = true;

    //System.out.println("xVector: " + vX + "\n" + "yVector: " + vY);
  }

  public void update(double percent) {
    if(!alive)
      return;
    x = x + percent*vX;
    y = y + percent*vY;
    if(x <= 0.0 || x + (double)radius*2 >= (double)Vars.WIDTH)
      vX = -vX;
    if(y <= 0.0 || y + (double)radius*2 >= (double)Vars.HEIGHT)
      vY = -vY;
  }

  public void draw(Graphics g) {
    if(!alive)
      return;
    g.drawImage(img,(int)x,(int)y,null);
  }



}  // end of Ball class

And finally the Vars class for screen width


public class Vars
{
  public static int WIDTH = 600;
  public static int HEIGHT = 400;
  

}  // end of Vars class

Any advice would be much appreciated. If you want to run the code just put all the files in the same directory as any image with the name “ball.gif”.

Thanks

Hi, what OS do you use? I run Windows XP, and I experienced no problem.

If you are running another OS, especially Linux, you can try Toolkit’s sync() method.

Edit:

A tip is also to pass your “delta” value (time since last loop cycle) into your ball’s update method.

public void update(long delta)
{
x+= vxdelta;
y+= vy
delta;
}

This allows you control your movement on a constant time basis, instead of letting it depend on your current (fluctuating) framerate. Should remove your problems, I think.

Please take a look at my first post. I have edited it to contain the source code and an updated status of my problem. I am about out of ideas here, so I’m pretty desperate for some advice.

Thanks

Remove the thread.sleep(1), as you can’t rely on the given value : in some OS (e.g. win98) it’s equivalent to tread.sleep(50), and on some others, thread.sleep(10). (depends on thread timer resolution).

Best thing to do is just yield() (to give a change to other threads to do something) and move relatively to the elapsed time (deltaTime of Addictman).

Your code should work better after that (well… i hope so)

Lilian

Yes, do remove that sleep(1) please. In addition, you could try some Garbage collection tuning. Your program isn’t large, but Garbage collection can still cause a frame or two to skip during so called “major collections” of the GC. I used to use a great resource for this, but I forgot the link. I will post it later if I remember it, meanwhile you can look at sun’s notes on it:

http://java.sun.com/docs/hotspot/gc1.4.2/

And, there is another small thing you could do to further optimize your program. I doubt it has any real impact on your program as it is at the moment, but as it grows, it helps reduce renduncany of painting swing components.

It’s called a Null repaintmanager, which tells the application to ignore painting swing components through events. setIgnoreRepaint does part of the job, but not ALL of the job (go figure). The events are still passed, and parts of the code in these eventsis still checked/executed.

just make a class, EmptyRepaintManager, extend javax.swing.Repaintmanager and override the following methods.



public static void install()
 {
              RepaintManager repaintManager = new EmptyRepaintManager();
              repaintManager.setDoubleBufferingEnabled(false);
              RepaintManager.setCurrentManager(repaintManager);
          }

public void addInvalidComponent(JComponent c) {
              // do nothing
          }

          public void addDirtyRegion(JComponent c, int x, int y,
              int w, int h)
          {
              // do nothing
          }

          public void markCompletelyDirty(JComponent c) {
              // do nothing
          }

          public void paintDirtyRegions() {
              // do nothing
          }


then just put it to use with the static install() method.

I haven’t tried the repaint manager thing yet, but I have taken out the sleep(1) line (and the entire while loop for that matter), and just used a yield() call. I have gotten a marginal improvement with this method, but it still jitters and jumps. I have added some code to gather statistics and I think I have a better idea of what ISN’T causing the problem now. The average time through the entire loop was 1.92 ms, however I am getting a peak of 23 ms. I then assumed that this was from the yield surrendering for that long, but the longest that the yield took to return was a little over 2 ms. This means that I am loosing around 20 ms occasionally through this loop. I’m not even sure if this could be the source of the jump though because 20 ms is still 50 fps which should appear smooth to the human eye and not visibly jumpy and jittery. I will consider trying the repaint manager thing, but since this is such a simple app at the moment it seems to me that there has to be some more obvious problem lurking somewhere or else everyone would have this problem in every game written in Java (i mean what is more basic than a bouncing ball?).

Using the -verbose:gc option I have also determined that there is almost never a major garbage collection (all entries have GC, not Full GC as the tag).

Also (and this is the strangest part), I have noticed that seemingly because of this jitter and jumps the ball can occasionally actually get stuck on the edge of the window. With the current vector settings, it will most often start by heading towards the bottom of the screen at an angle. Every once in a while when it gets to the point that it should bounce off, it will glide horizontally along the bottom with a small amount of the bottom of the image out of the view. This really should not be possible unless there is a mistake in my code that I don’t see in the ball.update method.

Thanks for you advice so far everyone.

You know, the weird bouncing-of-the-wall behaviour isn’t as weird as you might think.

Remember, the speed is constant time-wise. When the ball is approaching a wall, imagine that the JVM performs some garbage collection. This takes time. You lose a frame or two. Suddenly, the ball moves 2-3 times of what it moves on normal frames. Thus, it it moved far outside the boundary of the window. Now, on the next cycle, your update method picks this up, and switches its speed around. However, because of the skipped frames, when the loop cycle comes to its update part again, the ball hasn’t managed to move within its bounds yet! So, it is still outside, and thus you switch it’s direction again. Voila, you got a ball crawling alone the line of your window.

Solution? Easy. In your update method, if the ball is out of bounds, set its position to be inside its bounds, and then switch its direction.



  if(!alive) 
 return; 
    x = x + percent*vX; 
    y = y + percent*vY; 

    if(x<0)
    { 
         x = 0;
         vX = -vX;
    }
    else if(x + ((double)radius*2)>(double)Vars.WIDTH)
    {
         x = Vars.WIDTH - ((double)radius*2);
         vX = -vX;
    }

    // and the same for Y
}


Ah, I should have thought that scenario through. I have updated my Ball class, and that part of it works fine now. I am still confounded by these jitters and jumps though.

Thanks