Animation Using timers


The displaying of bitmaps can be quite a rapid process. Most modern games involve the display and animation of bitmaps. This section demonstrates the use of timers to animate the display of bitmaps. In this case, a bitmapped ball will be shifted around a window. A snapshot of the bouncing ball is shown below.

Creating the Ball

The creation message (see source code) of the client window does little except to query the aspect ratio of a pixel and start a timer, as shown in the code fragment below.

case message::create:
 {
  ...
  handle device_context = get_device_context(window_handle);
  xPixel = get_device_capabilities(device_context,capability::AspectX);
  yPixel = get_device_capabilities(device_context,capability::AspectY);
  release_device_context(window_handle,device_context);
 }
 break;

The timer is set at 10 milliseconds - which exceeds the maximum rate for a standard clock (so high performance counter hardware is assumed to be installed). Most of the hard work is done when sizing the window, where the bitmap for the ball is created - as shown below.

case message::size:
 {
  XCenter = (width_of_client  = low_part(parameter2)) / 2;
  YCenter = (height_of_client = high_part(parameter2)) / 2;

  int Scale = minimum(width_of_client * xPixel,
                          height_of_client * yPixel) / 16;

  XRadius = Scale/xPixel;
  YRadius = Scale/yPixel;

  XMove = maximum(1,XRadius/8);
  YMove = maximum(1,YRadius/8);

  XTotal = 2 * (XRadius + XMove);
  YTotal = 2 * (YRadius + YMove);

  if (bitmap) delete_object(bitmap);

  handle device_context = get_device_context(window_handle);
  handle MemoryDevice = create_memory_device_context(device_context);
  bitmap = create_compatible_bitmap(device_context,XTotal,YTotal);
  release_device_context(window_handle,device_context);

  select_object(MemoryDevice,bitmap);
  draw_rectangle(MemoryDevice,-1, -1,XTotal+1,YTotal+1);

  handle BrushHandle = create_hatch_brush(hatch_style::Diagonalcross,0);
  select_object(MemoryDevice,brush);
  set_background_color(MemoryDevice,red_green_blue(255,0,255));

  draw_ellipse(MemoryDevice,
              XMove,
              YMove,
              XTotal-XMmove,
              YTotal-YMove);

  delete_device_context(MemoryDevice);
  delete_object(brush);
 }
 break;

The inital position of the ball is the center of the client window (XCenter,YCenter). The size of the ball is 1/16th the size of the width or height of the client window (whichever is smaller). The ball is moved 1/8th of its original size each time the timer ticks. If a smaller fraction like 1/16th is used, the ball appears to travel more smoothly and slowly. A device context and a memory device context are created. The bitmap is replaced and then selected into the newly created memory device context. A rectangle is drawn and a hatch brush is set, the background color is changed and the ball is drawn using the ellipse drawing function. This completes the creation of the bitmap for the ball.

When a timer message is received, a bit block transfer is used to shift the ball - as shown below.

case message::timer:
 if (bitmap)
  {
   handle device_context = get_device_context(window_handle);
   handle MemoryDevice = create_memory_device_context(device_context);
   select_object(MemoryDevice,bitmap);

   bit_block_transfer(device_context,
                    XCenter-XTotal/2,
                    YCenter-YTotal/2,
                    XTotal,
                    YTotal,
                    MemoryDevice,
                    0, 0, RasterSourcecopy);

   release_device_context(window_handle,device_context);
   delete_device_context(MemoryDevice);

   XCenter += XMove;
   YCenter += YMove;
   ...
  }
  break;

Firstly, a device context and a memory device context are created and the ball bitmap is selected into the memory device context. A bit block transfer is then used to draw the ball at its center. The new center is then recalculated for when the next timer message is received. The center is displaced 1/8th the radius of the ball (as previously mentioned).