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

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

ただいまの
回答率

90.50%

  • Java

    14049questions

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

  • Android

    6618questions

    Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

花火プログラム

解決済

回答 1

投稿

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

nakamura-

score 40

コード
public class MainActivity extends Activity{

    FrameLayout frameLayout;
    CanvasView canvasview;
    int hanabiNum = 5;                          //花火の数
    Hanabi hanabi[] = new Hanabi[hanabiNum];    //Hanabiクラスのインスタンスの配列


    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);

        frameLayout = new FrameLayout(this);
        frameLayout.setBackgroundColor(Color.BLACK);
        setContentView(frameLayout);

        canvasview = new CanvasView(this);
        frameLayout.addView(canvasview);

        for(int i = 0; i < hanabiNum; i++){
            hanabi[i] = new Hanabi(((Math.random()*600) + 50),    //Hanabiのインスタンス化
                        ((Math.random()*50) + 150),
                        (int)((Math.random()*10000) + 500),
                        (int)(Math.random()*255),
                        (int)(Math.random()*255),
                        (int)(Math.random()*255));

            hanabi[i].setCanvasView(canvasview);
            hanabi[i].start();
        }

        canvasview.setNumAndHanabi(hanabiNum, hanabi);
    }

}
コード
public class CanvasView extends View{

    int hanabiNum;
    Hanabi hanabi[] = new Hanabi[hanabiNum];

    /*CanvasViewクラスのコンストラクタ*/
    public CanvasView(Context context){
        super(context);
    }

    public void setNumAndHanabi(int hanabiNum, Hanabi hanabi[]){
        this.hanabiNum = hanabiNum;
        this.hanabi = hanabi;
    }

    /*描画メソッド*/
    protected void onDraw(Canvas canvas){

        for(int i = 0; i < hanabiNum; i++){
            canvas.drawPoint((float)hanabi[i].X, (float)hanabi[i].Y, hanabi[i].paint);                //paintで座標(x,y)点描画

            for(int j = 0; j < 1000; j++){
                canvas.drawPoint((float)hanabi[i].X2[j], (float)hanabi[i].Y2[j], hanabi[i].paint);    //点描画
            }
        }
    }

}
コード
public class Hanabi extends Thread{

    private Handler handler = new Handler();
    Paint paint = new Paint();        
    CanvasView cv;                

    double G = 9.8*2;        //重力加速度
    double t;                //経過時間(0.1秒毎)
    int time;                //発射時刻
    int red;                //赤色成分
    int green;                //緑色成分
    int blue;                //青色成分

    /*打ち上がり段階で使う定数*/
    double X0;                //初期のX座標
    double Y0 = 1000;        //初期のY座標(1000で固定)
    double X;                //時刻t時のX座標
    double Y;                //時刻t時のY座標

    double V0x = 0;            //初速度のX成分(0で固定)
    double V0y;                //初速度のY成分
    double Vx;                //時刻t時の速度のX成分
    double Vy;                //時刻t時の速度のY成分

    /*爆発段階で使う定数*/
    int hanabiball = 1000;                        //花火の拡散弾数
    double angle[] = new double[hanabiball];    //発射する角度

    double X02[] = new double[hanabiball];        //初期のX座標
    double Y02[] = new double[hanabiball];        //初期のY座標
    double X2[] = new double[hanabiball];        //時刻t時のX座標
    double Y2[] = new double[hanabiball];        //時刻t時のY座標

    double V02[] = new double[hanabiball];        //V0x2とV0y2の合成
    double V0x2[] = new double[hanabiball];        //初速度のX成分
    double V0y2[] = new double[hanabiball];        //初速度のY成分
    double Vx2[] = new double[hanabiball];        //時刻t時の速度のX成分
    double Vy2[] = new double[hanabiball];        //時刻t時の速度のY成分


    /*Hanabiクラスのコンストラクタ*/
    public Hanabi(double X0, double V0y, int time, int red, int green, int blue){

        this.time = time;
        this.red = red;
        this.green = green;
        this.blue = blue;

        this.X0 = X0;
        this.V0y = V0y;

        X = X0;    
        Vx = V0x;

        for(int i = 0; i < hanabiball; i++){
            angle[i] = Math.random()*360;        //拡散弾のアングル設定(0~360°)
            V02[i] = Math.random()*50;            //拡散弾の初速度の設定(0~50)
            V0x2[i] = V02[i]*Math.cos(angle[i]*(Math.PI/180));        //V02のX成分
            V0y2[i] = V02[i]*Math.sin(angle[i]*(Math.PI/180));        //V02のY成分
        }
    }



    public void run(){
        Y = 5000;                            //最初枠外に置いて隠しておく
        Arrays.fill(Y2, 5000);                //Yと配列Y2の全てに5000代入

        try{
            Thread.sleep(time);                //発射する時間を設定
        }
        catch(InterruptedException e){
        }

        /*打ち上がり段階*/
        for(int T = 0; T < 1000 ; T++){
            t = T * 0.1;

            Vy = (V0y - G*t);
            Y = (Y0 - (V0y*t - G*t*t/2));

            try{
                Thread.sleep(50);            //0.05秒間隔
            }
            catch(InterruptedException e){
            }

            paint.setStrokeWidth(5);            
            paint.setColor(Color.WHITE);        

            viewStart();                    

            if(Vy < 0.2){
                break;
            }
        }

        /*爆発段階*/
        Arrays.fill(X02, X);
        Arrays.fill(Y02, Y);

        for(int T = 0; T < 30; T++){
            t = T * 0.1;

            for(int i = 0; i < hanabiball; i++){
                Vx2[i] = V0x2[i];
                Vy2[i] = (V0y2[i] - G*t);
                X2[i] = (X02[i] + (Vx2[i]*t));
                Y2[i] = (Y02[i] - (V0y2[i]*t - G*t*t/2));
            }
            try{
                Thread.sleep(50);            //0.05秒間隔
            }
            catch(InterruptedException e){
            }

            paint.setStrokeWidth(2);        
            paint.setColor(Color.rgb(red, green, blue));
            paint.setAlpha(240-(8*T));        

            viewStart();    
        }

    }


    public void viewStart(){
        handler.post(new Runnable() {
            @Override
            public void run(){
                cv.invalidate();        //onDrawメソッド呼び出し
            }
        });
    }

    public void setCanvasView(CanvasView cv) {
        this.cv = cv;
    }

}

上のプログラムは花火のプログラムで一応完成しました。
MainActivityクラスの花火の打ち上げる数int hanabiNumが1発の場合は綺麗な花火になっているのですが10発や20発と増やしていくと描画の点と点の間隔が広がっていき、綺麗な花火を描けません。
Hanabiクラスに点を描画する座標や時間を書いてインスタンスごとに持たしているのに、このようになる理由がわかりません。
この原因はなぜなのでしょうか?
また改善方法も教えていただきたいです。
よろしくお願いします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • yona

    2016/04/25 16:32

    画像を追加することはできますか?

    キャンセル

  • nakamura-

    2016/04/25 16:44

    花火が1発だけの場合と10発の場合の画像ということですよね? 画像を載せることはできますが、動画でないと画像では違いがわかりにくいと思います。 言葉で言うと花火1発の方はぬるぬる動いて、花火10発はかくかくに動きます。

    キャンセル

  • yona

    2016/04/25 17:08

    「描画の点と点の間隔」の意味がわかりません。 時間的な描画間隔が広がっているのか、点と点の間の距離が広がっているのかがわかりません。 「花火1発の方はぬるぬる動いて、花火10発はかくかくに動きます。」からすると描画間隔ですか?

    キャンセル

  • nakamura-

    2016/04/25 17:13

    すみません。 点と点の距離です!

    キャンセル

回答 1

checkベストアンサー

0

今の状況だと各スレッドから再描画依頼が届くので、各HanabiクラスのviewStartメソッドを廃止し、CanvasViewに定期的にinvalidを実施するスレッドを追加してみてはどうでしょうか?

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/04/26 11:06

    回答ありがとうございます^^
    「各スレッドから再描画依頼が届くので」とありますが、これは何かまずいのでしょうか?

    また「CanvasViewに定期的にinvalidを実施するスレッドを追加」ですが、これは必ずCanvasViewクラスでないといけないということはありますか?

    よろしくお願いします!

    キャンセル

  • 2016/04/26 12:07

    例えば、あなたが20人に囲まれながら絵を描いていたとして、周囲の人が口々に「ここに、こんな線を描いて」と言ってきたら混乱しますよね?だいたいこれと同じようなことが起きていると考えられます。
    そこで、周囲の人はメモに描画したいものについての設定値を書いておき、あなたが自分のタイミングで、メモの設定値から描画すると混乱は起きにくいですよね。

    上の例えでは周りの20人はHanabiに存在するスレッドであり、あなたは「CanvasView」または「MainActivity」に存在するスレッドになります。

    キャンセル

  • 2016/04/26 13:21 編集

    わかりやすい解説ありがとうございます!
    MainActivityクラスに定期的にinvalidを実施するスレッドを追加しました!

    コード
    public class MainActivity extends Activity implements Runnable {

    Handler handler = new Handler();
    Thread thread;
    //以下略

    @Override
    protected void onCreate(Bundle savedInstanceState){
    //省略
    canvasview.setNumAndHanabi(hanabiNum, hanabi);

    thread = new Thread(this);
    thread.start();
    }

    public void run(){
    handler.post(new Runnable() {
    @Override
    public void run(){
    canvasview.invalidate(); //onDrawメソッド呼び出し
    handler.postDelayed(this, 1);
    }
    });
    }

    このように書き換えたのですが結果が変わらず、描画の点と点があいたままでした。

    「定期的にinvalidを実施するスレッドを追加」とはどのように書いたらいいのでしょうか?

    キャンセル

  • 2016/04/26 13:37 編集

    Hanabi側のスレッドは削除しましたか?
    また、現状では1ms間隔で描画更新をしているのでとりあえず500等の大きい値に変更して見てください。

    キャンセル

  • 2016/04/26 14:18

    はい!
    ちゃんと消しましたよ。

    500で実行しても描画時間の間隔が長いので余計に点と点の間隔があきました!
    私の追加したプログラムではダメなんですかね?

    キャンセル

  • 2016/04/26 14:29

    点と点の間隔という表現ではわからないと言いましたよね?
    具体的にどの点とどの点ですか?
    前に表示した点と今表示されている点ですか?

    キャンセル

  • 2016/04/26 14:34

    CanvasViewのonDrawの処理が怪しいです。なぜ二重ループになっているのでしょうか?

    キャンセル

  • 2016/04/26 15:03

    すみません。
    今表示されている点と次に表示される点の距離の長さも時間間隔もあきました。

    ・花火1発だけのとき
    最初の点y=1000 次の点y=980 その次の点y=960
    この点と点の間の時間0.1秒

    ・花火10発のとき
    最初の点y=1000 次の点y=950 その次の点y=900
    この点と点の間の時間0.5秒
    見たいな感じです

    二重ループは打ち上がる花火の数につき、爆発のときの拡散弾1000個を描画しています。
    確かに打ち上がっているときも拡散弾のほうも描画されているので無駄な処理ではありますね。
    これが処理を重たくしているんですかね?

    キャンセル

  • 2016/04/26 15:36

    10*1000回描画処理をしているので、なかなかな計算量になりそうです。
    一度コメントアウトしてみるとどうなりますか?

    キャンセル

  • 2016/04/26 17:53

    コメントアウトしたところ普通に動きました。
    yonaさんの言うとおりここが原因みたいでした!
    1000のところを200にすると花火も普通に動きました。
    10*1000回ってそんなに多いんですね笑

    ありがとうございました!

    キャンセル

  • 2016/04/26 18:54

    一秒間に24回以上描画しない人間の目が違和感を感じるので、いろいろなオーバーヘッドを考えてもonDrawの中はなるべく重い処理は書かないほうがいいですね。

    キャンセル

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

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

関連した質問

同じタグがついた質問を見る

  • Java

    14049questions

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

  • Android

    6618questions

    Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。