[Go to “teaching” in Morimoto Lab]
①ボールに力を加える・投げる
②見えないボールでアトラクタを作る
クラスの中でクラスを使うという、慣れるまではちょっと難易度高い内容になっています。
classの練習、Attractorのための助走としてボールに力を加えるプログラムを作る。
力を加えるということは、現在のボールの速さに別のベクトルを加えることです。
まず、ボールの移動プログラムを用意します(classを使いましょう)。
今回は初速度は0、重力はなし、壁もなし、空気抵抗は考慮しましょう。
次に、マウスを押したら適当なベクトルが速度に足されるコードを作りましょう。
以下はマウスを押したらボールに力を加えるプログラムのベースです。
updateメソッド内でボールの速度と位置を更新する部分は書いてみてください(Game by ball 1,2でやった内容です)。
Ball ball;
void setup(){
size(500,500);
ball = new Ball();
}
void draw(){
background(255);
ball.update();
if(mousePressed) {
ball.addForce();
}
}
class Ball{
PVector pos;
float diam = 10;
PVector vel;
float air_drag = 0.95f;//空気抵抗は考慮する
Ball()
{
pos = new PVector(width/2, height/2);
vel = new PVector(0, 0);//速度は最初0にしておく
}
void update()
{
//update velosity:ここは第1,2回にしたので省略
//update position:ここは第1,2回にしたので省略
//bound:壁とのバウンドは今回なくてよいです
display();
}
void display(){
stroke(0);
noFill();
ellipse(pos.x, pos.y, diam, diam);
}
void addForce(){
PVector vec = new PVector(3,-3);
vel.add(vec);
}
}
ボールを押したときがわかりにくければ、押したときに 文字が出る、背景色を変える、などしてもよい。
当たり判定は必要ない。
与える力の大きさや空気抵抗は適宜調整してください。
以下はupdate velocityのヒント。空気抵抗は今回必須です。
//update velosity
vel.x *=air_drag;//空気抵抗
vel.y *=air_drag;//multメソッドなど使ってもよい
初歩的なミス:
Ballクラスの中では、メンバ変数であるposやvelをpos.xなどとして使います。
Ballクラスの中で、ball.pos.xなどとしないようにしましょう。
↑結果例
Game by Ball (1)では、マウス接触したときにボールの速度を反転させましたが、今回はマウスから風が吹き出すイメージで、マウスからボールの方向に速度を加えるようにしましょう。マウスとボールの間のベクトルを使えばよいです。
また、今回は、マウスとボールの距離に応じた力を加えましょう。
<実装のステップ(例)>
addForce内などで ※
マウスとボールの距離を計算
一定の距離内にボールがあるかどうか判定(この「一定の距離=影響範囲」は適当に決めましょう)
もし影響距離内にボールがあったら、マウスからボールへの単位ベクトルを計算し、適当な倍数にして調整して、ボールの速度にベクトルを足す
4で、一旦「適当な倍数」としましたが、距離に応じて倍数を変えるようにしましょう。近ければ近いほど強い力が加わるようにしましょう。
※上記、マウスとボールの距離を計算するメソッドを作り、draw()のところで距離判定をするのもよいかもしれません。適宜、やりやすいようにアレンジしてみてください。
ヒント:近いほど強くなるような力の計算方法
例えば、以下のように計算した値を調整した上で力の大きさとするとよさそうです。
①(1/距離)
②(影響範囲-マウスとボールの距離)※1
③(ガウス関数などの釣鐘型の関数に距離を変数として計算)※2
経験的には②の挙動がいいみたいですが、他にもよい方法があるかもしれません。※1 値が0以下になるときはifで場合分けの処理をすること。
※2 指数関数はexp(), べき乗はpow()で計算できる。
↑点を並べてマウスを押して周囲に力を与えた場合
※Ex. Bはボール一つでやってみてください。できる人は上記の図のようなボール配置でやってみても〇
※最終的にボールは上記のように配置されますが、講義では1次元配列しか教えていません。今回のAttractorの課題は1次元配列でできるように設計しています。2次元配列はどこかで学ぶとよいとは思いますが、今回に関してはだいぶ意図とは違う解き方になります。(ちょっとこの絵はミスリーディングかもしれません。。🙇)
※この課題は横道にそれるのでとばしてもいいですが、ポケモンGOみたいにボールを投げるコンテンツに興味のある人はやってみてください。
ボールをつかむには?
マウスとボールのあたり判定が必要
ボールをドラッグするには?
マウスを押している(ドラッグしている)かどうかの判定が必要。mousedragged()
つかんだら、ボールは常にマウスの位置に描画する
ボールを離したとき、pmouseX, pmouseYなどを使って速度を計算する
pmouseX/Yは現在の一つ前のフレームでのマウス位置です
mouseReleased()はマウスボタンを離したときに呼ばれる関数です。
計算した速度ベクトルを適当に何倍かして調整し、ボールの速度に足す
Attractorは数値的値の集合が動的に発展をするもの、らしい。
数学的Attractor:
Attractorについてのサイトをいくつか紹介。
今回実装するのはAttractorのような雰囲気のビジュアルのものである。
ここでの実装では、アトラクターは見えないボールとボールの間を線でつなぎ、
そのボールをマウスにひきつけたり、逆に離したりすることで、
不思議な模様を描きます。
参考にしたのは以下のGenerative Designという本の例です。
https://download.bnn.co.jp/support/generativedesign/code/M_4_3_01_TOOL.html
以下のようにコードをダウンロードして、
デスクトップ上でこのプログラムを実行してみよう。
このページの右のボタン”code”から、”download zip”などでdownloadする
解凍したフォルダからCode-Package-Processing-3.x/02_M/M_4_3_01_TOOL/を開く
pdeファイルをProcessingで開く。
Generative Designのライブラリをimportする必要があります。
メニュー>スケッチ>ライブラリをインポート>ライブラリを追加 より”GenerativeDesign”をインストールする
実行できるはず
クリックして、挙動を見てみましょう。
左上のボタンを押すと、UIが現れ、影響範囲や点の動かし方を変更できます。
しかしコードを見てみると、つらく感じる人もいるだろう。
プロのプログラムを読むのはつらい作業だ(教えてないこともあるし)
むしろ、これまで培ったGame by ballの知識で一から実装してみよう。
そっちの方が簡単だと思う。
※高みを目指す人には人のプログラムは色々見てもらいたい。
とても勉強になる。
今日の課題を終えてからもう一度見てみるとよい。
Ex Bを発展してAttractor(もどき?)を作りましょう。画面にボールを並べて、横線で繋ぎ、クリックしたところから近いほど強い力で移動するようにします。ボールはy方向にしか移動しないようにします。
(この課題の途中までのイメージとしては、ExBの最後の図の一列だけを作る感じです。)
具体的なやり方は以下になります。
①まず横一列を作る
Ballクラスの配列を作って、横一列にボールを並べる
重力や壁とのバウンドはいらない
クラスの配列の定義の仕方はGameByBall5Cを参照。
ボールをマウスから遠ざけるように力を加える
距離に反比例するように力の大きさを加える
ボールとボールの間に線を描く(横方向だけ)※下記ヒントを参照
ボールはy方向にしか動かないようにする
ヒント:Ballクラスはあくまでも一つのBallのデータを扱う=Ballクラスに配列を入れない!
線を描画するときに、Ballの配列の複数のBallデータが必要なら、Ballクラス内だけで完結することはできません。
グローバルに定義したBall配列をBallクラス内で扱おうとする人がいますが、これはいけません。
もちろんPVectorの配列で位置を定義するのもNG。
ボールの位置データはBallクラス内に既にposがあり、Ballクラスの配列を作れば位置も一緒にたくさん作られます。
とにかく、一度完成したBallクラス内はその後ほとんどいじりません。
Ballクラスの配列を使って線を描画するための例を以下に二つ挙げます。
drawメソッド内で、balls.get(i).posとballs.get(i+1).posを使ってlineを描画する。
Ballクラス内のdisplayメソッドにPVectorの引数を指定できるようにする→drawメソッド内でdisplayメソッドを呼び出すように変更&balls.get(i).display( balls.get(i+1).pos )などとする→→displayメソッド内でlineを描画するようにする。
上記のように、一度実装した後、一本分の線の描画内容を今度はBallLineクラスとして作成します。
つまり、最終的にBall配列を定義する場所はBallLineクラス内です。
そのときはBallLineクラスの配列をグローバル変数として作りましょう。下図参照。
↑今回のクラスの入れ子関係:
Ballクラスの配列を持ったBallLineクラス、
BallLineクラスの配列をグローバル変数として定義。
②縦に増やす
横一列できたら、それをクラス化(BallLineクラス)する ※
y方向にBallLineをたくさん並べる
ボールを消す
色や点の間隔や動き方を調整する
線にきれいな色(グラデーションなど)を指定する
(HSB色空間や透明色などを使う)
※Ballクラスには位置を指定する引数を持つコンストラクタが必要になります。BallLineクラスも同様。
※BallLineクラスにBallクラスの配列を作ります。
※このテキストの最後にBallLineクラスの例を載せていますが、できれば一度見ずに作成してみましょう。