3

私は Java で Tetris クローンに取り組んでいますが、行全体をクリアして上記のすべてを削除するまで、すべてが適切に機能しているようです。私のデータはすべて変換を適切に表していますが、私の 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());
    }

}

これは、タイマー リスナーの actionPerformed メソッドの一部で、ブロックを検出/クリアし、repaint メソッドを呼び出します。

        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() メソッドが呼び出されるまでに、well 配列は行全体が完全に null であることを適切に示します。repaint() メソッドでパネルを更新してこの空の行を表示したいのですが、paintComponent() が呼び出されていないようです。これは 2 番目の repaint() メソッドにも当てはまります。行をクリアしてドロップダウンした後、ブロックを新しい位置に表示するようにフレームを更新したいと考えています。繰り返しますが、paintComponent() は呼び出されません。ただし、前回の repaint() 呼び出しでは、落下するピースの位置を更新するだけで、以前に行う必要があったかどうかに関係なく、repaint() は paintComponent() を呼び出します。質問 1 は、なぜ paintComponent() が repaint() 呼び出しのこのインスタンスでのみ呼び出されるのかということです。

ただし、paintComponent() が呼び出されてメソッドの最後に到達すると、デバッグ モードで追跡して、パネルが変更を反映する行を確認します。:"Repaintmanager.paintDirtyRegions(Map< Component,Rectangle >)" 行:856 に到達すると、行がクリアされ、新しい落下ピースが表示されますが、見えないブロックとファントム ブロックがあります。

2 つ目の質問は、なぜ 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でのPaintingを読んで、Swing(およびAWT)で使用されるペイントスキームについて説明します。

次に、再描画を制御するのではなく、再描画マネージャーとOSが制御し、提案を提供するだけです。

第三に、 JComponent.paintImmediatelyメソッドを見ることができます。更新する領域を知っている必要がありますが、役立つ場合があります。

Javaドキュメントから

このコンポーネントの指定された領域と、その領域と重なるすべての子孫をすぐにペイントします。

このメソッドを呼び出す必要はめったにありません。ほとんどの場合、repaintを呼び出す方が効率的です。これにより、実際のペイントが延期され、冗長なリクエストが1つのペイント呼び出しにまとめられる可能性があります。このメソッドは、現在のイベントがディスパッチされている間に表示を更新する必要がある場合に役立ちます。

また、ゲームの状態をオフスクリーンバッファーにレンダリングし、これをメソッドで使用する方が賢明かもしれませんpaintComponent。これらをキューに入れて、ペイントプロセス中にポップオフして、オンデマンドで多かれ少なかれ作成できるようにすることができます(少数のプールを維持し、必要に応じてプールを拡大、縮小します)...

于 2012-10-07T23:51:40.183 に答える
-1

repaint()呼び出さないpaintComponent()でください。これはメソッドの正しい動作シーンですrepaint()

コンポーネントを再描画する必要がある状態をマークするだけで、Swing 自体によってさらに paintComponent() が呼び出されます。

多くのrepaint()呼び出しは単一の呼び出しのみを引き起こす可能性がありpaintComponent()、これは有効な動作です。

于 2013-10-14T12:57:10.317 に答える