1

ということで、グラフィックカードゲームを作っています。各カードは、ボタンとそれに関連付けられた 2 つの画像を持つ JPanel です。これは、カードがクリックされたときにアクション リスナーで最初に呼び出すメソッドです。

public void flip()
{
    if(b1.getIcon() == card2) b1.setIcon(card1);
    else b1.setIcon(card2);
    revalidate();
    repaint();
}

ただし、何らかの理由で、このメソッドを呼び出した後、ある時点までカードが反転しません (アイコンが変化しないことを意味します)。たとえば、flip を呼び出した後に Thread.sleep を配置すると、flip の完了後にプログラムが一時停止すると想定しますが、そうではありません。画像がまだ切り替えられていない状態でスリープし、スリープ時間が終了した後の時点でのみ画像を切り替えます。

AI コードを実行する前に flip() を呼び出しているにもかかわらず、カードが画面上で反転する前に AI イベントが発生するため、このカード ゲームで人間に AI をプレイさせると、いくつかの大きな問題が発生します。ここで何が起こっているのか、誰かが私に手がかりを与えることができますか?

4

2 に答える 2

4

再描画は、再描画の呼び出しではなく、再描画するための Swing への要求です。カバーの下で repaint() は、再描画ジョブをイベントのキューに投稿しています。その再描画ジョブに加えて、マウスの移動、マウスのクリック、キーボードのアクティビティなどもそこに投稿されます。Swing スレッドがやって来て、定期的にそのキューからジョブを実行し、UI を再描画したり、マウスやキーボードのイベントをコンポーネントに配信したりします。実際、swing スレッドはこれらのイベントを頻繁に配信しますが、UI が実行している作業量に依存します。そのSwingスレッドで。マウス クリック、キーボード イベント、および再描画を配信しているときは、そのキューから読み取ることができません。そのため、コードがイベントに応答するのに長い時間がかかる場合、swing が新しいイベントにタイムリーに応答する能力が低下するか、すべてがブロックされます。

そのため、Swing イベント ディスパッチ スレッドを使用してネットワーク経由でブロッキング サービス コールを実行すると、そのコールが戻るまで UI の描画が停止します。これが、ネットワーク呼び出しなどの長時間実行されるジョブを Swing スレッドから移動し、呼び出しが戻ってきたときに SwingUtilities.invokeLater() を使用して、イベント スレッドで UI を更新できるようにすることが重要である理由です。(invokeLater() は、上記で説明した Swing イベント キューにジョブをポストすることでこれを行います)。

これが、Swing イベント スレッドで sleep を呼び出すのがひどい考えである理由でもあります。そのスレッドをスリープ状態にすると、キューからイベントを処理できなくなります。また、リクエストした repaint() は、Swing スレッドがスリープしている間は実行されません。

まず、スリープ コールを削除します。2番。追加のロジックを実行する前にペイントを終了することに依存するコードを記述しないでください。再描画と再検証は特別な呼び出しであり、Swing は再描画/再検証の要求を組み合わせて、10 回連続して再描画しないようにします (重要な CPU 時間を浪費します)。

Swing は、呼び出し元のスレッドが最初に呼び出したメソッドから離れるまで、パネルを再描画できません。スレッドがそのメソッドを離れるのが早ければ早いほど、 repaint() が早く発生します。

再描画をすぐに実行したい理由を説明していないので、気にしないようにコードを構造化する方法についてアドバイスすることはできません。しかし、再描画がまだ行われていないことを気にする必要はありません。

于 2012-11-20T16:03:30.873 に答える
1

@chubbsondubsに完全に同意します。追加したかったのは、 Flip が呼び出された後に何らかのイベントを発生させることが目標である場合は、swing timersの使用を検討することです。ご承知のとおり、Sleep を呼び出しても目的の効果は得られません。しかし、代わりに、あなたは

  • たとえば1秒のタイマーを作成します
  • タイマーが起動したときに実行するアクションを作成する
  • タイマーを開始する

その後、イベント ディスパッチ スレッドをブロックすることなく、必要な遅延を作成できます。

public void flip()
{
    if(b1.getIcon() == card2) b1.setIcon(card1);
    else b1.setIcon(card2);
    revalidate();
    repaint();

    javax.swing.Timer t = new javax.swing.Timer(1000, new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          // do something interesting
        }
     });
    t.repeats(false);
    t.start();
}
于 2012-11-20T16:24:06.767 に答える