差分
このページの2つのバージョン間の差分を表示します。
両方とも前のリビジョン 前のリビジョン 次のリビジョン | 前のリビジョン | ||
lecture:design_with_prototyping:p5.js編:13.動きの再構成 [2021/05/18 10:11] – [物理演算] baba | lecture:design_with_prototyping:p5.js編:13.動きの再構成 [2021/05/25 13:25] (現在) – [パーティクル] baba | ||
---|---|---|---|
行 12: | 行 12: | ||
特段面白いものではないと思いますが、次のようなコードはどうでしょうか?p5jsのエディタごとこのページにはります。短いプログラムですが、じっくり読んでみましょう。単純にマウス位置に丸を描くのではなく、マウスカーソルの位置に徐々に丸が近づいていくプログラムにしています。 | 特段面白いものではないと思いますが、次のようなコードはどうでしょうか?p5jsのエディタごとこのページにはります。短いプログラムですが、じっくり読んでみましょう。単純にマウス位置に丸を描くのではなく、マウスカーソルの位置に徐々に丸が近づいていくプログラムにしています。 | ||
- | <html> | + | |
- | <iframe src="https://editor.p5js.org/tetsuakibaba/sketches/lq6TI8Sis" | + | <WRAP group> |
- | </html> | + | <WRAP half column> |
+ | < | ||
+ | function setup() { | ||
+ | createCanvas(displayWidth, | ||
+ | } | ||
+ | |||
+ | var position | ||
+ | x: 0, | ||
+ | y: 0 | ||
+ | }; | ||
+ | |||
+ | function draw() { | ||
+ | background(100); | ||
+ | circle(position.x, | ||
+ | |||
+ | position.x = position.x + (mouseX - position.x) | ||
+ | position.y = position.y + (mouseY - position.y) | ||
+ | } | ||
+ | </file> | ||
+ | </WRAP> | ||
+ | |||
+ | <WRAP half column> | ||
+ | {{: | ||
+ | </WRAP> | ||
+ | </WRAP> | ||
これらプログラムはどちらもマウスカーソルにくっつくプログラムであり、目的は達成しています。一方で操作して「気持ちいい」、「楽しい」と感じるのはどちらでしょうか? 状況にもよるかもしれませんが、おそらく後者の方が気持ちいい、楽しいと思う人が多いのではないでしょうか?ただ動く、といってもその動きは様々です。まっすぐ移動する円であっても、徐々に早くなって、最後はゆっくり止まるといったある種の無駄な動作が、私達人間にとっては体験の楽しさや興味、注意を引く大きな仕掛けになっていることがあります。これは映像制作に限らずインタラクションでも同じことが言えます。 | これらプログラムはどちらもマウスカーソルにくっつくプログラムであり、目的は達成しています。一方で操作して「気持ちいい」、「楽しい」と感じるのはどちらでしょうか? 状況にもよるかもしれませんが、おそらく後者の方が気持ちいい、楽しいと思う人が多いのではないでしょうか?ただ動く、といってもその動きは様々です。まっすぐ移動する円であっても、徐々に早くなって、最後はゆっくり止まるといったある種の無駄な動作が、私達人間にとっては体験の楽しさや興味、注意を引く大きな仕掛けになっていることがあります。これは映像制作に限らずインタラクションでも同じことが言えます。 | ||
行 143: | 行 168: | ||
上記で記述した自由落下のアルゴリズムを少し変更し,落ち始めの瞬間は一瞬止まったようにみえるが,ちょっと時間が立つと「ストン!」と落ちるようなアニメーションに記述し直してみましょう.ニュートン力学ではなく,トムとジェリー力学ですね(https:// | 上記で記述した自由落下のアルゴリズムを少し変更し,落ち始めの瞬間は一瞬止まったようにみえるが,ちょっと時間が立つと「ストン!」と落ちるようなアニメーションに記述し直してみましょう.ニュートン力学ではなく,トムとジェリー力学ですね(https:// | ||
+ | |||
+ | |||
+ | <WRAP group> | ||
+ | <WRAP half column> | ||
+ | ボールにかかる重力加速度を $a$,ボールの下方向の速度を$V$としたとき | ||
+ | |||
+ | \[ | ||
+ | a = \begin{cases} | ||
+ | 1.0 & V > 0.2 \\ | ||
+ | 0.01 & V \leqq 0.2 | ||
+ | \end{cases} | ||
+ | \] | ||
+ | |||
+ | として,プログラムを記述すると右図のようなアニメーションに変わります.ボールが下に落ちる速度がある一定の大きさになると,加速度が大きくなり,速度が落ちるとまた加速度が小さくなります.結果として,「落下する」というアニメーションを物理法則にとらわれずに記述することができました. | ||
+ | </ | ||
+ | |||
+ | <WRAP half column> | ||
+ | {{: | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <WRAP center round todo 60%> | ||
+ | 以上の内容を踏まえて, | ||
+ | * 軽いボール | ||
+ | * 重いボール | ||
+ | をそれぞれ物理法則を変えてもいいので,自分なりに表現してみましょう. | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== パーティクル ===== | ||
+ | この動きを一つだけで表現するのではなく、たくさんのオブジェクトに対して共通の動きを適応した代表的な手法にパーティクルがあります。これまでのサンプルを利用して、複数のボールを作成してみます。 | ||
+ | |||
+ | <WRAP group> | ||
+ | <WRAP half column> | ||
+ | < | ||
+ | // ボールをたくさん作るので配列で宣言する | ||
+ | var balls = []; | ||
+ | |||
+ | function setup() { | ||
+ | createCanvas(400, | ||
+ | | ||
+ | // ボールの個数を100個にして、ballsの配列に連想配列を初期化して追加する | ||
+ | for (let i = 0; i < 100; i++) { | ||
+ | balls.push({ | ||
+ | x: random(width), | ||
+ | y: random(height), | ||
+ | v: { | ||
+ | x: 0.0, | ||
+ | y: 0.0, | ||
+ | }, | ||
+ | }); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function draw() { | ||
+ | background(220); | ||
+ | |||
+ | // ボールの個数分の描画処理 | ||
+ | for (const ball of balls) { | ||
+ | let g = 1.0; | ||
+ | ball.x += ball.v.x; | ||
+ | ball.y += ball.v.y; | ||
+ | if (ball.y > 400 - 5) { | ||
+ | ball.v.y = 0.5 * -ball.v.y; | ||
+ | ball.y = 400 - 5; | ||
+ | } | ||
+ | ball.v.y += g; | ||
+ | |||
+ | noStroke(); | ||
+ | fill(0); | ||
+ | circle(ball.x, | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <WRAP half column> | ||
+ | {{: | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | 同じプログラムでもそれがたくさんになると、印象が変わりますね。では次は重力の無い世界のプログラムに変更してみます。ただし重力がないとただそこにとどまるだけのボールになってしまうため、初速度を設定しておきます。初速度はそのボールに設定する最初の速度です。また飛んでいってそのまま見えなくなってしまうとつまらないので、一定期間飛び続けたら自動的に最初に戻るようにします。パーティクルではこのことをライフと読んでおり、パーティクル(粒子)が発生してからなくなるまでの寿命のことを意味します。 | ||
+ | |||
+ | <WRAP group> | ||
+ | <WRAP half column> | ||
+ | < | ||
+ | // ボールをたくさん作るので配列で宣言する | ||
+ | var balls = []; | ||
+ | |||
+ | // 初速度 | ||
+ | var v0 = 0.3; | ||
+ | |||
+ | function setup() { | ||
+ | createCanvas(400, | ||
+ | |||
+ | // ボールの個数を100個にして、ballsの配列に連想配列を初期化して追加する | ||
+ | for (let i = 0; i < 100; i++) { | ||
+ | balls[i] = { | ||
+ | x: width / 2, | ||
+ | y: height / 2, | ||
+ | v: { | ||
+ | x: random(-v0, v0), | ||
+ | y: random(-v0, v0), | ||
+ | }, | ||
+ | life: random(255), | ||
+ | }; | ||
+ | } | ||
+ | |||
+ | // 色が重なっているときの振る舞いを加算モードにする | ||
+ | // 詳細:https:// | ||
+ | blendMode(ADD); | ||
+ | } | ||
+ | |||
+ | function draw() { | ||
+ | // 画面の描画をすべてなくす | ||
+ | clear(); | ||
+ | |||
+ | // 背景色の描画 | ||
+ | background(0); | ||
+ | |||
+ | // ボールの個数分の描画処理 | ||
+ | for (const ball of balls) { | ||
+ | ball.x += ball.v.x; | ||
+ | ball.y += ball.v.y; | ||
+ | ball.life--; | ||
+ | |||
+ | // 寿命が付きたら最初に戻る | ||
+ | if (ball.life < 0) { | ||
+ | ball.x = width / 2; | ||
+ | ball.y = height / 2; | ||
+ | ball.v.x = random(-v0, v0); | ||
+ | ball.v.y = random(-v0, v0); | ||
+ | ball.life = random(255); | ||
+ | } | ||
+ | |||
+ | noStroke(); | ||
+ | fill(255, 150, 50, ball.life / 10); | ||
+ | circle(ball.x, | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <WRAP half column> | ||
+ | {{ : | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | さて、ここまでパーティクルをコーディングしてみて、パーティクルが炎や雲等の様々なコンピュータグラフィクスに利用されている可能性が理解できたかなと思います。パーティクルはウェブ等で調べても多くヒットするとおもうので、興味を持った人は自学してみてくださいね。 | ||