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

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

0

自作クラスとArrayListについて。

私はJava—というかProcessingを使ってプログラミングをしているかなたんといいます。
ProcessingがJavaと似ていることもあり、ここで質問させてもらうことにしました。

手の座標を用いたペイントソフトなんかを作りたいと思っています。
座標はPVector型で取得することができて、その取得した座標をArrayListへ追加し、その動きを線でつなごうと思っています。
複数人でもできるようにしたいと思っているので、手の数がいくつになるのかわかりません。
でも、複数の手それぞれに線の色を決めたり手の座標を記録していきたいと思っています。
そこで、線の色や手の座標などを管理したり、その座標を用いて線を描くためのクラスを作成することにしました。

class User_Paint{
  color paint_color; //線の色を記憶
  ArrayList<PVector> paint_pos=new  ArrayList<PVector>(); //手の座標を記憶
  User_Paint(){
    paint_color=color(random(0,255),random(0,255),random(0,255)); //線の色をランダムに決定
    paint_pos.clear(); //ArrayListの中身を念のため空にしておく
  }
  void point_add(PVector hand_pos){
    paint_pos.add(hand_pos); //取得した手の座標を追加
  }
  void draw_paint(){
    stroke(paint_color); //線の色を決定した色にする
    strokeWeight(3); //線の太さを3にする
    for(int n=1;n<paint_pos.size();n++){ //リストの1番目から終わりまで繰り返す
      line(paint_pos.get(n-1).x,paint_pos.get(n-1).y,paint_pos.get(n-1).z,paint_pos.get(n).x,paint_pos.get(n).y,paint_pos.get(n).z); //ひとつ前の点から今の点まで線を引く
    }
  }
}

そして、それを手の数だけ実体化させようと、ArrayListを用いて&#8237;人を検出したらインスタンスを追加するようにしています。

//グローバル変数
ArrayList[] paint={new ArrayList<User_Paint>(),new ArrayList<User_Paint>()}; //左手と右手用にArrayListの配列にする
//人を検出したら
paint[0].add(new User_Paint()); //左手のインスタンス追加
paint[1].add(new User_Paint()); //右手のインスタンス追加

手の座標はdraw関数内で取得し、その取得した座標を先ほど作ったUser_Paintクラスのpoint_add関数を用いてクラス変数であるArrayListのpaint_posに追加して、その軌跡を描画させています。

//draw内で
context.getJointPositionSkeleton(userList[i],SimpleOpenNI.SKEL_LEFT_HAND,hand_pos[0]); //i番目の人の左手の座標取得
context.getJointPositionSkeleton(userList[i],SimpleOpenNI.SKEL_RIGHT_HAND,hand_pos[1]); //i番目の人の右手の座標取得
User_Paint[] painting={(User_Paint)paint[0].get(i),(User_Paint)paint[1].get(i)}; //i番目の人のインスタンスを取得
painting[0].point_add(hand_pos[0]); //左手の座標追加
painting[0].draw_paint(); //左手の軌跡描画
painting[1].point_add(hand_pos[1]); //右手の座標追加
painting[1].draw_paint(); //右手の軌跡描画

実行して手を動かしてみても、線が描画されません。
実行画面を見てみると、線でなく点になってしまっています。
そこで、追加した座標や描画している座標を出力させてみたのですが、なんと追加したらその前の座標がみんな最後に追加した座標になってしまっています。

座標追加:[ 408.17795, 504.1026, 1185.2173 ]
[ 408.17795, 504.1026, 1185.2173 ],[ 408.17795, 504.1026, 1185.2173 ],[ 408.17795, 504.1026, 1185.2173 ],[ 408.17795, 504.1026, 1185.2173 ],きちんとなってる?
painting
-5745259
[ 408.17795, 504.1026, 1185.2173 ],[ 408.17795, 504.1026, 1185.2173 ],[ 408.17795, 504.1026, 1185.2173 ],[ 408.17795, 504.1026, 1185.2173 ],描画終了
座標追加:[ -251.10933, -11.485046, 1247.2742 ]
[ -251.10933, -11.485046, 1247.2742 ],[ -251.10933, -11.485046, 1247.2742 ],[ -251.10933, -11.485046, 1247.2742 ],[ -251.10933, -11.485046, 1247.2742 ],[ -251.10933, -11.485046, 1247.2742 ],きちんとなってる?
painting
-12211937
[ -251.10933, -11.485046, 1247.2742 ],[ -251.10933, -11.485046, 1247.2742 ],[ -251.10933, -11.485046, 1247.2742 ],[ -251.10933, -11.485046, 1247.2742 ],[ -251.10933, -11.485046, 1247.2742 ],描画終了
座標追加:[ 394.7011, 511.8561, 1187.2467 ]
[ 394.7011, 511.8561, 1187.2467 ],[ 394.7011, 511.8561, 1187.2467 ],[ 394.7011, 511.8561, 1187.2467 ],[ 394.7011, 511.8561, 1187.2467 ],[ 394.7011, 511.8561, 1187.2467 ],きちんとなってる?
painting
-5745259
[ 394.7011, 511.8561, 1187.2467 ],[ 394.7011, 511.8561, 1187.2467 ],[ 394.7011, 511.8561, 1187.2467 ],[ 394.7011, 511.8561, 1187.2467 ],[ 394.7011, 511.8561, 1187.2467 ],描画終了

クラスやArrayListの使い方を間違えたでしょうか・・・?

15

回答

14520

閲覧

15件の回答

評価

0

たいして読んでないけど。

>なんと追加したらその前の座標がみんな最後に追加した座標
になってしまっています。

ここだけ読む限りは、よくあるListの使い方の間違いだな。
Listに追加したインスタンスを使い回すと(参照というものを
理解してない場合に)そういうことになる。

評価

0

>Listに追加したインスタンスを使い回すと(参照というものを理解してない場合に)そういうことになる。

(C言語でのポインタやアドレスは好きじゃなかったり・・・)
私が使いまわし方を誤ったとかいう話でしょうか?

評価

0

>C言語でのポインタやアドレスは好きじゃなかったり
好き嫌いを言えるレベルではなさそうだが…。

>使いまわし方を誤った
というか、使い回してはいけない。

評価

0

>>C言語でのポインタやアドレスは好きじゃなかったり
>好き嫌いを言えるレベルではなさそうだが…。

参照という単語を聞いて、勝手にポインタやアドレスを連想したもので。
ポインタを利用して配列を回していたときに途中でゴミ(文字化け)が出現してきた経験などがあったりして、ポインタはあまり使いたくないなぁと。
だからあまり好きじゃなくて・・・

使いまわしてはいけない—
User_Paint[] painting={(User_Paint)paint[0].get(i),(User_Paint)paint[1].get(i)};
なんてしたからでしょうか・・・?
人数×両手分インスタンスを用意したいんです、でも人数はわからないから、動的配列にしようと思ったんです。
人数が増えたらその分インスタンスを追加するようにして。
でも、
paint[0].get(i).point_add(hand_pos[0]);
という書き方をしてもエラーが出ていたので、先ほどのような書き方をしたのです。
フレームごとに手の座標を取得し、その取得した座標をクラス変数のArrayListに追加するためにインスタンスを取得してクラス内の関数を呼び出します。
そのようなことが使いまわしているということなのでしょうか・・・?

評価

0

>参照という単語を聞いて、勝手にポインタやアドレスを連想したもので。
まさにそれなんだけど。

Javaは、ポインタが見えないようになってるだけ。やってることはCと一緒。

>人数×両手分インスタンスを用意したいんです
人数はともかく、問題はその「両手」の方だろう。

A a = new A();
list.add(a);
list.add(a);
という操作は、「Aのインスタンスを2つ追加」ではなく、「Aのインスタンス1つを2回追加」だということは、理解できてるだろうか。

何かの予約リストに対して、赤い服を着て記名、次に青い服を着て記名した。
名前が点呼されたとして、答えるのは両方とも青い服を着た自分だろう。
今やってるのは、そういうこと。

評価

0

//グローバル変数で
ArrayList[] paint={new ArrayList<User_Paint>(),new ArrayList<User_Paint>()};
//人を検出したら
paint[0].add(new User_Paint());
paint[1].add(new User_Paint());

と説明のクラスAでの話は同じ状況であるということでしょうか?
説明のことは理解できます。
でも今回はnewを配列のArrayListにaddしているので、別のインスタンス・・・ではないのですか?
人物を検出したら、左手用にpaint[0]・右手用にpaint[1]にインスタンスを追加しているのですが。

今のところ実行時の人物というのは私一人なので、赤い服を着て記名した後は青い服を着て記名するなんてことはないと思っているのですが、実は気がつかないうちに青い服を着て記名して行ってしまっているのでしょうか?
その説明だと、毎回新たらしいインスタンスを使うようになっていて、そこに座標を登録しているから、線ではなく点のままだということだと思ったのですが、その状況ではArrayListのサイズは常に1だということになりませんか?
でも、私のプログラムでは追加とともにサイズは増えているが、すべて最後に取得した座標なんです。
これもインスタンスが原因なのでしょうか?

評価

0

説明が悪かったので理解したつもりにさせてしまったか。
人ではなく手についての話だったんだが、喩えか人じゃ無理
か…。

壁に連続して手をついて行くとする。
「ついた」という記憶は残ってても、人物が一人ならそれら
を同時に見ることはできない。
同時に見たいなら、その分だけ人が必要。

「人物を検出したら」じゃなくて、「手が動いたら」じゃな
きゃだめだってこと。

評価

0

>「人物を検出したら」じゃなくて、「手が動いたら」じゃなきゃだめだってこと。

手が動くたびにインスタンスを作らなくてはならないってことでしょうか?
クラスで描画する線の色・取得した手の座標たちを記憶させて、それらを用いて描画をするようにしているので、そうだとしたらこのクラスはよくないということでしょうか?
人物を検出したときに左手右手それぞれインスタンスを作り、それぞれの軌跡をそれぞれのインスタンスのクラス変数に記憶させて、それぞれでクラス変数の内容を用いて描画しようとしているのですが、そういう考えは間違っているでしょうか?

(ArrayList<User_Paint>)paint[0]
1人目の左手用インスタンス
//変数
color 1人目の左手の軌跡の描画に使う色
ArrayList<PVector> 1人目の左手の軌跡の座標たち
//関数
void 取得した1人目の左手の座標を軌跡に追加 引数PVector
void 1人目の左手の軌跡を描画に使う色を用いて点をつないで線にする 引数なし
(2人目の—)

(ArrayList<User_Paint>)paint[1]
1人目の右手用インスタンス
//変数
color 1人目の右手の軌跡の描画に使う色
ArrayList<PVector> 1人目の右手の軌跡の座標たち
//関数
void 取得した1人目の右手の座標を軌跡に追加 引数PVector
void 1人目の右手の軌跡を描画に使う色を用いて点をつないで線にする 引数なし
(2人目の—)

というつもりなのですが・・・

評価

0

まだ誤解してるのか、そもそも違うバグなのか。

手を動かすたびに作るインスタンスはUser_Paintではなく
PVectorなんだがok?

評価

0

クラスを実体化したもの=インスタンス
と思っていましたが、
インスタンス=クラスを実体化したもの"だけではない"
のですね。

//グローバル
import SimpleOpenNI.*;
SimpleOpenNI context;
//setup関数内
context = new SimpleOpenNI(this);
context.setMirror(true);
if(context.enableDepth() == false){
 println("Can't open the depthMap, maybe the camera is not connected!"); 
 exit();
 return;
}
context.enableUser(SimpleOpenNI.SKEL_PROFILE_ALL);
//draw関数内
context.update();

でSimple-OpenNIを用いてKinectから情報を得ています。
drawはフレームレートに従ってフレームごとに呼び出されます。
そんなdraw関数内で、

context.getJointPositionSkeleton(userList[i],SimpleOpenNI.SKEL_LEFT_HAND,hand_pos[0]);
context.getJointPositionSkeleton(userList[i],SimpleOpenNI.SKEL_RIGHT_HAND,hand_pos[1]);

として手の座標を取得しています。
hand_posについては、グローバルで以下のように定義しています。

PVector[] hand_pos={new PVector(),new PVector()};

draw内で取得した座標をもとにそれぞれのクラス変数に追加しているので、毎回手を動かせば異なる座標が引数になっているはずです。
現に、引数として受け取った座標を毎回出力させてみたとき、毎回異なる座標になっています。

そのグローバルで宣言したhand_posをdraw内で使いまわしているというのが原因ですか?
座標取得はdrawでそれぞれ1度ずつ行っています。
追加はdrawでそれぞれ1度ずつ行っているので、各フレーム1度ずつだけ行われているはずです。
正直言えばグローバルで—Hand_Paintファイルでグローバル宣言した変数が別ファイルのUser_Paintファイルでも有効になっていますか?
それで、引数で渡している値が正しい状況ではなく、追加がうまくいっていないということですか?
引数の変数名も取得に使用する変数名も配列かどうかの違いはあれどおんな変数名ですし・・・

評価

0

>現に、引数として受け取った座標を毎回出力させてみたと
き、毎回異なる座標になっています。

1.ArrayList<PVector>をpublicにする。
2.PVector内の座標を書き換えた直後に(addする前に)
ArrayListに追加済みのPVectorの値を外から表示してみる。

Cのポインタから逃げたツケが回って来ているような気がす
る。

ところでProcessingとやらは、Javaじゃないんだね。
全然気にしてなかった。
本来は掲示板の対象外だろうが、実質的に同じものと言って
いいくらいだし、参照の落とし穴がJavaと全く一緒だから、
まあいいのかな。
私が決めるところじゃないが。

評価

0

addした後、座標を書き換え、さらにUser_Paint内の
ArrayList.get(0)と、外のPVectorを==で比較した結果を表示し
てみても良いかも。

評価

0

グローバルで宣言している取得した手の座標を記憶させる変数名と、クラス変数に追加するための関数の引数の変数名が同じだったのが原因でした。
グローバルで宣言している方をdraw関数内の手の座標を取得する直前で宣言するように変更した結果、無事右手・左手の軌跡を描画させることができました。

ProcessingはJavaに似ている(ArrayListの存在もJava経由で知って、現在Processingで使っています。)からということでここで質問させてもらいましたが、ありがとうございました。

評価

0

>グローバルで宣言している取得した手の座標を記憶させる変
数名と、クラス変数に追加するための関数の引数の変数名が
同じだったのが原因でした。
ほう。
ではJavaと違って値渡しなのか…。
かつ、Javaの場合はグローバル(クラスのフィールドだが)
とメソッド引数が同名だとメソッド引数が優先される。

そうすると似てるのは見た目だけぽいから、やはり今度から
はJavaの掲示板で聞かない方が良さそうだ。

老婆心ながら。
グローバル変数が使える言語の一般指針としては、グローバ
ルはあえてそうする必要がない限りは使わないことだ。

評価

0

>ではJavaと違って値渡しなのか…。
>かつ、Javaの場合はグローバル(クラスのフィールドだが)とメソッド引数が同名だとメソッド引数が優先される。

やはり言語によってそういった部分も異なるんですね。

>そうすると似てるのは見た目だけぽいから、やはり今度からはJavaの掲示板で聞かない方が良さそうだ。

わかりました。
今度はできるだけProcessing用の掲示板を探すようにします。

>グローバル変数が使える言語の一般指針としては、グローバルはあえてそうする必要がない限りは使わないことだ。

はい。
これからはもっと変数の宣言位置や名前など気にしていこうと思います。

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