1

I am trying to achieve double buffering of my game in Java by overriding the update method for my JPanel, I do all the usual code etc and still it won't work, it throws a stack overflow error, below is the specific error:

Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError
        at java.awt.Rectangle.<init>(Rectangle.java:193)
        at java.awt.Rectangle.<init>(Rectangle.java:208)
        at sun.awt.image.BufImgSurfaceData.getBounds(BufImgSurfaceData.java:369)
        at sun.java2d.loops.GraphicsPrimitive.convertFrom(GraphicsPrimitive.java:533)
        at sun.java2d.loops.GraphicsPrimitive.convertFrom(GraphicsPrimitive.java:523)
        at sun.java2d.loops.MaskBlit$General.MaskBlit(MaskBlit.java:171)
        at sun.java2d.loops.Blit$GeneralMaskBlit.Blit(Blit.java:186)
        at sun.java2d.pipe.DrawImage.blitSurfaceData(DrawImage.java:927)
        at sun.java2d.pipe.DrawImage.renderImageCopy(DrawImage.java:550)
        at sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:54)
        at sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:982)
        at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:2979)
        at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:2964)
        at epicgame.Menu.displayMenu(Menu.java:71)
        at epicgame.GUI$1.paintComponent(GUI.java:64)
        at javax.swing.JComponent.paint(JComponent.java:1029)
        at epicgame.GUI$1.update(GUI.java:117)
        at epicgame.GUI$1.paintComponent(GUI.java:98)
        at javax.swing.JComponent.paint(JComponent.java:1029)

My code isn't particularly complex either:

mainPanel = new JPanel()
        {
            @Override protected void paintComponent(Graphics g)
            {
                //super.paintComponent(g);

                if(menuEnabled == 1)
                {
                    Menu.displayMenu(g, mainPanel);
                }
                else if(gameNum == 1)
                { 
                    StreetFighter.StreetFighter(g, mainPanel);

                    // Calls the controls method within the controls class.
                    Controls.controls(Calendar.getInstance().getTimeInMillis() - timeOld);
                    timeOld = Calendar.getInstance().getTimeInMillis();
                }
                else if(gameNum == -1)
                {
                    Menu.scoreBoard(g, mainPanel);
                    if(loaded != true)
                    {
                        Menu.loadScoreBoard(mainPanel);
                        loaded = true;
                    }
                }
                if(gameNum > 0)
                {
                    if(longcat == true && longcatloaded != true)
                    {
                        Extras.loadLongCat();
                        longcatloaded = true;
                    }
                    if(longcatloaded == true && longcat == true)
                    {
                        Extras.displayLongCat(g, mainPanel);
                    }
                }

                // Causes an infinite loop, e.g makes the screen render over and over.
                //repaint();
                update(g);
            }

            @Override public void update(Graphics g)
            {
                System.err.println("Updating screen and using double buffer!");

                // initialize buffer
                if(dbImage == null)
                {
                    dbImage = createImage (this.getSize().width, this.getSize().height);
                    dbg = dbImage.getGraphics ();
                }
                // clear screen in background
                dbg.setColor (getBackground ());
                dbg.fillRect (0, 0, this.getSize().width, this.getSize().height);
                // draw elements in background
                dbg.setColor (getForeground());

                paint(dbg);

                // draw image on the screen
                g.drawImage (dbImage, 0, 0, this);

                try
                {
                    Thread.sleep(200);

                }
                catch (InterruptedException ex)
                {
                    System.err.print("cant delay repaint.");
                }
            }
        };

I was hoping someone could point out where I went wrong, I'm thinking maybe something to do with the update being called too many times, or possible update is the wrong method?

Luke Alderton
  • 3,198
  • 1
  • 23
  • 34

3 Answers3

1

Don't call paint() or update() methods from paintComponent().

Also don't call Thread.sleep() in any painting methods. Instead, create a thread that updates your game model every x milliseconds and then calls repaint() on your custom component where you have overridden paintComponent() so that it draws the game state.

z7sg Ѫ
  • 3,153
  • 1
  • 23
  • 35
1

You're calling paint within the component's paintComponent, which will cause the component to keep repainting itself. This will cause a StackOverflowException. Also, the API admonishes a developer about explicitly invoking paint in an application:

Invoked by Swing to draw components. Applications should not invoke paint directly, but should instead use the repaint method to schedule the component for redrawing.

This method actually delegates the work of painting to three protected methods: paintComponent, paintBorder, and paintChildren. They're called in the order listed to ensure that children appear on top of component itself. Generally speaking, the component and its children should not paint in the insets area allocated to the border. Subclasses can just override this method, as always. A subclass that just wants to specialize the UI (look and feel) delegate's paint method should just override paintComponent.

mre
  • 43,520
  • 33
  • 120
  • 170
  • I need this, as the basis of my whole game relies on its continual refreshing. :/ – Luke Alderton Mar 19 '11 at 03:17
  • 1
    @Luke: One way to go about it is to check the variables `gameNum` and `menuEnabled` whenever their values are altered (or periodically through use of a timer or executor service). If they meet a certain condition, set a `global` flag and subsequently call `repaint` (and possibly `removeAll` prior). And then, check that flag in `paintComponent` and perform the appropriate drawing. – mre Mar 19 '11 at 03:26
  • Thanks, I will take that into consideration in the future. – Luke Alderton Mar 19 '11 at 03:40
0

You need to call g.dispose() every frame after you are done with it, otherwise it will never be released from memory and you get the stack overflow error you see. http://download.oracle.com/javase/1.3/docs/api/java/awt/Graphics.html

Bleaourgh
  • 29
  • 1
  • after adding this at the bottom of my update method i still recieve the stack overflow. Help. – Luke Alderton Mar 19 '11 at 03:07
  • This is required "only if it was created directly from a component or another `Graphics` object." Also, it might help to cite a more recent API. – trashgod Mar 19 '11 at 03:24
  • API link was the first Google result, pretty sure it still applies. Not sure about the specifics of when you need to dispose, but if you're doing double buffering in Java, you're doing active rendering (not passive rendering), which requires that you do a manual `dispose()` call after each frame rendered. – Bleaourgh Mar 19 '11 at 03:27
  • Either link makes it [clear](http://download.oracle.com/javase/6/docs/api/java/awt/Graphics.html) that your suggestion is wrong in this context. – trashgod Mar 19 '11 at 03:38