[java] Creating a Double Buffer

Started by
8 comments, last by Jonathon 22 years, 6 months ago
Hey, everyone! I have just started my first semester studying Java in school. But the class moves a bit slow, and we won''t be doing anything interesting with graphics or animation for quite some time. So I''ve been checking things out on my own. I have read Adam King''s first two tutorials on Java Game Programming. I have created a few simple applets, which feature a ball bouncing around inside a box. (Imagine Breakout without the paddles, blocks or scoring, and you''ve got the idea.) Of course, there is considerable flicker, and I would like to implement a double buffer. I understand the concept of double buffering. I know why it works, and I understand the technology involved. But I can''t get the implementation. The tutorials are very good, except that they do not feature a complete code example of double buffering. Something is wrong in my init() method, and while the code compiles, it won''t run. I keep getting a null pointer exception. And unfortunately, I haven''t gotten the hang of navigating the JDK documentation yet. Could someone give a brief code example, from data declaration, through init(), run() and paint()? Gee, that sounds bad, doesn''t it? It sounds like "Hey, can you write my code for me?" That''s not really what I''m looking for. Perhaps it would be better for me to post what I''ve got. I''ll be able to post my code tomorrow. But if anyone would like to give an example in the meantime, that would be good too. Thanks. Jonathon
quote:"Mathematics are one of the fundamentaries of educationalizing our youths." -George W. Bush
Jonathon[quote]"Mathematics are one of the fundamentaries of educationalizing our youths." -George W. Bush"When a nation is filled with strife, then do patriots flourish." - Lao Tzu America: Love it or leave it ... in the mess it's in. [/quote]
Advertisement
Hola Jonathon,

Double buffering in Java really isn't that hard, here's what it involves. (Also see code below)

First, you create an Image that you use for your offscreen buffer. Make sure it's the same size as the window/applet you're rendering to. Then inside your paint function, retireve the graphics object of that image, draw on that Graphics, dispose that Graphics, then draw the offscreen image onto the screen.

Example:

        import java.awt.*;public class DBTest extends java.applet.Applet{  private Image buffer = null;  public void init ()  {    // Note: createImage is a method of java.awt.Component    // so you can create an image off of any component, not    // just applets.    buffer = createImage(getSize().width, getSize().height);  }  public void update (Graphics g)  {    // Overriding this function makes sure that Java does    // not fill the screen with the background color    // between frames    paint(g);  }  public void paint (Graphics g)  {    Graphics b = buffer.getGraphics();    if (b != null)    {      myPaint(b);      b.dispose();    }    g.drawImage(buffer, 0, 0, this);  }  public void myPaint (Graphics b)  {    // Do your drawing in here  }}  


This can be optimized, but I will leave that to you .

Inside your run() function you can now do one of two things to re-draw the screen: you can call repaint() which schedules a Repaint Event on the Event Dispatch Thread. This is ok, but not immidiate (probably what you want for games). So try this:

  public void run (){  while (isRunning)  {    // Do all per-frame updates here    Graphics g = getGraphics();    if (g != null)    {      paint(g);      g.dispose();    }  }}  


Just always remember to call dispose() on any Graphics objects you get via getGraphics() when you're done with them.

Also check out the new Full-Screen Exclusive Mode API. With the above method, the offscreen buffer has to be copied on-screen every frame. With the new FSEM API you can use the full-screen page-flipping capabilities of the underlying graphics hardware. Page flipping would be like having two images and just flipping between which one is on-screen. This requires much less data copy and should improve performance, but it's only available in the new beta version of Java 1.4.

"So crucify the ego, before it's far too late. To leave behind this place so negative and blind and cynical, and you will come to find that we are all one mind. Capable of all that's imagined and all conceivable."
- Tool



Edited by - wayfarerx on October 24, 2001 12:11:45 PM
"There is no reason good should not triumph at least as often as evil. The triumph of anything is a matter of organization. If there are such things as angels, I hope that they're organized along the lines of the mafia." -Kurt Vonnegut
Thanks for your help, WayfarerX.

When I had originally written the code, I received deprecation warnings regarding the getSize() methods. So I tried another means of setting the offscreen buffer to the same size as the application. Perhaps it would be best if I posted my code. I keep getting a Null Pointer exception, which I'll list after the code. (Forgive me if this isn't formatted very well. I've never used the "code" tag before.)

import java.applet.*;import java.awt.*;public class BouncingBox extends Applet implements Runnable {  // Data members  // Variables for size of window, and for the Box to bop about in it  int xBoxMin, yBoxMin, xBoxMax, yBoxMax;  int xWinMin, yWinMin, xWinMax, yWinMax;  // Dir variables are used to indicate the direction in which the box will wander  int xDir, yDir, animSpeed, boxSize;  // This is one of the parts I'll probably fumble miserably  Image offscreenImage; Graphics offscreen;  Thread t;  public void init () {    setBackground ( Color.black );    xDir = 1; yDir = 1; animSpeed = 6; boxSize = 10;    // Set the starting location for the box to ( 20, 5 )    xBoxMin = 20;  yBoxMin = 5;    xBoxMax = ( xBoxMin + boxSize );    yBoxMax = ( yBoxMin + boxSize );    // Hard code the dimensions to 400 x 300    xWinMin = 0;  yWinMin = 0;    xWinMax = 400;  yWinMax = 300;    // Start the thread    t = new Thread ( this );    t.start();    // Screw something up    offscreenImage = createImage ( xWinMax, yWinMax );    offscreen = offscreenImage.getGraphics();  }  public void run () {    // Set up an infinite loop, just to be safe   This next part controls the direction of the box    while ( true ) {      if ( xBoxMin <= xWinMin ) { xDir = 1; }      if ( yBoxMin <= yWinMin ) { yDir = 1; }      if ( xBoxMax >= xWinMax ) { xDir = -1; }      if ( yBoxMax >= yWinMax ) { yDir = -1; }      // Set the box's new location      xBoxMin = xBoxMin + ( xDir * animSpeed );      yBoxMin = yBoxMin + ( yDir * animSpeed );      xBoxMax = xBoxMax + ( xDir * animSpeed );      yBoxMax = yBoxMax + ( yDir * animSpeed );      // Draw the image      repaint();      try { t.sleep ( 300 ); }      catch ( InterruptedException e ) { ; }    }  // End while loop  } // End run()  public void paint ( Graphics g ) {    // Draw to the offscreen buffer    offscreen.setColor ( Color.black );    offscreen.fillRect ( 0, 0, xWinMax, yWinMax );    offscreen.setColor ( Color.white );    offscreen.fillRect ( xBoxMin, yBoxMin, xBoxMax, yBoxMax );    // Transfer the drawn image to the onscreen buffer    g.drawImage ( offscreenImage, 0, 0, this );  }  // End paint()  public void update ( Graphics g ) {    // Do this because I've been told to    paint ( g );  }  // End update()} End BouncingBox class  


And here are the error messages I've been getting:

quote:

java.lang.NullPointerException

at my_applets.BouncingBox.init (BouncingBox.java: 34 )

at com.borland.jbuilder.runtime.applet.AppletTestbed.startApplet ( Unknown source )

at com.borland.jbuilder.runtime.applet.AppletTextbed.main ( Unknown source )



I'm using one of the computers at school, so I can't stay on much longer. I'll print up what WayfarerX has posted, and check it out more closely when I get home.

Thanks again, WayfarerX!



Jonathon
quote:"Mathematics are one of the fundamentaries of educationalizing our youths." -George W. Bush


Edited by - Jonathon on October 25, 2001 3:17:07 PM
Jonathon[quote]"Mathematics are one of the fundamentaries of educationalizing our youths." -George W. Bush"When a nation is filled with strife, then do patriots flourish." - Lao Tzu America: Love it or leave it ... in the mess it's in. [/quote]
Well, I believe you''ve stumbled onto a bug in the jbuilder AppletTestBed setup. I copied your code down, compiled it, and ran it in IE and it works fine (except that sleeping for 300 ms is about 9 fps ). Here''s what I think''s going on.

The Java AWT is weird, and a lot of issues arise from trying to get it initalized properly. When you''re creating offscreen buffers, a heavyweight AWT component is requred (this is why you can''t do it on systems without a UI). An Applet is a heavyweight component, but somehow the JBuilder test bed is failing to initalize it before calling your init() function. When you create the image, it returns null. When you call getGrahics() on null, well, stuff blows up.

The solution? In init() set offscreenImage & offscreen to null. Then, in your paint() function, check if offscreenImage is null, if it is then create it. Then check offscreen, if it''s null re-create it too. Be sure and always re-create offscreen if you re-create offscreenImage (don''t want to use a Graphics that doesn''t draw on the buffer!).

Couple of other little notes:

1) Component.size() is deprecated, Component.getSize() is not.

2) If you''re creating a thread in init() that uses global variables (like the buffer & graphics), be sure and initalize those varables BEFORE starting the thread. It''s unlikely that the thread will reach those variables before the initalization completes, but it''s definately not impossible.

3) I saw your comment on your update() function. Just so you know this is what update() does:

  public void update (Graphics g){  g.setColor(getBackground());  g.fillRect(0, 0, getSize().width, getSize().height);  g.setColor(getForeground());  paint(g);}  


So if you don''t override it, your on-screen applet will be filled with the applet''s background color (what color depends on the system) for a split second, then your buffer will be drawn. This causes a really ugly flickering effect.

4) the tag is ''source'' not ''code'' for these messageboards.

Don''t you hate it when something actually works, but something like a bug in your IDE makes you think it doesn''t? Keep plugging man!

"So crucify the ego, before it''s far too late. To leave behind this place so negative and blind and cynical, and you will come to find that we are all one mind. Capable of all that''s imagined and all conceivable."
- Tool

"There is no reason good should not triumph at least as often as evil. The triumph of anything is a matter of organization. If there are such things as angels, I hope that they're organized along the lines of the mafia." -Kurt Vonnegut
It''s weird that you got a deprecated error from getSize(), are you sure you''re not confusing it with size() which was deprecated from JDK 1.1 onwards.

I ran copied and compiled your code and it runs fine on my computer, I ran it with both the JDK AppletViewer an in Internet Explorer 5.5. I might be a Borland specific error.

The only thing that I cna think of that could cause a problem is that you run the thread before you create your offscreen buffer which means that when paint() is called for the first time, offscreen and offscreenimage will both be null.

Try swapping round the following code and putting it in a public void start() method

i.e.

public void start() {
offscreenImage = createImage(xWinMax, yWinMax);
offscreen = offscreenImage.getGraphics();

t = new Thread(this);
t.start();
}


- Kaijin

"If you find a job that you love you''ll never have to work again."
Great minds think alike, WayfarerX

- Kaijin

"If you find a job that you love you''ll never have to work again."
You said it Kaijin , I actually forgot about start(), but that reminds me of something else.
Jonathon: you''re going to want to override stop() as well and stop your update thread. Otherwise that baby will run until the user closes their browser. (stop() is called when the user leaves the page with your applet).

"So crucify the ego, before it''s far too late. To leave behind this place so negative and blind and cynical, and you will come to find that we are all one mind. Capable of all that''s imagined and all conceivable."
- Tool

"There is no reason good should not triumph at least as often as evil. The triumph of anything is a matter of organization. If there are such things as angels, I hope that they're organized along the lines of the mafia." -Kurt Vonnegut
Thanks for your help, folks!

I was surprised to hear that you actually took the time to compile my code. That''s very cool. (I''m also glad to hear that it worked.)

Thanks for giving me not only possible solutions, but for also helping me to understand exactly what''s going on. That''s the most important part. If I just knew how to fix what was wrong here, I''d probably just end up making similar mistakes in the future. For instance, now that I know the "sleep()" method is measured in ms, I''ll be able to use that method more competently in the future.

I was going crazy trying to debug my code! I tried this, I tried that, I did a spiritual dance... but nothing seemed to work. I''m glad you were able to give me a sense of what''s taking place. Because otherwise, I''d be driving myself crazy on my next project as well!

Since my college just began to offer Java courses, my professor is also just learning the language. She can certainly offer good direction when it comes to the material we''re currently working on in class. But when it comes to more advanced topics, I''m pretty much on my own. So I really appreciate the help.

I can''t wait to get homw and tinker about with this!
Jonathon[quote]"Mathematics are one of the fundamentaries of educationalizing our youths." -George W. Bush"When a nation is filled with strife, then do patriots flourish." - Lao Tzu America: Love it or leave it ... in the mess it's in. [/quote]
quote:Original post by Jonathon
I can''t wait to get homw and tinker about with this!


Ahh, there''s nothing like seeing someone excited about learning . *WayfarerX sends warm fuzzy feelings to the GDNet community*

In the future, a lot of your questions can be answered with realtive ease. A link for your bookmarks: Java Platform Documentation. This page contains links to the API documentation for each version of Java (which explains every Class and all of their methods & properties), the Java Language Specification, Virtual Machine Specification, and articles about all the different components of the language. The info within these docs will answer many of your questions.

Another great place to look is in your JDK directory for the ''src.jar'' file. This is basicly a zip file with the source code to the entire Java Platform (minus native methods). Not only can this give you a ton of insight into how the whole platform works, you also see good examples of how to code.

Best of luck man!

"So crucify the ego, before it''s far too late. To leave behind this place so negative and blind and cynical, and you will come to find that we are all one mind. Capable of all that''s imagined and all conceivable."
- Tool

"There is no reason good should not triumph at least as often as evil. The triumph of anything is a matter of organization. If there are such things as angels, I hope that they're organized along the lines of the mafia." -Kurt Vonnegut
AWESOME! Thanks again. I''ve been trying to check out the documentation for my IDE. But I keep getting crashes. And it takes a while to load up the IDE each time, what with the licensing screens and all.... What a pain! So I''ll definitely check out this other documentation.

Also, thanks to WayfarerX and Kaijin, I was able to make a few quick changes and get my original applet up and running. Of course, I''ve been playing around with it. And I did change the sleep time!

I''m pretty busy with school. I have some big projects coming up, so I don''t have as much time to play as I would like. But there''s nothing like creating something from an idea and a few lines of code. I''m definitely hooked!


Jonathon
quote:"Mathematics are one of the fundamentaries of educationalizing our youths." -George W. Bush
Jonathon[quote]"Mathematics are one of the fundamentaries of educationalizing our youths." -George W. Bush"When a nation is filled with strife, then do patriots flourish." - Lao Tzu America: Love it or leave it ... in the mess it's in. [/quote]

This topic is closed to new replies.

Advertisement