Thursday, March 31, 2011

Moving Objects and Sprite Manager - Part 1

Simple moving objects


A moving object is often known as a sprite in the world of game programming. It is simple to write a routine for moving a small object across the screen. We just need simple formula relating the velocity and the coordinates of the object.

Suppose an object is currently located in (x,y). We can define the next coordinates by the velocity formula :
x = x+xv;
y = y+yv;

The (x,y) coordinates are calculated on each animation frame.

For example, if xv = 3 and yv = 0, then the object is moving to the right at a rate of 3 pixels per frame. If yv = -1 and xv = 0, then the object is moving upwards at a rate of 1 pixel per frame.

The anatomy of a moving object


We would make an object moving across the screen by repeating the following steps.
  1. Erase the object at old position.
  2. Update the position of the object by the velocity formula.
  3. Draw the object at new position.
  4. Wait for a short time so that the user can see the object.

It is commonly agreed that we need 20 to 30 frames per second (FPS) to achieve a reasonably smooth animation. It is best to implement the animation in a timer thread. The thread is actually a loop that sleep for 30 ms for each pass. Since 1 second contains 1000 ms, this effectively equals to an FPS of 33. (1000ms / 30 ms = 33 ) Of course the actual FPS is a bit smaller due to the overhead of redrawing the object and repainting the screen. The animation thread is listed as follows :

// thread entry point
  public void run()
  {
    try
    {
      while (true)
      {
        moveBall();       // move the ball to a new position
        repaint();        // redraw the panel.
        Thread.sleep(30); // delay so that user can see the ball.
      }
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }


Demonstration

A full sample of a bouncing ball follows. This should be simple enough as a starting point of writing simple animation program.

/******************************************************************************
* File : BouncingBall.java
* Author : http://java.macteki.com/
* Description :
*   Display a bouncing ball.
* Tested with : JDK 1.6
******************************************************************************/


import java.awt.image.BufferedImage;

class BouncingBall extends javax.swing.JPanel implements Runnable
{
  BufferedImage image=new BufferedImage(300,300,BufferedImage.TYPE_INT_RGB);

  int xBall=100, yBall=100;   // initial coordinates of the moving ball
  int xVelocity=3;            // moving 3 pixels per frame (to the right)

  public BouncingBall()
  {
    int w=image.getWidth(this);
    int h=image.getHeight(this);
    this.setPreferredSize(new java.awt.Dimension(w,h));
  }
 
  // override the paint() method, we don't count on the system.
  // We draw our own panel.
  public void paintComponent(java.awt.Graphics gr)
  {
    // Redraw the whole image instead of redrawing every object in the screen.
    // This technique is commonly known as "double buffering"
    gr.drawImage(image,0,0,this);
  }  


  // start the bouncing thread
  public void start()  
  {
    new Thread(this).start();
  }

  // thread entry point
  public void run()
  {
    try
    {
      while (true)
      {
        moveBall();       // move the ball to a new position
        repaint();        // redraw the panel.
        Thread.sleep(30); // delay and let the user see the ball.
      }
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }

  
  public void moveBall()
  { 
    // Double Buffering Technique :
    // Erase and redraw everything in a image object 
    // instead of directly drawing to the screen.
    java.awt.Graphics2D gr=(java.awt.Graphics2D) image.getGraphics();

    // erase ball at old position
    int diameter=10;
    gr.setColor(java.awt.Color.BLACK);  // this is the background color
    java.awt.geom.Ellipse2D circle=
      new java.awt.geom.Ellipse2D.Double(xBall,yBall,diameter,diameter);

    gr.fill(circle);

    // update ball position
    xBall+=xVelocity;
    if (xBall>=300-diameter || xBall<0) // hit border
    {
      xBall-=xVelocity;        // undo movement
      xVelocity=-xVelocity;    // change direction of velocity
    }

    // draw ball at new position
    gr.setColor(java.awt.Color.RED);  // this is the ball color
    java.awt.geom.Ellipse2D newCircle=
      new java.awt.geom.Ellipse2D.Double(xBall,yBall,diameter,diameter);

    gr.fill(newCircle);
  }

  public static void main(String[] args) throws Exception
  {
    javax.swing.JFrame window = new javax.swing.JFrame();
    window.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
    window.setTitle("Macteki bouncing ball");

    BouncingBall ball=new BouncingBall();
    window.add(ball);

    window.pack();
    window.setVisible(true);
    
    ball.start();
  }
}

Part 1 completed. To be continued...

2 comments: