3

我正在用 Java 编写俄罗斯方块克隆,一切似乎都可以正常工作,直到我想要清除一整行并删除上面的所有内容。尽管我的所有数据都正确地表示了转换,但我的 paintComponent 方法似乎只清除了该行,但上面显示的所有内容都与 repaint() 调用之前一样。新碎片将穿过幻影块并落在底行的隐形块上,上面的碎片会掉下来。

这是我的油漆组件方法:

public void paintComponent(Graphics page)
{
    super.paintComponent(page);
    Graphics2D page2D = (Graphics2D) page;
    for (int c = 0; c < 10; c++) 
    {
        for (int r = 0; r < 18; r++)
        {
            if (well[c][r] != null) //Well is a 2D array of Block objects that have Rectangle object, coordinates and color
            {
                page2D.setColor(well[c][r].getColor());
                page2D.fill(well[c][r].getSquare());
                page2D.setColor(Color.gray);
                page2D.draw(well[c][r].getSquare());
            }       

        }
    }
    for (int i = 0; i < 4; i++) //tetro = the player's tetris piece
    {
        page2D.setColor(tetro.getColor());
        page2D.fill(tetro.getBlock(i).getSquare());
        page2D.setColor(Color.GRAY);
        page2D.draw(tetro.getBlock(i).getSquare());
    }

}

这是我的 Timer 侦听器中的 actionPerformed 方法的一部分,它检测/清除块并调用重绘方法。

        int count = 0;  //Number of occupied cells in well
        int clears = 0; //number of rows to be clear
        int lowestClear = -1; //Lowest row that was cleared, -1 if none
        for (int row = 0; row < 18; row++)
        {
            for (int col = 0; col < 10; col++)
            {
                if (well[col][row] != null)
                {
                    count++;
                }
            }
            if (count == 10)
            {
                clears++;
                if (lowestClear < 0)
                {
                    lowestClear = row;
                }
                for (int col = 0; col < 10; col++)
                {
                    well[col][row] = null;
                }
            }
            count = 0;
        }
        if (clears > 0)
        {
            repaint(); //Doesn't call paintComponent()
            for (int i = 1; i <= clears; i++)
            {
                for (int r = 16; r >= 0; r--)
                {
                    if (r > lowestClear)
                    {
                        break;
                    }
                    for (int c = 0; c < 10; c++)
                    {
                        if (well[c][r] != null)
                        {
                            well[c][r+1] = well[c][r];
                            well[c][r] = null;
                        }           
                    }
                }
            }
            repaint(); //Does not call paintComponent()
        }       
        tetro.fall();
        repaint(); //DOES call paint component

在调用第一个 repaint() 方法时,井数组正确显示整行现在完全为空。我希望 repaint() 方法更新面板以显示此空行,但似乎没有调用 paintComponent()。这也是第二个 repaint() 方法的情况,我希望它更新框架以在清除一行并将它们放下后将块显示在新位置。同样,paintComponent() 没有被调用。然而,对于最后一次 repaint() 调用,我只想更新下落块的位置,而不管它之前可能需要或不需要进行的任何更新,repaint() 确实调用了 paintComponent()。所以:第一个问题是,为什么 paintComponent() 只在 repaint() 调用的这个实例中被调用。

但是,当调用paintComponent() 并到达该方法的末尾时,我会在调试模式下跟踪它以查看面板在哪一行反映了更改。一旦到达 :"Repaintmanager.paintDirtyRegions(Map< Component,Rectangle >)" line:856,它已经清除了行并显示了新的落块,但是有不可见的块和幻像块。

所以,我的第二个问题是,为什么 paintComponent() 会以这种方式表现。显然,我需要对 Repaintmanager 和 Java 绘画进行大量阅读,但如果有人可以向我解释这一点,我将不胜感激。

如果重要,这是主要方法:

import javax.swing.JFrame;

public class TetrisDriver 
{


public static void main(String[] args) 
{
    JFrame frame = new JFrame("Tetris");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    frame.getContentPane().add(new Matrix()); //Matrix = jPanel class
    frame.pack();
    frame.setVisible(true);
}

}

如果这太长了,我很抱歉。

4

3 回答 3

1

首先,如果您还没有这样做,我会阅读AWT 和 Swing中的绘画,其中解释了 Swing(和 AWT)使用的绘画方案。

其次,您不控制重绘,重绘管理器和操作系统可以控制,您只需提供建议。

第三,您可以查看JComponent.paintImmediately方法,它需要知道您要更新的区域,但可能会有所帮助

来自 Java 文档

立即绘制此组件中的指定区域及其与该区域重叠的所有后代。

很少需要调用此方法。在大多数情况下,调用 repaint 会更有效,这会延迟实际的绘制,并且可以将多余的请求合并到单个绘制调用中。如果在调度当前事件时需要更新显示,则此方法很有用。

我也可能更谨慎地将游戏状态渲染到屏幕外缓冲区并在paintComponent方法中使用它。您可以将它们放在一个队列中并在绘制过程中将它们弹出,允许或多或少地按需创建(保持几个池并不断增长,根据需要缩小池)......

于 2012-10-07T23:51:40.183 回答
-1

repaint()不要打电话paintComponent(),这是repaint()方法的正确工作场景。

它只标记了组件应该被重新绘制的状态,并且由 Swing 本身进一步调用 paintComponent()。

许多repaint()调用只能导致单个paintComponent()调用,这是有效的行为。

于 2013-10-14T12:57:10.317 回答
-3

repaint()您可以尝试直接调用paint()自己,而不是使用。

Graphics g;
g = getGraphics();
paint(g);

这样,只要您想进行更改,它就会立即绘制。将其分配给另一个函数,并在您想立即查看更改时调用它。

于 2012-10-08T02:26:32.393 回答