Home arrow Java arrow Page 5 - Swing Animation
JAVA

Swing Animation


Have you ever been interested in creating a game using the Swing-based animation library? This article covers some important information to help you understand the backbone of this library. It is excerpted from the book Advanced Java Game Programming, written by David Wallace Croft (Apress, 2004; ISBN 1590591232).

Author Info:
By: Apress Publishing
Rating: 5 stars5 stars5 stars5 stars5 stars / 19
March 29, 2005
TABLE OF CONTENTS:
  1. · Swing Animation
  2. · RepaintCollector
  3. · SimpleRepaintCollector
  4. · CoalescingRepaintCollector
  5. · LoopGovernor
  6. · WindowedLoopGovernor
  7. · AnimatedComponent
  8. · Static method check()

print this article
SEARCH DEVARTICLES

Swing Animation - LoopGovernor
(Page 5 of 8 )

When it comes to animation, timing is everything. If the frame rate is too fast, you will be burning unnecessary processing time generating new frames faster than the human eye can detect, faster than the monitor can display, and faster than the sprites can move. If the frame rate is too slow or the time period between frames is inconsistent, the animation will appear jerky.

As shown in Figure 3-2, you can observe the smoothness of animation using the Sprite demonstration. In the options, turn off all painting except for the head sprites. You may need to disable double buffering as well. Set the target frame rate to some reasonable value, such as 30 fps. Since the background is not being painted, you should see the head sprites being drawn at each new position with the old positions remaining on the screen. From this you should be able to determine the distance and consistency of the displacement between old and new sprite positions.


Figure 3-2.  Observing smoothness

The smoothness of your animation depends primarily on your choice of LoopGovernor from package com.croftsoft.core.util.loop. Concrete implementations of interface LoopGovernor regulate the frame rate by putting the animation-Thread to sleep for a limited time. The term governor refers to a feedback device that regulates the speed of the machine, in this case the animation loop.

  public void govern ( )
    throws InterruptedException;

The LoopGovernor interface simply defines a single method, govern(), which is called by the animation loop. The magic occurs in the concrete implementations.

Fixed Delay

Our first LoopGovernor implementation from package com.croftsoft.core.util. loop, FixedDelayLoopGovernor, maintains a periodic rate by stalling the loop by a fixed delay given in milliseconds plus some additional number of nanoseconds.

  package com.croftsoft.core.util.loop;
  import com.croftsoft.core.math.MathConstants;
  public final class FixedDelayLoopGovernor
    implements LoopGovernor
  ////////////////////////////////////////////////////
  ////////////////////////////////////////////////////
  {
  private final long delayMillis;
  private final int delayNanos;
  /////////////////////////////////////////////////////
  ////////////////////////////////////////////////////
  public  FixedDelayLoopGovernor (
    long delayMillis,
    int  delayNanos )
  //////////////////////////////////////////////////
  {
    this.delayMillis = delayMillis;
    this.delayNanos = delayNanos;
  }
  public  FixedDelayLoopGovernor ( double frequency )
  /////////////////////////////////////////////////
  {
    if ( frequency <= 0.0 )
  {
     throw new IllegalArgumentException ( "frequency <=
     0.0" );
  }
  long periodNanos
   = ( long ) ( MathConstants.NANOSECONDS_PER_SECOND /
   frequency );
  delayMillis
 = periodNanos / MathConstants.NANOSECONDS_PER_MILLISECOND;
  delayNanos = ( int )
  ( periodNanos %
  MathConstants.NANOSECONDS_PER_MILLISECOND );
}

An alternate constructor accepts the frequency in units of fps as a constructor argument. This is converted into the delay period by simply taking the reciprocal of the frequency.

  public void govern ( )
    throws InterruptedException
  //////////////////////////////////////////////////
  {
    Thread.sleep ( delayMillis, delayNanos );
  }

Using a fixed delay works pretty well when the time it takes to update the sprite positions and repaint the component is short. In this case, the total time for the loop will be about equal to the fixed delay period, since update and repaint times are negligible. This will usually be the case when you have a small, simple animation.

Using a fixed delay also works well when you are not concerned with the actual frame rate achieved so long as it is consistent on a given machine. In this case you can set your target frame rate to some large limiting value. Twenty-four to thirty fps is fast enough to maintain the illusion of animation in most cases, but this may depend on how large your frame-to-frame sprite displacement is. You will want the highest frame rate you can achieve so that your sprite displacement is minimized no matter how fast your sprite velocity is. At the same time, there is no point in exceeding the monitor refresh rate since then this would waste processing time drawing frames faster than the monitor could display them. Many people such as myself can see annoying screen flicker at the monitor refresh rate of 60 Hz, especially out of the corner of the eye. For this reason, the recommended standard for the monitor refresh rate is now 85 Hz. I recommend a maximum target frame rate of 85 fps.

Frame Rate Synchronization

Suppose you set your sprite velocity to 30 pixels per second and your average frame rate was 24 fps. The average frame-to-frame displacement for your sprite would be 1.25 pixels per frame. In other words, the sprite would move just one pixel in most frames but two pixels every fourth frame. This periodic displacement inconsistency may be noticeable.

You can avoid this by synchronizing your sprite velocity to your frame rate. Your sprite velocity is then expressed in integer values of pixels per frame instead of floating point values of pixels per second. Since your sprite velocities are now tied to your frame rate, it is important to know that you can achieve the same frame rate on both slow and fast machines. FixedDelayLoopGovernor cannot be used in these situations because it will only approach the targeted frame rate. On slower machines where the time spent in the paint and update phases may not be negligible, the frame rate could be drastically reduced.

What you need, then, is an implementation of LoopGovernor that uses a variable loop delay that is just long enough to achieve a desired frame rate no matter how fast or how slow the host machine is running. Unfortunately, however, this is hard to achieve. If you had access to a high-resolution clock, you could simply subtract out the update and repaint times from the desired total loop time and add a delay for whatever amount remains.

  while ( animationIsRunning )
  {
    long startTime = System.currentTimeMillis ( );
    update ( );
    repaint ( );
    long finishTime = System.currentTimeMillis ( );
    long elapsedTime = finishTime - startTime;
    long variableDelay = desiredLoopTime - elapsedTime; 
    if ( variableDelay < 0 )
    {
     variableDelay = 0;
    }
    Thread.sleep ( variableDelay );
 }

The above code will not work because the clock resolution on many machines is too coarse. On Windows/Intel (Wintel) machines, for example, the clock resolution jumps around between 50 and 60 ms. Keep in mind that animation starts to appear smooth to the human eye around 24 fps, which has a period of less than 42 ms per frame. Trying to measure the deviation of a 42 ms period requires a clock with a much finer resolution than 50 or 60 ms.

This is probably also the reason you cannot use the Swing Timer class to drive animation frame rates faster than about 20 fps. The Timer implementation appears to be dependent upon the System clock. This is unfortunate, as it is just a tad short of the acceptable film-quality animation frame rate of 24 fps. Using Thread.sleep() within a loop seems to give much finer control.

The second reason the above code would not work is that it is measuring the time it takes to request a repaint instead of the actual repaint time. Recall that the repaint() method merely queues a repaint request for serial execution by the event dispatch thread at some point in the future; it does not actually repaint the component when called.

SamplerLoopGovernor

While you can get high-resolution clock timing on the Windows operating system by calling native code instead of System.currentTimeMillis(), doing so means that your game cannot run as an unsigned applet or Java Web Start application due to security restrictions. I have experimented with a fair number of ways to achieve frame rate synchronization in pure portable Java. One technique that I have tried is to set the delay period based upon an estimate of the frame rate calculated over a fairly long sample time. To do this you simply count the number of frames that were painted and then divide by the time it took to draw those frames. Using this technique, it does not matter how coarse your clock resolution is so long as your sample period is large enough.

I implemented this technique in class SamplerLoopGovernor from package com.croftsoft.core.util.loop that uses a default sample period of three seconds. To prevent the animation frame rate from suddenly speeding up or slowing down from one sample period to the next, the delay to be used during the next sample period is the average of the newly calculated estimated-target delay and the delay used during the previous sample period.

Even with this smoothing operation, however, there are still problems related to the length of the sample period. An excessively long sample period can mean that the system cannot adapt fast enough to sudden changes in animation complexity or available processing time. On the other hand, if the sample period is too short, the number of frames counted during the period approaches one and the resolution of the estimate drops.

This article is excerpted from Advanced Java Game Programming by David Wallace Croft (Apress, 2004; ISBN 1590591232). Check it out at your favorite bookstore today. Buy this book now.


blog comments powered by Disqus
JAVA ARTICLES

- Java Too Insecure, Says Microsoft Researcher
- Google Beats Oracle in Java Ruling
- Deploying Multiple Java Applets as One
- Deploying Java Applets
- Understanding Deployment Frameworks
- Database Programming in Java Using JDBC
- Extension Interfaces and SAX
- Entities, Handlers and SAX
- Advanced SAX
- Conversions and Java Print Streams
- Formatters and Java Print Streams
- Java Print Streams
- Wildcards, Arrays, and Generics in Java
- Wildcards and Generic Methods in Java
- Finishing the Project: Java Web Development ...

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2017 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials