Converting to Active Rendering


The current painting strategy is to call repaint( ) in run( )'s animation loop:

     while(running) {       gameUpdate( );   // game state is updated       gameRender( );   // render to a buffer       repaint( );      // paint with the buffer       try {         Thread.sleep(20);  // sleep a bit       }       catch(InterruptedException ex){}     }

Since a call to repaint( ) is only a request, it's difficult to know when the repaint has been completed. This means that the sleep time in the animation loop is little more than a guess; if the specified delay is too long, then the animation speed is impaired for no reason. If the delay is too short, then repaint requests may be queued by the JVM and skipped if the load becomes too large.

In fact, no single sleep time is satisfactory since the time taken to update and render a frame will vary depending on the activity taking place in the game. The sleep time must be calculated afresh each time round the loop after measuring the iteration's update and rendering periods. Unfortunately, the repaint( ) part of the rendering is done by the JVM and cannot be easily measured.

As a first step to dealing with these issues, I switch to active rendering, shown below as modifications to run( ):

     public void run( )     /* Repeatedly update, render, sleep */     {       running = true;       while(running) {         gameUpdate( );   // game state is updated         gameRender( );   // render to a buffer         paintScreen( );  // draw buffer to screen         try {           Thread.sleep(20);  // sleep a bit         }         catch(InterruptedException ex){}       }       System.exit(0);     } // end of run( )     private void paintScreen( )     // actively render the buffer image to the screen     {       Graphics g;       try {         g = this.getGraphics( );  // get the panel's graphic context         if ((g != null) && (dbImage != null))           g.drawImage(dbImage, 0, 0, null);         Toolkit.getDefaultToolkit( ).sync( );  // sync the display on some systems         g.dispose( );       }       catch (Exception e)       { System.out.println("Graphics context error: " + e);  }     } // end of paintScreen( )

The call to repaint( ) is gone, as is the overriding of paintComponent( ); its functionality has been incorporated into paintScreen( ).

Active rendering puts the task of rendering the buffer image to the screen into my hands. This means that the rendering time can be accurately measured, and concerns about repaint requests being delayed or skipped by the JVM disappear.

However, the panel's graphics context may be changed by the JVM, typically when the canvas is resized or when it becomes the front window after being behind others. The context may disappear if the application or applet exits while the animation thread is running. For these reasons, the graphics context must be freshly obtained each time it is needed (by calling getGraphics( )), and its use must be surrounded by a TRy-catch block to capture any failure due to its disappearance.

In practice, if the program has a fixed window size, then the most likely time for an exception is when a game applet is terminated by the user closing its surrounding web page.

The call to Toolkit.sync( ) after drawImage( ) ensures that the display is promptly updated. This is required for Linux, which doesn't automatically flush its display buffer. Without the sync( ) call, the animation may be only partially updated, creating a "tearing" effect. My thanks to Kyle Husmann for pointing this out.



Killer Game Programming in Java
Killer Game Programming in Java
ISBN: 0596007302
EAN: 2147483647
Year: 2006
Pages: 340

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net