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);
}
}