Javaに関する様々な情報をご紹介します。

Javaに関する様々な情報をご紹介します。
評価

0

間隔をあけて移動するようにしたい。

アラスカソリティアというトランプゲームを作っていて、画面(トランプ以外)クリックで列から場に移動できるトランプを自動で移動できるようにしています。
public void mouseClicked(MouseEvent event)の中でやっている移動のときに1枚1枚の間にいくらか間をあけて1枚ずつ移動しているようにしたいのですが、waitを挟んでみてもまとめてwaitされてる感じでそのあとまとめて移動されてしまいます。
どうしたらいいでしょうか?

5

回答

83413

閲覧

5件の回答

評価

0

その間に、「再描画」されるようになってないからでしょ。

評価

30

waitではなくThread#sleepを使います。
Swingのシングルスレッドモデル(EDT = イベントディスパッチスレッド)に
対する理解が必要になりますが、だいたい以下の様なコードになると思います。
0.5秒おきにmoveCard()を呼び出して再描画する処理を10回繰り返します。

    public void mouseClicked(MouseEvent e) {
        final int count = 10;
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < count; i++) {
                    try {
                        SwingUtilities.invokeLater(new Runnable() {
                            public void run() {
                                moveCard();
                            }
                        });
                        Thread.sleep(500);
                    } catch (InterruptedException e) {}
                }
            }
        }).start();
    }
    
    public void moveCard() {
        //カード移動のための処理を実装
        component.repaint(); //componentは描画しているコンポーネント
    }

moveCardメソッド内でrepaint以外にSwingコンポーネントのメソッドを
呼び出していない場合はrunメソッドの部分を
    for (int i = 0; i < count; i++) {
        try {
            moveCard();
             component.repaint(); //componentは描画しているコンポーネント
            Thread.sleep(500);
        } catch (InterruptedException e) {}
    }
として大丈夫です。
「Swing スレッドモデル」などで検索してみてください。
もし分からなかったらmouseClickedメソッドとpaintComponentあるいは
paintメソッドのコードを示してください。

評価

0

やはり新たにスレッドを作ったりする必要があったんですね。
とりあえずカード移動部分をそのまま抜き出してエンクロージング型クラスを作り、waitではなくThread.sleepを使い、下のコードで私の思っているような動作になりました。

トランプは実はpaintによる描画ではなく、クリック判定を楽する目的で画像を入れたJLabelにしています。
CardクラスというのがJLabelを親クラスとした自作クラスになっていて、トランプの数字記憶用変数numとマーク記憶用変数markとトランプをめくるcardTurnメソッドなどがあります。
列や場にあるトランプの並びを記憶させたりするためのAreaクラスがあり、ベースの枠を表示させるためにもJLabelを親クラスとして作っていて、その上に重なっているトランプの並びを記憶するためのVector<Card>のcard変数などがあります。
line配列やarea配列はそのクラスのもので、line配列で各列・area配列で各場にあるトランプを管理しています。

gameClearメソッドでクリア判定をし、true(クリア)なら以降の動作はやるだけ無駄なのでreturnしていましたが、Threadのrunメソッドの場合returnはするべきではなかったりしますか?

//-----------------------------
@Override
public void mouseClicked(MouseEvent event){
    if(!(event.getSource() instanceof Card)){
        new CardMove().start();
    }
}

class CardMove  extends Thread{        
    @Override
    public void run(){
        try {
            while(true){
                int move=0;
                for(int x=0;x<7;x++){
                    if(line[x].card.size()>0){
                        for(int x2=0;x2<4;x2++){
                            if(area[x2].card.size()==0){
                                if(line[x].card.get(0).num==0){
                                    Thread.sleep(500);
                                    getContentPane().setComponentZOrder(line[x].card.get(0), 0);
                                    line[x].card.get(0).setLocation(area[x2].getLocation());
                                    area[x2].card.add(line[x].card.get(0));
                                    line[x].card.remove(0);
                                    move++;
                                    if(gameClear()==true){
                                        return;
                                    }
                                    break;
                                }
                            }
                            else{
                                if((line[x].card.get(0).mark==area[x2].card.get(area[x2].card.size()-1).mark) &&
                                        (line[x].card.get(0).num==area[x2].card.get(area[x2].card.size()-1).num+1)){
                                    Thread.sleep(500);
                                    getContentPane().setComponentZOrder(line[x].card.get(0), 0);
                                    line[x].card.get(0).setLocation(area[x2].getLocation());
                                    area[x2].card.add(line[x].card.get(0));
                                    line[x].card.remove(0);
                                    move++;
                                    if(gameClear()==true){
                                        return;
                                    }
                                    break;
                                }
                            }
                        }
                        if(line[x].card.size()>0 && line[x].card.get(0).turn==0){
                            Thread.sleep(500);
                            line[x].card.get(0).cardTurn();
                        }
                    }
                }
                if(move==0){
                    break;
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

評価

0

サンプルがいい加減でした。
すいません。
最初のtryブロックと最後のcatch部分を削除して
2カ所のsleepを以下のようにコンパクトにしてください。
try {
    Thread.sleep(500);
} catch (InterruptedException e) {}
あと大事なことですがSwingのコンポーネントへのアクセスはEDT上で行う必要が
あるのでrunメソッドの内容はSwingUtilities#invokeLaterを使った形に
書き直さなければ成りません。
「Swing スレッドモデル」などで検索して学習してください。
それとrunメソッドを途中でreturnすること自体には何も問題ありません。

評価

0

途中でエラーが出た時に一連を中止してもらおうかと持って一連をtry-chatchで囲ってました。
途中でreturnすることは特に問題ないようならこのままにします。
EDTとかは知らないのでとりあえず自分で調べてみようと思います。

回答ありがとうございました。

質問から6ヶ月以上経過しているので、回答を書き込むことはできません。