3

ダブルバッファリングを使い始めたばかりで、JScrollPaneを画面に追加して後でカメラを動かすことができるようになるまで、すべてが正常に機能しました。JScrollPaneのScrollBarを除いて、すべてが正常に表示されます(私のスプライト)。そして、私はそれらを見せて欲しいです!

ただし、ウィンドウのサイズを変更すると、スクロールバーがちらつくので、そこにあることがわかります。十分に速ければ、それらを使用することもできます。なぜ彼らはレンダリングに現れないのですか?:(

問題のSSCCEは次のとおりです。

public class BufferStrategyDemo extends JFrame
{
    private BufferStrategy bufferStrategy;
    private JPanel gameField;
    private JScrollPane scroll;

    public BufferStrategyDemo()
    {

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        
        this.getContentPane().setPreferredSize(new Dimension(800, 600));
        this.pack();
        this.createBufferStrategy(2);

        this.gameField = new JPanel();
        this.gameField.setPreferredSize(new Dimension( 1400, 600 ));

        this.scroll = new JScrollPane( gameField );
        this.add( scroll, BorderLayout.CENTER );

        this.setVisible( true );
        this.bufferStrategy = this.getBufferStrategy();

        setupGameFieldContent();

        new Renderer( this, scroll , bufferStrategy ).start();
    }

    // Add some contents to gameField that shows up fine
    private void setupGameFieldContent()
    {
        // For ( all my sprites )
        //      gameField.add( sprite );

    }

    private class Renderer extends Thread
    {
        private BufferStrategy bufferStrategy;
        private JFrame window;
        private JScrollPane scroll;
        private Image imageOfGameField;

        public Renderer( JFrame window, JScrollPane scroll, BufferStrategy bufferStrategy )
        {
            this.bufferStrategy = bufferStrategy;
            this.window = window;
            this.scroll = scroll;
        }

        public void run()
        {
            while ( true )
            {
                Graphics g = null;
                try 
                {
                    g = bufferStrategy.getDrawGraphics();
                    drawSprites(g);
                } 
                finally { g.dispose(); }

                bufferStrategy.show(); // Shows everything except the scrollbars..
                Toolkit.getDefaultToolkit().sync(); 

                try { Thread.sleep( 1000/60 ) ; } 
                catch(InterruptedException ie) {}
            }
        }

        private void drawSprites( Graphics g )
        {
            Graphics2D g2d=(Graphics2D)g;

            //Also tried using the JPanels (gameField) createImage with same result
            imageOfGameField = this.scroll.createImage(800, 600 ); 
            Graphics screen = (Graphics2D)imageOfGameField.getGraphics();

            /**
             * For all my sprites, draw on the image
            for ( Sprite current : sprites )
                screen.drawImage( current.getImage(), current.getX(), current.getY(), current.getWidth() , current.getHeight(), null);
            */

            g2d.drawImage( imageOfGameField, 0, 0 , null );

        }
    }
}
4

3 に答える 3

5

ではなく、フレーム全体に描画しgameFieldます。実際、一時的に表示される唯一の原因は、マウス ドラッグ ハンドラのどこかでJScrollPane呼び出されることです。paintImmediately

最初に頭に浮かぶのは、I_Love_Thinking が書いたように に置き換え、そのキャンバス インスタンスから取得JPanelしてレンダリングに使用することです。しかし、残念ながら期待どおりには機能しません。ライトウェイトは、ヘビーウェイトを適切に駆動できません。Canvas は、領域外 (0、0、viewport_width、viewport_height) のコンテンツの描画を拒否します。これは既知のバグであり、修正されません。重量のあるコンポーネントと軽量のコンポーネントを混在させるのは悪い考えです。CanvasbufferStategyJScrollPaneCanvas

JScrollPaneヘビーウェイトに交換するとうまくいくScrollPaneようです。ほとんど。状況によっては、スクロール バーに顕著なレンダリング アーティファクトがあります。

この時点で、私は壁を打ち破ることをやめ、あきらめることにしました。スクロール ペインは多くの問題の原因であり、レイジー スクロールに使用する価値はありません。スクロールを別の視点から見る時が来ました。はゲームフィールドそのものでCanvas はなく、ゲーム世界を観察するための窓です。これは、gamedev でよく知られているレンダリング戦略です。キャンバスのサイズは、観測可能な領域とまったく同じです。スクロールが必要な場合は、オフセットを付けて世界をレンダリングするだけです。オフセットの値は、外部スクロールバーから取得できます。

次の変更を提案します。

  1. 捨てるJScrollPane
  2. キャンバスをフレームに直接追加します。
  3. JScrollBarキャンバスの下に単一の水平を追加します。
  4. スクロールバーの値を使用してレンダリングをシフトします。

この例は、コードに基づいています。この実装は、フレームのサイズ変更を考慮しません。実際には、いくつかの場所をビューポートのサイズと同期する必要があります (これは、スクロールペインが以前に行っていたことです)。また、例は Swing のスレッド化ルールに違反し、レンダリング スレッドからスクロールバーの値を読み取ります。

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.JScrollBar;

public class BufferStrategyDemo extends JFrame {
    private BufferStrategy bufferStrategy;
    private Canvas gameField;
    private JScrollBar scroll;

    public BufferStrategyDemo() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        getContentPane().setLayout(new BorderLayout());
        getContentPane().setPreferredSize(new Dimension(800, 600));

        gameField = new Canvas();
        gameField.setIgnoreRepaint(true);
        gameField.setPreferredSize(new Dimension(800, 580));
        getContentPane().add(gameField, BorderLayout.CENTER);

        scroll = new JScrollBar(JScrollBar.HORIZONTAL);
        scroll.setPreferredSize(new Dimension(800, 20));
        scroll.setMaximum(1400 - 800); // image width - viewport width
        getContentPane().add(scroll, BorderLayout.SOUTH);

        this.pack();

        gameField.createBufferStrategy(2);
        bufferStrategy = gameField.getBufferStrategy();

        new Renderer().start();
    }

    private class Renderer extends Thread {
        private BufferedImage imageOfGameField;

        public Renderer() {
            // NOTE: image size is fixed now, but better to bind image size to the size of viewport
            imageOfGameField = new BufferedImage(1400, 580, BufferedImage.TYPE_INT_ARGB);
        }

        public void run() {
            while (true) {
                Graphics g = null;
                try {
                    g = bufferStrategy.getDrawGraphics();
                    drawSprites(g);
                } finally {
                    g.dispose();
                }

                bufferStrategy.show(); 
                Toolkit.getDefaultToolkit().sync();

                try {
                    Thread.sleep(1000 / 60);
                } catch (InterruptedException ie) {
                }
            }
        }

        private void drawSprites(Graphics g) {
            Graphics2D g2d = (Graphics2D) g;

            Graphics g2d2 = imageOfGameField.createGraphics();
            g2d2.setColor(Color.YELLOW); // clear background
            g2d2.fillRect(0, 0, 1400, 580); // again, fixed width/height only for SSCCE
            g2d2.setColor(Color.BLACK);

            int shift = -scroll.getValue(); // here it is - get shift value

            g2d2.fillRect(100 + shift, 100, 20, 20); // i am ugly black sprite
            g2d2.fillRect(900 + shift, 100, 20, 20); // i am other black sprite
                                                     // located outside of default view

            g2d.drawImage(imageOfGameField, 0, 0, null);

            g2d2.dispose();
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                BufferStrategyDemo mf = new BufferStrategyDemo();
                mf.setVisible(true);
            }
        });
    }
}

もう 1 つの例は、主にJava Games: Active Renderingの記事のコードに基づいています。JScrollBarの値とサイズに適切に同期しましCanvasた。また、 (中間オブジェクトではなく)インスタンスGraphicsから取得した に直接描画します。結果は次のようになります。BufferStrategyBuffereImage

アクティブ レンダリングのデモ

于 2012-03-04T16:04:20.890 に答える
1

ゲームがスクロール ペインをペイントします。ウィンドウ
のサイズを変更すると、スクロール ペインが一瞬表示されます。これは、通常のペイント メソッドが呼び出されるためです (ダブル バッファリングはありません)
。そして、あなたがそれで何をしたいのか正確にはわかりません...

jframe 内のすべてのコンポーネントを自分で制御したい場合:

  • paint(Graphics g) メソッドをオーバーライドし、空のままにします
  • ペイントループにスクロールペイン描画を含めます(メソッド「実行」)

別のコンポーネントでダブル バッファリングを使用する場合は、ダブル バッファリングを使用して他のコンポーネントを直接管理する必要はありません。
これに最適なコンポーネントは Canvas です。使用できるダブルバッファリングのメソッドが既にあります。
ずいぶん前に作ったものなので、その仕組みを正確にお伝えすることはできません。しかし、それを見てください、それは難しいことではありません。

于 2012-03-04T13:22:42.090 に答える
1

あなたのコードは、バックグラウンド スレッドで Swing オブジェクトを操作しています。これは機能しません。

The Event Dispatch Thread and Concurrency in Swingを読んでください。

于 2012-03-04T13:23:51.230 に答える