質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.23%

Javaでコンポーネントを裏画面に描画したい

受付中

回答 1

投稿 編集

flag 質問者が3日前に「まだ回答を求めています」と言っています。

  • 評価
  • クリップ 0
  • VIEW 277

NAOPI-05

score 98

Javaでゲームプログラミングをしており、JPanelに画像や図形を描画することでゲームの内容を表示しています。
あるシーンにおいて数字の入力を受け付けるために、そのパネルにJTextFieldのコンポーネントを貼り付けました。
しかし、ダブルバッファリングを用いて再描画を行なっているので、その際の画面消去のせいか入力欄が表示されません。
貼り付けた位置をクリックして文字を入力すると、イベントの発火によってコンポーネントそのものが再描画されるためか、それ以降は入力欄がちらついて表示されるようになります。おそらく画面とコンポーネントがバラバラに再描画されているためだと思います。

そこで、コンポーネントの表示を画面の再描画と合わせて行えるようにしたいです。
今のところ、裏画面にコンポーネントを描画する方法を考えています。そうすれば、再描画でコンポーネントも同時に表示されると思います。
実際のプログラムは長すぎるので、サンプルを記載します。

public class Game extends JPanel implements Runnable {

    public static final int WIDTH = 500;     // 画面の幅
    public static final int HEIGHT = 500;    // 画面の高さ

    private JTextField num = new JTextField("入力欄");

    // メインメソッドから呼び出されるコンストラクタ
    public Game() {

        setPreferredSize(new Dimension(WIDTH, HEIGHT));
        setFocusable(true);
        setLayout(null);

        // 入力欄の位置とサイズを設定
        num.setBounds(250, 250, 100, 50);

        // パネルに入力欄を貼り付け
        add(num);

        // ゲームループを起動
        new Thread(this).start();
    }

    // ゲームループ
    public void run() {

        while (true) {

            try {

                // 入力
                input();

                // 更新
                update();

                // 描画
                draw();

                Thread.sleep(20);

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void input() {
        // 省略
    }

    public void update() {
        // 省略
    }

    public void draw() {

        if (buffer == null) {

            // 裏画面を作成
            buffer = createImage(WIDTH, HEIGHT);

            if (buffer == null)
                return;
            else
                context = buffer.getGraphics(); // 裏画面のグラフィックコンテキストを取得
        }

        // 画面消去
        context.setColor(Color.WHITE);
        context.fillRect(0, 0, WIDTH, HEIGHT);

        // 裏画面に描画 ----------
        player.draw(context);
        ball.draw(context);
        // ---------------------

        try {

            // 表画面のグラフィックコンテキストを取得
            Graphics g = getGraphics();

            // 裏画面を表画面に転送
            if (g != null && buffer != null)
                g.drawImage(buffer, 0, 0, null);

            Toolkit.getDefaultToolkit().sync();

            if (g != null)
                g.dispose();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

なお、裏画面に描画を行う箇所で以下のコードを挿入すると、ちらついて表示される入力欄とは別に画面の左上の隅に入力欄が表示されます。
ちらつく入力欄の内容と常に一致しており、こちらはダブルバッファリングが効いているためか綺麗に表示されます。
裏画面の原点から描画されているようなので、できればこの表示の位置を調整したいです。

num.print(context);


なお、コンポーネントを裏画面に描画する以外に良い方法があるなら是非教えていただきたく思います。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

+1

Swingでは標準でダブルバッファリングで描画しています。
paintComponentrepaintを使用してください。

差を見るためにごちゃごちゃやっていますが、デフォルトはtrueなので特に何もする必要はありません。

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

public class Main extends JFrame implements ItemListener {
    public static void main(String[] args) {
        new Main();
    }

    private Game game;

    private Main() {
        game = new Game();
        game.setPreferredSize(new Dimension(200, 150));
        add(game);
        ((Checkbox) add(new Checkbox("ダブルバッファリング", true))).addItemListener(this);

        setLayout(new FlowLayout());
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(300, 300);
        setVisible(true);
    }

    public void itemStateChanged(ItemEvent e) {
        Checkbox ch = (Checkbox) e.getItemSelectable();
        getRootPane().setDoubleBuffered(ch.getState());

        var root = getRootPane();
        root.setDoubleBuffered(ch.getState());
        var content = ((JComponent) getContentPane());
        content.setDoubleBuffered(ch.getState());
        game.setDoubleBuffered(ch.getState());
    }

    class Game extends JPanel implements Runnable {

        private int x = 0;

        Game() {
            setLayout(null);
            setBackground(Color.gray);
            JTextField num = new JTextField("入力欄");
            num.setBounds(50, 50, 100, 50);

            add(num);
            new Thread(this).start();
        }

        public void run() {
            while (true) {
                repaint();
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);

            x++;
            if (getSize().width - 20 < x) {
                x = 0;
            }
            g.setColor(Color.blue);
            g.fillRect(10, 10, x, 20);
        }
    }
}

参考にしたサイト
Double Buffer を調べる

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/12/15 16:06

    paintChildrenはよくないようです。
    入力中たまにpaintChildrenから返ってこずゲームループが止まります。

    キャンセル

  • 2019/12/15 19:05 編集

    文字を打ったり消したりすると枠がちらつきます。
    実際のコードはサンプルとはだいぶ異なりますから再現は難しいかと思います。
    BufferStrategyのことは知らなかったので良く分かりません。

    paintChildrenですが、確かにたまに止まることがありますね。
    私も色々調べておりますが、また何か発見がございましたらご教授いただけると幸いです。
    お手間を取らせてしまい申し訳ありませんでした。

    キャンセル

  • 2019/12/15 19:13

    雑ですがpaintをずらしたら止まることはなくなった気がします。
    context.translate(num.getX(), num.getY());
    num.print(context);
    context.translate(-num.getX(), -num.getY());

    なかなか難しいですねぇ。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.23%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる