🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Swing

SwingはJavaに標準で付属するグラフィック関連のクラスライブラリを指します。

Q&A

1回答

1972閲覧

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

NAOPI-05

総合スコア132

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Swing

SwingはJavaに標準で付属するグラフィック関連のクラスライブラリを指します。

0グッド

0クリップ

投稿2019/12/14 12:07

編集2020/04/05 16:23

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

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

Java

1public class Game extends JPanel implements Runnable { 2 3 public static final int WIDTH = 500; // 画面の幅 4 public static final int HEIGHT = 500; // 画面の高さ 5 6 private JTextField num = new JTextField("入力欄"); 7 8 // メインメソッドから呼び出されるコンストラクタ 9 public Game() { 10 11 setPreferredSize(new Dimension(WIDTH, HEIGHT)); 12 setFocusable(true); 13 setLayout(null); 14 15 // 入力欄の位置とサイズを設定 16 num.setBounds(250, 250, 100, 50); 17 18 // パネルに入力欄を貼り付け 19 add(num); 20 21 // ゲームループを起動 22 new Thread(this).start(); 23 } 24 25 // ゲームループ 26 public void run() { 27 28 while (true) { 29 30 try { 31 32 // 入力 33 input(); 34 35 // 更新 36 update(); 37 38 // 描画 39 draw(); 40 41 Thread.sleep(20); 42 43 } catch (InterruptedException e) { 44 e.printStackTrace(); 45 } 46 } 47 } 48 49 public void input() { 50 // 省略 51 } 52 53 public void update() { 54 // 省略 55 } 56 57 public void draw() { 58 59 if (buffer == null) { 60 61 // 裏画面を作成 62 buffer = createImage(WIDTH, HEIGHT); 63 64 if (buffer == null) 65 return; 66 else 67 context = buffer.getGraphics(); // 裏画面のグラフィックコンテキストを取得 68 } 69 70 // 画面消去 71 context.setColor(Color.WHITE); 72 context.fillRect(0, 0, WIDTH, HEIGHT); 73 74 // 裏画面に描画 ---------- 75 player.draw(context); 76 ball.draw(context); 77 // --------------------- 78 79 try { 80 81 // 表画面のグラフィックコンテキストを取得 82 Graphics g = getGraphics(); 83 84 // 裏画面を表画面に転送 85 if (g != null && buffer != null) 86 g.drawImage(buffer, 0, 0, null); 87 88 Toolkit.getDefaultToolkit().sync(); 89 90 if (g != null) 91 g.dispose(); 92 93 } catch (Exception e) { 94 e.printStackTrace(); 95 } 96 } 97}

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

Java

1num.print(context);

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

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

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

Java

1import java.awt.*; 2import javax.swing.*; 3import java.awt.event.*; 4 5public class Main extends JFrame implements ItemListener { 6 public static void main(String[] args) { 7 new Main(); 8 } 9 10 private Game game; 11 12 private Main() { 13 game = new Game(); 14 game.setPreferredSize(new Dimension(200, 150)); 15 add(game); 16 ((Checkbox) add(new Checkbox("ダブルバッファリング", true))).addItemListener(this); 17 18 setLayout(new FlowLayout()); 19 setLocationRelativeTo(null); 20 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 21 setSize(300, 300); 22 setVisible(true); 23 } 24 25 public void itemStateChanged(ItemEvent e) { 26 Checkbox ch = (Checkbox) e.getItemSelectable(); 27 getRootPane().setDoubleBuffered(ch.getState()); 28 29 var root = getRootPane(); 30 root.setDoubleBuffered(ch.getState()); 31 var content = ((JComponent) getContentPane()); 32 content.setDoubleBuffered(ch.getState()); 33 game.setDoubleBuffered(ch.getState()); 34 } 35 36 class Game extends JPanel implements Runnable { 37 38 private int x = 0; 39 40 Game() { 41 setLayout(null); 42 setBackground(Color.gray); 43 JTextField num = new JTextField("入力欄"); 44 num.setBounds(50, 50, 100, 50); 45 46 add(num); 47 new Thread(this).start(); 48 } 49 50 public void run() { 51 while (true) { 52 repaint(); 53 try { 54 Thread.sleep(10); 55 } catch (InterruptedException e) { 56 e.printStackTrace(); 57 } 58 } 59 } 60 61 @Override 62 public void paintComponent(Graphics g) { 63 super.paintComponent(g); 64 65 x++; 66 if (getSize().width - 20 < x) { 67 x = 0; 68 } 69 g.setColor(Color.blue); 70 g.fillRect(10, 10, x, 20); 71 } 72 } 73}

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

投稿2019/12/14 16:04

TN8001

総合スコア9855

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

NAOPI-05

2019/12/15 01:09 編集

ご回答ありがとうございます。 確かにSwingでは標準でダブルバッファリングを行なっているそうですが、repaintメソッドはあくまでも再描画の要求であって実際のタイミングはJVMが判断しているのではないでしょうか? 記載いただいたコードを拝見しますと、アクティブレンダリングを行なっていないようですが、ゲームプログラミングにおいては基本的に外せないと思っております。正確なFPSを実現したい場合もあるからです。
TN8001

2019/12/15 03:10

なるほどそういうテクニックがあるんですね。知りませんでした。 > num.print(context); に関しては paintChildren(context); とすることで正しい位置に描画されました。 しかし窓をリサイズすると自前描画部分がちらつきました。 再描画が干渉しているのでしょうが、止め方がまだわかりません^^; もうちょっと調べてみます。
NAOPI-05

2019/12/15 04:29 編集

ご協力に感謝いたします。 paintChildrenメソッドを呼び出すと正しい位置に表示されましたが、入力を行うたびにちらつくので、後ろでコンポーネントが独自のタイミングで再描画されているのかもしれません。 ちなみに、画面のサイズは固定にしているのでリサイズに関しては気にしなくても大丈夫です。 とりあえず、コンポーネント単体の描画を制御できれば解決となると思います。
TN8001

2019/12/15 05:59 編集

当方では入力時のちらつきが再現できません。 1文字打つ(消す)たびに発生する感じですか?? num.setIgnoreRepaint(true); とか JTextFieldを継承してpaint関連のメソッドを@Overrideするとかでしょうか。。。 BufferStrategy方式でも同様なんでしょうか。
TN8001

2019/12/15 07:06

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

2019/12/15 10:06 編集

文字を打ったり消したりすると枠がちらつきます。 実際のコードはサンプルとはだいぶ異なりますから再現は難しいかと思います。 BufferStrategyのことは知らなかったので良く分かりません。 paintChildrenですが、確かにたまに止まることがありますね。 私も色々調べておりますが、また何か発見がございましたらご教授いただけると幸いです。 お手間を取らせてしまい申し訳ありませんでした。
TN8001

2019/12/15 10:13

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問