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

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

ただいまの
回答率

87.78%

ProcessingのPAppletが正しく機能しない

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 2,734

score 20

 PApplet内の変数g(Pgraphics)がNullのまま処理が行われる。 

Processingのアニメーションを練習するため簡単なクラスを作ったのですが、なぜかPAppletクラスの変数である

PGraphics g 

という変数が初期化されず、Nullのままなため、PAppletクラスを継承したTestPVectorellpse()を呼び出したときにPApplet
java.lang.NullPointerExceptionが発生します。
ざっとデバックしてみたのですが、なぜnullのままなのかまったくわかりません。そもそもPApplet内のどこでgが初期化されているのかすらわかりませんでした。

画面は、フレームは表示されるが、まったく更新されず灰色のまま特に動くことはありません。

 発生している問題・エラーメッセージ

java.lang.NullPointerException
    at processing.core.PApplet.ellipse(PApplet.java:12103)
    at usingProcessing.TestPVector.draw(TestPVector.java:40)
    at usingProcessing.RandomSpreadParticle.draw(RandomSpreadParticle.java:34)
    at processing.core.PApplet.handleDraw(PApplet.java:2429)
    at processing.awt.PSurfaceAWT$12.callDraw(PSurfaceAWT.java:1557)
    at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:313)

 該当のソースコード

public class RandomSpreadParticle extends PApplet {
    int num = 1000;
    TestPVector[] mys = new TestPVector[num];

    public static void main(String[] args) {
        // TODO 自動生成されたメソッド・スタブ
        PApplet.main(new String[] {RandomSpreadParticle.class.getName()});
    }

    public  void settings() {
        size(800,600);
    }
    public void setup() {
        frameRate(60);
        for(int i = 0; i < num; i++) {
            mys[i] = new TestPVector();
            mys[i].location.set(width/2.0f, height/2.0f);
            mys[i].acceleration.set(random(-10, 10), random(-10, 10));
            mys[i].gravity.set(0.0f,0.1f);
            mys[i].friction = 0.01f;
        }
    }
    public void draw() {
        fill(0, 31);
        rect(0, 0, width, height);
        noStroke();
        fill(255);
        for(int i = 0; i < num; i++) {
            mys[i].update_loc();
            mys[i].draw();
            mys[i].bounce();
        }
    }
}
--------------------------------------------------------------------------------------------------------

public class TestPVector extends PApplet {
    PVector location;
    PVector acceleration;
    PVector velocity;
    PVector gravity;
    PVector min;
    PVector max;
    float mass;
    float friction;
    float radius;

    public TestPVector() {
        mass = 1.0f;
        friction = 0.01f;
        radius = 4.0f;
        //位置、加速度、速度、質量、初期の力を初期化
        location = new PVector(0f, 0f);
        velocity = new PVector(0f, 0f);
        gravity = new PVector(0f, 0f);
        acceleration =  new PVector(0f, 0f);
        //運動範囲
        min = new PVector(0.0f,0.0f);
        max = new PVector(width,height);
    }

    public void draw() {
        ellipse(location.x, location.y,mass * radius * 2, mass * radius * 2);
    }

    public void update_loc() {
                //加速度に重力を追加
                acceleration.add(gravity);
                //速度に運動量を追加
                velocity.add(acceleration);
                //運動量に摩擦力をかける
                velocity.mult(1.0f-friction);
                //位置に運応用を追加
                location.add(velocity);
                //加速度0
                acceleration.set(0 ,0);
    }

    public void bounce() {
        if(location.x > max.x) {
            location.x = max.x;
            velocity.x *= -1;
        }
        if(location.x < min.x) {
            location.x = min.x;
            velocity.x *= -1;
        }
        if(location.y > max.y) {
            location.y = max.y;
            velocity.y *= -1;
        }
        if(location.y < min.y) {
            location.y = min.y;
            velocity.y *= -1;
        }
    }
    public void throughWall() {
        if(location.x > max.x) {
            location.x = max.x;
        }
        if(location.x < min.x) {
            location.x = min.x;
        }
        if(location.y > max.y) {
            location.y = max.y;
        }
        if(location.y < min.y) {
            location.y = min.y;
        }
    }
}

 試したこと

入門本を参考に書いてるため難しいことはしていません。なので、ほとんどサンプルコードを参考に書いてます。デバッグはしたのですが、PApplet以外の箇所でnullが発生していることはなかったです。

 補足情報(FW/ツールのバージョンなど)

eclipse32bit版の環境で動かしています。pleiadesでダウンロードしたので、おそらくjavaも32bit版だと思います。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

0

入門本を参考に書いてるため難しいことはしていません。

とはおっしゃいますが、ProcessingのプログラムをJavaとして記述している点、少々難しいことをしていると思います。ProcessingのフロントエンドはIDEとして貧弱なのでJavaのIDEを使いたくなる気持ちはわかりますが...

 原因

ProcessingのプログラムをJavaで記述する場合、PAppletを継承したクラスは一つしか書かないようにすべきですが、ご質問のプログラムでは2つのクラスがそれぞれPAppletを継承したクラスとして定義されています。それが原因です。

Processingのプログラムを書いたり動かしたりする最も普通の方法は

(1) JavaのIDEではなくProcessingのフロントエンドを起動
(2) 変数や関数やクラスをトップレベルに直接記述する
(3) 長いプログラムになった場合は複数のタブに分けて記述する
(4) Processingのフロントエンドからプログラムを起動する

なのですが、これをJavaプログラムとして作成する場合、Processingのちょっとしたトリックについて知っておく必要があります。そのトリックとは

  • (A) Processingは暗黙的にスケッチのメインクラスを定義する
    ご質問のコードでいえばRandomSpreadParticleがこれに当たります。
  • (B) トップレベルに記述した変数, 関数はメインクラスのフィールド, メソッドになる
    RandomSpreadParticleのsetupやdrawなどがこれに当たります。
  • (C) トップレベルに記述したクラスはメインクラスの内部クラスになる
    TestPVectorがこれに当たります。

質問者さんのコードは(A),(B)には対処しておられますが、(C)の配慮ができていません。

 対処

TestPVectorを次のようにRandomSpreadParticleのinner class(※)として定義すると、Processingのフロントエンドで作成したプログラムと同様に動作します。

public class RandomSpreadParticle extends PApplet {
  ...

  class TestPVector { // PAppletを継承してはならない。またstaticであってはならない。
    ...
    void 任意のメソッド() { // drawという名前でもよいが任意の名前でよい。
      ellipse(...); // PAppletのメソッドを呼び出せる
    }
  }
}

つまりTestPVectorから呼び出した描画メソッドは実際はRandomSpreadParticleのインスタンスへの描画と解釈されるようになります。

※: inner class
Java言語仕様の用語です。個人ブログなどにはあるクラスの内側に定義したクラスのことを内部クラスと呼んでいるものがありますが、言語仕様ではそれをnested class(入れ子のクラス)と呼んでいます。言語仕様で定義されているinner classはstaticがついていないクラスのことですのでご注意ください。本件ではinner classでないとダメです。


余談:

対処にはinner classにする方法を書いたのですが・・・Javaのプログラムとしてみた場合この方法は嬉しくない点もあります。プログラムが複雑になるにつれ「一つのソースコードが巨大になりすぎる」点です。質問者さんの元のコードのように別のソースファイルに分けて記述したい場合もままあります。それに配慮した別の解決法も(正統的な方法から怪しい方法まで)いくつか考えられますが、長くなるので本回答では省略します。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

関連した質問

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