yoppa.org


SFC - デザインとプログラミング 2016

ニュートン力学による動き – Processingで動きを極める!

p5example161102

これまでやってきたように、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);
  }
}