SFC - デザインとプログラミング 2016
ニュートン力学による動き – Processingで動きを極める!
これまでやってきたように、Processingで位置と運動のベクトル(PVector)を操作することで、アニメーションを作成することができました。単純な動きの場合はこうした座標の足し算引き算で対応可能です。しかし、ここからさらに発展させて、よりリアルな動きを実現しようとすると、限界があります。今回は、単なる座標操作ではなく、運動の背後にある物理的な原理を理解して、その本質に迫ります。
アイザック・ニュートンは、運動の法則を基礎として構築した、力学体系を構築しました(ニュートン力学)。物体の運動や力、質量といったものは、ニュートンの運動の法則によって説明できます。
- 第1法則(慣性の法則): 質点は、力が作用しない限り、静止または等速直線運動する
- 第2法則(ニュートンの運動方程式): 質点の加速度は、そのとき質点に作用する力に比例し、質点の質量に反比例する
- 第3法則(作用・反作用の法則): 二つの質点の間に相互に力が働くとき、質点 2 から質点 1 に作用する力と、質点 1 から質点 2 に作用する力は、大きさが等しく、逆向きである
今回は、このニュートンの運動の法則を援用しながら、様々な動きや力についてProcessingで実装しながら、動きに関する理解を深めていきます。
スライド資料
サンプルコード
位置と速度による運動 – 基本
ParticleVec2 particle; void setup() { //初期設定 size(800, 600); frameRate(60); noStroke(); fill(255); //パーティクルをインスタンス化 particle = new ParticleVec2(); //初期位置を設定 particle.location.set(10, 10); //速度を設定 particle.velocity.set(3, 2); } void draw() { background(0); //パーティクルの位置を更新 particle.update(); //パーティクルを描画 particle.draw(); } //パーティクルクラス class ParticleVec2 { PVector location; //位置 PVector velocity; //速度 float radius; //パーティクルの半径 //コンストラクター ParticleVec2() { //初期パラメーターを設定 radius = 4.0; location = new PVector(width/2.0, height/2.0); velocity = new PVector(0, 0); } //座標の更新 void update() { location.add(velocity); //速度から位置を算出 } //パーティクルを描画 void draw() { ellipse(location.x, location.y, radius * 2, radius * 2); } }
速度と加速度
ParticleVec2 particle; void setup() { //初期設定 size(800, 600); frameRate(60); noStroke(); fill(255); //パーティクルをインスタンス化 particle = new ParticleVec2(); //初期位置を設定 particle.location.set(10, 10); //力を加える particle.acceleration.set(3, 2); } void draw() { background(0); //パーティクルの位置を更新 particle.update(); //パーティクルを描画 particle.draw(); } //パーティクルクラス class ParticleVec2 { PVector location; //位置 PVector velocity; //速度 PVector acceleration; //加速度 float radius; //パーティクルの半径 //コンストラクター ParticleVec2() { //初期パラメーターを設定 radius = 4.0; location = new PVector(width/2.0, height/2.0); velocity = new PVector(0, 0); acceleration = new PVector(0, 0); } //座標の更新 void update() { velocity.add(acceleration); //速度に加速度を加算 location.add(velocity); //速度から位置を算出 acceleration.set(0, 0); //加速度をリセット } //パーティクルを描画 void draw() { ellipse(location.x, location.y, radius * 2, radius * 2); } }
摩擦力を加える
ParticleVec2 particle; void setup() { //初期設定 size(800, 600); frameRate(60); noStroke(); fill(255); //パーティクルをインスタンス化 particle = new ParticleVec2(); //初期位置を設定 particle.location.set(10, 10); //摩擦力を設定 particle.friction = 0.01; //力を加える particle.acceleration.set(3, 2); } void draw() { background(0); //パーティクルの位置を更新 particle.update(); //パーティクルを描画 particle.draw(); } //パーティクルクラス class ParticleVec2 { PVector location; //位置 PVector velocity; //速度 PVector acceleration; //加速度 float radius; //パーティクルの半径 float friction; //摩擦 //コンストラクター ParticleVec2() { //初期パラメーターを設定 radius = 4.0; location = new PVector(width/2.0, height/2.0); velocity = new PVector(0, 0); acceleration = new PVector(0, 0); friction = 0.0; } //座標の更新 void update() { velocity.add(acceleration); //速度に加速度を加算 velocity.mult(1.0 - friction); //摩擦力を加味した速度を計算 location.add(velocity); //速度から位置を算出 acceleration.set(0, 0); //加速度をリセット } //パーティクルを描画 void draw() { ellipse(location.x, location.y, radius * 2, radius * 2); } }
重力を加える
ParticleVec2 particle; void setup() { //初期設定 size(800, 600); frameRate(60); noStroke(); fill(255); //パーティクルをインスタンス化 particle = new ParticleVec2(); particle.location.set(10, height/2); //初期位置を設定 particle.gravity.set(0, 1); //重力を設定 particle.friction = 0.01; //摩擦力を設定 particle.mass = 1.0; //質量を設定 //力を加える particle.addForce(new PVector(20, -20)); } void draw() { background(0); particle.update(); //パーティクルの位置を更新 particle.draw(); //パーティクルを描画 } //パーティクルクラス class ParticleVec2 { PVector location; //位置 PVector velocity; //速度 PVector acceleration; //加速度 PVector gravity; //重力 float radius; //パーティクルの半径 float friction; //摩擦 float mass; //質量 //コンストラクター ParticleVec2() { //初期パラメーターを設定 radius = 4.0; location = new PVector(width/2.0, height/2.0); velocity = new PVector(0, 0); acceleration = new PVector(0, 0); gravity = new PVector(0, 0); friction = 0.0; mass = 1.0; } //座標の更新 void update() { velocity.add(acceleration); //速度に加速度を加算 velocity.add(gravity); //重力を加える velocity.mult(1.0 - friction); //摩擦力を加味した速度を計算 location.add(velocity); //速度から位置を算出 acceleration.set(0, 0); //加速度をリセット } //力を加える関数 void addForce(PVector force) { //質量から加速度を計算 (a = f/m); force.div(mass); acceleration.add(force); } //パーティクルを描画 void draw() { ellipse(location.x, location.y, radius * 2, radius * 2); } }
壁でバウンドさせる
ParticleVec2 particle; void setup() { //初期設定 size(800, 600); frameRate(60); noStroke(); fill(255); //パーティクルをインスタンス化 particle = new ParticleVec2(); particle.location.set(10, height/2); //初期位置を設定 particle.gravity.set(0, 1); //重力を設定 particle.friction = 0.01; //摩擦力を設定 particle.mass = 1.0; //質量を設定 //力を加える particle.addForce(new PVector(20, -20)); } void draw() { background(0); //パーティクルの位置を更新 particle.update(); //壁でバウンドさせる particle.bounceOffWalls(); //パーティクルを描画 particle.draw(); } //パーティクルクラス class ParticleVec2 { PVector location; //位置 PVector velocity; //速度 PVector acceleration; //加速度 PVector gravity; //重力 float radius; //パーティクルの半径 float friction; //摩擦 float mass; //質量 //コンストラクター ParticleVec2() { //初期パラメーターを設定 radius = 4.0; location = new PVector(width/2.0, height/2.0); velocity = new PVector(0, 0); acceleration = new PVector(0, 0); gravity = new PVector(0, 0); friction = 0.0; mass = 1.0; } //座標の更新 void update() { velocity.add(acceleration); //速度に加速度を加算 velocity.add(gravity); //重力を加える velocity.mult(1.0 - friction); //摩擦力を加味した速度を計算 location.add(velocity); //速度から位置を算出 acceleration.set(0, 0); //加速度をリセット } //力を加える関数 void addForce(PVector force) { //質量から加速度を計算 (a = f/m); force.div(mass); acceleration.add(force); } //壁でバウンドさせる void bounceOffWalls() { if (location.x < 0 || location.x > width) { velocity.x *= -1; } if (location.y < 0 || location.y > height) { velocity.y *= -1; } //画面からはみ出さないように画面内に限定 location.x = constrain(location.x, 0, width); location.y = constrain(location.y, 0, height); } //パーティクルを描画 void draw() { ellipse(location.x, location.y, radius * 2, radius * 2); } }
大量の物体を動かす
int NUM = 1000; ParticleVec2[] particles = new ParticleVec2[NUM]; void setup() { //初期設定 size(800, 600, P3D); frameRate(60); noStroke(); fill(255); for (int i = 0; i < NUM; i++) { //パーティクルをインスタンス化 particles[i] = new ParticleVec2(); //初期位置を設定 particles[i].position.set(width/2, height/4); particles[i].gravity.set(0, 0.2); //摩擦力を設定 particles[i].friction = 0.005; //力を加える particles[i].mass = 1.0; particles[i].addForce(new PVector(random(-10, 10), random(-10, 10))); } } void draw() { background(0); for (int i = 0; i < NUM; i++) { //パーティクルの位置を更新 particles[i].update(); //壁でバウンドさせる particles[i].bounceOffWalls(); //パーティクルを描画 particles[i].draw(); } } //パーティクルクラス class ParticleVec2 { PVector position; //位置 PVector velocity; //速度 PVector acceleration; //加速度 PVector gravity; //重力 float radius; //パーティクルの半径 float friction; //摩擦 float mass; //質量 //コンストラクター ParticleVec2() { //初期パラメーターを設定 radius = 4.0; position = new PVector(width/2.0, height/2.0); velocity = new PVector(0, 0); acceleration = new PVector(0, 0); gravity = new PVector(0, 0); friction = 0.0; mass = 1.0; } //座標の更新 void update() { velocity.add(acceleration); //速度に加速度を加算 velocity.add(gravity); //重力を加える velocity.mult(1.0 - friction); //摩擦力を加味した速度を計算 position.add(velocity); //速度から位置を算出 acceleration.set(0, 0); //加速度をリセット } //力を加える関数 void addForce(PVector force) { //質量から加速度を計算 (a = f/m); force.div(mass); acceleration.add(force); } //壁でバウンドさせる void bounceOffWalls() { if (position.x < 0 || position.x > width) { velocity.x *= -1; } if (position.y < 0 || position.y > height) { velocity.y *= -1; } //画面からはみ出さないように画面内に限定 position.x = constrain(position.x, 0, width); position.y = constrain(position.y, 0, height); } //パーティクルを描画 void draw() { ellipse(position.x, position.y, radius * 2, radius * 2); } }
円形にランダムに拡がるように / マウスクリックでリセット
int NUM = 5000; ParticleVec2[] particles = new ParticleVec2[NUM]; void setup() { //初期設定 size(800, 600, P3D); frameRate(60); noStroke(); fill(255); for (int i = 0; i < NUM; i++) { //パーティクルをインスタンス化 particles[i] = new ParticleVec2(); //初期位置を設定 particles[i].position.set(width/2, height/4); //重力を設定 particles[i].gravity.set(0, 0.1); //摩擦力を設定 particles[i].friction = 0.002; //質量を設定 particles[i].mass = 1.0; //半径を設定 particles[i].radius = 1.5; //円形にランダムになるよう力を加える float length = random(10); float angle = random(2*PI); particles[i].addForce(new PVector(length*cos(angle), length*sin(angle))); } background(0); } void draw() { //背景をフェード fill(0,31); rect(0, 0, width, height); //パーティクルを描画 fill(255); for (int i = 0; i < NUM; i++) { //パーティクルの位置を更新 particles[i].update(); //壁でバウンドさせる particles[i].bounceOffWalls(); //パーティクルを描画 particles[i].draw(); } } void mouseReleased() { for (int i = 0; i < NUM; i++) { //パーティクルをインスタンス化 particles[i] = new ParticleVec2(); //初期位置を設定 particles[i].position.set(mouseX, mouseY); //重力を設定 particles[i].gravity.set(0, 0.1); //摩擦力を設定 particles[i].friction = 0.002; //質量を設定 particles[i].mass = 1.0; //半径を設定 particles[i].radius = 1.5; //円形にランダムになるよう力を加える float length = random(10); float angle = random(2*PI); particles[i].addForce(new PVector(length*cos(angle), length*sin(angle))); } } //パーティクルクラス class ParticleVec2 { PVector position; //位置 PVector velocity; //速度 PVector acceleration; //加速度 PVector gravity; //重力 float radius; //パーティクルの半径 float friction; //摩擦 float mass; //質量 //コンストラクター ParticleVec2() { //初期パラメーターを設定 radius = 2.0; position = new PVector(width/2.0, height/2.0); velocity = new PVector(0, 0); acceleration = new PVector(0, 0); gravity = new PVector(0, 0); friction = 0.0; mass = 1.0; } //座標の更新 void update() { velocity.add(acceleration); //速度に加速度を加算 velocity.add(gravity); //重力を加える velocity.mult(1.0 - friction); //摩擦力を加味した速度を計算 position.add(velocity); //速度から位置を算出 acceleration.set(0, 0); //加速度をリセット } //力を加える関数 void addForce(PVector force) { //質量から加速度を計算 (a = f/m); force.div(mass); acceleration.add(force); } //壁でバウンドさせる void bounceOffWalls() { if (position.x < 0 || position.x > width) { velocity.x *= -1; } if (position.y < 0 || position.y > height) { velocity.y *= -1; } //画面からはみ出さないように画面内に限定 position.x = constrain(position.x, 0, width); position.y = constrain(position.y, 0, height); } //パーティクルを描画 void draw() { rectMode(CENTER); rect(position.x, position.y, radius*2, radius*2); rectMode(CORNER); } }