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

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

0

Javaアニメーションのちらつきをなくしたい

下記のJavaアニメーションのちらつきをなくしたいのですがどうすればいいでしょうか?教えてください。

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

public class InPen extends JFrame implements Runnable{

    static double t0;
    
    double time = 0.0;
    
    double r, t, rdot, tdot;
    double rh, th, rdoth, tdoth;
    
    public static void main(String[] args){
        
        InPen wnd=new InPen();
        wnd.addWindowListener(new WindowAdapter(){public void windowClosing(WindowEvent e){System.exit(0);}});
        wnd.setVisible(true);
    }
    
    public InPen(){
        setTitle("Inverted Pendulum");    
        setBounds(300,100,800,700);
        
        UIManager.put("OptionPane.okButtonText", "OK");
        UIManager.put("OptionPane.cancelButtonText", "Cancel");
        
        String inputValue;
        inputValue = JOptionPane.showInputDialog(null, "Please input a value of t0 (degree)", "Input t0", JOptionPane.PLAIN_MESSAGE);

        t0 = Double.parseDouble(inputValue);
        t0 = degreetoradian(t0);
        
        Thread thr = null;
        if(thr == null){
            thr = new Thread(this);
            thr.start();
        }
    }
    
    public void run(){
       double r0 = 0.0, rr0 = 0.00, tt0 = 0.00;
        r = r0; t = t0; rdot = rr0; tdot = tt0;
        
        try{
            Thread.sleep(1000);
        }catch(Exception e){}
        
        while(true){
        
        move();
        
        if(time > 5){
        System.exit(0);
        }
        try{
            repaint();
            Thread.sleep(30);
        }catch(Exception e){}
        time = time + 0.001;
      }
    }
    
    public void move(){
    
        double step = 0.0001;
        
        for(double ti = 0; ti < 0.01; ti = ti + step){

       rh   = r    + step * dadt(r, t, rdot, tdot);
       th   = t    + step * dbdt(r, t, rdot, tdot);
       rdoth = rdot + step * dcdt(r, t, rdot, tdot);
       tdoth = tdot + step * dddt(r, t, rdot, tdot);
        
        r = rh; t = th; rdot = rdoth; tdot = tdoth;
        }
    }
    
    public void update(Graphics g){
        paint(g);
    }
    
    public void paint(Graphics g){
        super.paint(g);
        int s = getSize().width/2,a = 1000;
        
         g.drawLine((int)(s+r*a-20)-100,375,(int)(s+r*a+20)+100,375); //台車上辺
        g.drawLine((int)(s+r*a+20)+100,375,(int)(s+r*a+20)+100,425); //台車右辺
        g.drawLine((int)(s+r*a-20)-100,425,(int)(s+r*a+20)+100,425); //台車下辺
        g.drawLine((int)(s+r*a-20)-100,375,(int)(s+r*a-20)-100,425); //台車左辺
        g.drawLine(0,450,s*2,450);
        g.drawLine(s,450,s,455);
        g.fillOval((int)(s+r*a-15)-65,400,50,50);
        g.fillOval((int)(s+r*a+5)+25,400,50,50);
        g.drawLine((int)(s+r*a),375,(int)(s+r*a+105.0f*Math.sin(t)),(int)(375-110.0f*Math.cos(t))); //振子の棒
        g.fillOval((int)(s+r*a+110.0f*Math.sin(t))-5,(int)(370-115.0f*Math.cos(t)),10,10); //振子の球
    }
    
    
    double dadt(double r, double t, double rdot, double tdot){
       double m = 0.1, M = 5.01, l = 0.115, R = 24.5;
        double drdot, dtdot;
        drdot = dcdt(r, t, rdot, tdot);
        dtdot = dddt(r, t, rdot, tdot);
    
        return (1 / R) * (m * l * Math.sin(t) * tdot * tdot - (M + m) * drdot - m * l * Math.cos(t) * dtdot);
    }
    
    double dbdt(double r, double t, double rdot, double tdot){
       double m = 0.1, J = 0.00214, l = 0.115, g = 9.8, c = 0.000598;
        double drdot, dtdot;
        drdot = dcdt(r, t, rdot, tdot);
        dtdot = dddt(r, t, rdot, tdot);
    
        return (1 / c) * (-m * l * Math.cos(t) * drdot - (J + m * l * l) * dtdot + m * l * g * Math.sin(t));
    }
    
    double dcdt(double r, double t, double rdot, double tdot){
        double u = 0.0, m = 0.1, M = 5.01, J = 0.00214, l = 0.115;
        double g = 9.8, R = 24.5, c = 0.000598, a = 30.9;
        return ((J + m * l * l) *(-R * rdot + m * l * Math.sin(t) * tdot * tdot + a * u) + (-m * l * Math.cos(t)) * (-c * tdot + m * g * l * Math.sin(t))) / ((M + m) * (J + m * l * l) - m * m * l * l * Math.cos(t) * Math.cos(t));
    }
    
    double dddt(double r, double t, double rdot, double tdot){
        double u = 0.0, m = 0.1, M = 5.01, J = 0.00214, l = 0.115;
        double g = 9.8, R = 24.5, c = 0.000598, a = 30.9;
        return ((-m * l * Math.cos(t)) * (-R * rdot + m * l * Math.sin(t) * tdot * tdot + a * u) + (M + m) * (-c * tdot + m* g * l * Math.sin(t))) / ((M + m) * (J + m * l * l) - m * m * l * l * Math.cos(t) * Math.cos(t));
    }
    
    double degreetoradian(double degree){
          return degree / 180.0 * Math.PI;
        }
}

18

回答

84083

閲覧

18件の回答

評価

0

このあたり、参考になりませんか?
http://www.geocities.jp/ntaka329/java/faq/ques_anim.html
http://hp.vector.co.jp/authors/VA012735/java/dbuf1.htm
http://www.mm2d.net/applet-howto/howto-07.shtml

これほどのコードが書けるのであれば、上記サイトで十分対応出来ると思います。

評価

0

長すぎ
知らない人に見てもらおうってんだから
台車だとか振り子だとか全部消して問題のみ再現できる
最小構成でもってきてください。

別にそんな決まりは全く無いけど、100行超えるプログラムを
「気分転換にこの質問のプログラム読んでみるかー」って気は絶対しない。

paintだとかrepaintだとかダブルバッファだとか
gに直接書くなだとか色々想像できるけど読む気がしない

評価

0

>恋さん
ありがとうございます。参考にさせていただきます。

>--さん
そこまで考えが及ばず不快な思いをさせてしまい誠に申し訳ありませんでした。

評価

0

なんか Swing と AWT が混じってる感じ。
あまり細かいとこまでは見てませんが、問題点は2つ。

イベントディスパッチスレッド以外から Swing を。。。
main の中身と run の ループ内の sleep 以外を
SwingUtilities.invokeLater を使ってイベントディスパッチスレッドで実行させるといい。厳密には run 内の初期設定部分も、これはスレッドを作る前に行う手もあるかな。
なお、スリープ入りループのスレッドではなく javax.swing.Timer を使う手もある。この場合は SwingUtilities.invokeLater なしでもイベントディスパッチスレッドで実行される。

JFrame の paint をオーバーライド。。。
JFrame はかなり複雑なコンポーネントなので下手にいじらないほうがいい。カスタム描画は JFrame に add した JPanel で paintComponent をオーバーライドして行うといい。update のオーバーライドは不要。

評価

0

いまさら新しいスレにうつるのもなんなのでこっちに書きます。
恋さんのリンク先呼んでたら解決してそうだけど↓こんなかんじ

    public void paint(Graphics g){
        int w = getWidth();
        int h = getHeight();

        Image img = createImage( w, h);
        Graphics2D g2 = (Graphics2D)img.getGraphics();

        super.paint(g2);

        int s = getSize().width/2;
        g2.drawLine( 0, 100, s*2, 100);
        g.drawImage(img, 0,0, null);
    }

短く収める為に↑のように書いたけど
>int w = getWidth();
>int h = getHeight();
>Image img = createImage( w, h);
>Graphics2D g2 = (Graphics2D)img.getGraphics();

この部分は paint 内ではなく初期処理的なところに書いたほうがより良い
その場合ウィンドウサイズが変更されたらw,h,img,g2 は作成し直すように作る。

ついでに気になったところ

updateはいらない。

catch(Exception e){} 
は最悪の書き方。最低でも↓の様にすべき。
catch(Exception e){
    e.printStackTrace();
}

評価

0

>仙人さん
詳しい解説ありがとうございます。

>--さん
この度はご迷惑をおかけして申し訳ありませんでした。解説ありがとうございます。

評価

0

いや、別に全く迷惑かかってないよ。
俺がやる気しないって書いただけで本来黙っとけばいいんだけど
たまたま機嫌悪くて書いちゃっただけだから。

そして、その発言に対してちゃんとソースを直して来たら
俺だって応えない訳にはいかんでしょ。

評価

0

>--さん
ありがとうございます。上のソースに適用してみましたらちらつきがなくなりました。ただ今度は若干アニメーションがカクカクしてしまったのですが原因が分からず困っています。もしよろしければ教えていただけませんか?

評価

0

akihinoさんが色々考えた結果を書くなら
俺も考えるよ

評価

0

>--さん
おはようございます。やはりカクカクするということはすべて描画されてるわけではなくて途中途中でしか描画されてないと考えたのですがいかがでしょうか?

評価

0

--さんの提示している内容はあまり筋が良くないからなあ。考えてみろといわれても時間の無駄になるかも。

現在のSwingコンポーネントはデフォルトでダブルバッファリングされるから、UIスレッド(EDT)から描画するように注意すれば「ちらつき」は発生しないと思います。

--さんのコードがカクカクするのは処理が重いからでしょう。Image img = createImage(w, h); とかちょっと例がひどすぎますね。ちらつきの原因や解決方法例としてダブルバッファリングの説明をするのならともかく、そういった説明もなく、悪いコード例だけ提示するというのは…。質問者に間違った認識を持たせてしまう悪い回答だと思います。

評価

0

文章での説明では難しかったかな。
前回の文章も参考に、下記の変更をお試しください。

1. InPen extends JForm を InPen extends JPanel にして JPanel のサブクラスにする。

2. 1 の変更に対応するため main の一行目 InPen wnd = new InPen(); を次の3行と入れ替える。
    JFrame wnd = new JFrame("Inverted Pendulum");
    wnd.setBounds(300, 100, 800, 700);
    wnd.add(new InPen());

3. InPen コンストラクタから setTitle と setBounds を削除。

4. run メソッドの while ループの中の move(); を下記のようにする。
SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        move();
    }
});

5. update() をオーバーライドするのをやめる。(3行消す)

6. paint を paintComponent にする。
public void paintComponent(Graphics g) {
    super.paintComponent(g);

前回も説明しましたが、厳密には Swing コンポーネントやそれらが使うもの(今回は r, t, rdot, tdot, rh, th, rdoth, tdoth 等) は自前のスレッドや main スレッドから変更すると予期しないトラブルの可能性がありますので、他にもあちこち直したほうがいい場所がありますが、とりあえず、ちらつきは上記の変更のみでも改善すると思います。

評価

0

>@さん
回答ありがとうございます。UIスレッドを使ってどのように変更すればちらつきがなくなり、かつアニメーションが滑らかになりますか?

評価

0

>仙人さん
とても詳しい解説ありがとうございます。早速やってみます。

評価

0

「UIスレッド」という仙人さんと違う言葉を使ってしまいましたが…

UIスレッド = EDT = イベントディスパッチスレッド です。あとは仙人さんの説明を良く読んで実践すればできるはず。それと、仙人さんもすでに指摘していますが、Thread + Thread.sleep(30) よりも javax.swing.Timer のほうが滑らかになります。(フレームレートをコントロールしやすいです。)

Thread.sleep(30) を使う場合、描画に 5〜10ms かかるとすると書き換えサイクルは 35〜40ms になってしまいますが、javax.swing.Timer で30ms周期に設定した場合は、描画が 5〜10ms かかった場合でもサイクルは 30ms となるためです。

評価

0

>@さん
回答ありがとうございます。ただTimerをどのように組み込んだらいいかインターネットで調べたのですがいまいち分からないので教えていただけませんか?

評価

0

まずは仙人さんの方法を試してみたらどうですか?
それで滑らかに動けば、より滑らかにする必要ないでしょ。

評価

0

>@さん
ご指摘ありがとうございます。仙人さんの方法で滑らかになりました。

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