Двойной буфер Java, использующий переопределение для метода обновления, выбрасывает переполнение стека

Я пытаюсь добиться двойной буферизации моей игры на Java, переопределив метод обновления для моего JPanel, я делаю весь обычный код и т. Д., И все же он не работает, он выдает ошибку переполнения стека, ниже приведена конкретная ошибка:

Exception in thread "AWT-EventQueue-0" java.lang.StackruError
        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)

Мой код не особенно сложен:

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.");
                }
            }
        };

Я надеялся, что кто-то может указать, где я ошибся, я думаю, может быть, что-то связано с тем, что обновление вызывается слишком много раз, или, возможно, обновление - неправильный метод?

3 ответа

Решение

Не звони paint() или же update() методы из paintComponent(),

Также не звоните Thread.sleep() в любых методах покраски. Вместо этого создайте поток, который обновляет модель игры каждые x миллисекунд, а затем вызывает repaint() на ваш пользовательский компонент, где вы переопределили paintComponent() так что рисует игровое состояние.

Ты звонишь paint в компоненте paintComponent, что заставит компонент продолжать перекрашивать себя. Это вызовет StackruException, Кроме того, API предупреждает разработчика о явном вызове paint в приложении:

Вызывается Swing для рисования компонентов. Приложения не должны вызывать рисование напрямую, а вместо этого должны использовать метод repaint, чтобы запланировать перерисовку компонента.

Этот метод фактически делегирует работу рисования трем защищенным методам: paintComponent, paintBorder и paintChildren. Они вызываются в указанном порядке, чтобы гарантировать, что дочерние элементы появляются поверх самого компонента. Вообще говоря, компонент и его дочерние элементы не должны рисовать в области вставок, выделенных для границы. Подклассы могут просто переопределить этот метод, как всегда. Подкласс, который просто хочет специализировать метод рисования делегата UI (внешний вид), должен просто переопределить paintComponent.

Вам нужно позвонить g.dispose() каждый кадр после того, как вы закончили с ним, иначе он никогда не будет освобожден из памяти, и вы получите ошибку переполнения стека, которую видите. http://download.oracle.com/javase/1.3/docs/api/java/awt/Graphics.html

Другие вопросы по тегам