yoppa.org


人工言語入門 A 2010

Processingによる高度なモーション:バネの動きを作る

今日の内容

  • 高度なアニメーション表現を身につける
  • 物理法則を用いたアニメーションの表現
    • ばね
    • 万有引力

「ばね」を定義する

「ばね」を数学的に定義する

  • 「ばね」を数学的に定義する
  • フックの法則
    • バネの伸びが x のとき、それによって生じる力を F とすると
    • F = -kx ( k:ばね定数)


「ばね」の公式

  • フックの定理は以下のように書き直すことができる
    • springForce = -stiffness * stretch
    • バネの力 = -バネの硬さ x バネの伸び
  • さらに「バネの伸び」の部分を解釈する
    • springForce = -stiffness * (position – restPosition)
    • バネの力 = -バネの硬さ x (現在位置 – ばねの静止位置)
  • 式を整理
    • springForce = stiffness * (restPositon – position)
    • バネの力 = バネの硬さ x (静止位置 – 現在位置)
  • この時、ばねの移動速度は下記のように表現される
    • velocity = friction * (velocity + springFroce)
    • 速度 = 摩擦 * (速度 + バネの力)
  • この式を利用して、簡単なアニメーションをProcessingで作成してみる

ばね1:シンプルな「ばね」

  • 「ばね」の公式(フックの法則)を利用して、シンプルな「ばね」の動きをプログラミングしてみる
float stiffness = 0.1; //ばねの強度
float damping = 0.9; //摩擦
float velocity = 0.0; //速度
float targetY; //目標位置
float y; //現在位置

void setup() {
    size(400, 400);
    noStroke();
}

void draw() {
    fill(0, 12);
    rect(0, 0, width, height);
    fill(255);
    float force = stiffness * (targetY - y); //フックの法則 f = -kx
    velocity = damping * (velocity + force); //速度を計算
    y += velocity; //速度分を座標に
    rect(10, y, width - 20, 10); //四角を描画
    targetY = mouseY; //マウスのY座標を目標位置に
}

ばね2:重さ(mass)を付加

  • もうひとつの要素を付加する – バネに付いている物体の重さ(mass)
  • 重さと力の関係はニュートンの運動の第二法則から導きだされる
    • F = ma
    • F:力、m:質量、a:加速度
  • 式を変換
    • a = F / m
  • これを「ばね」にあてはめると、下記のように解釈できる
    • acceleration = springForce / mass
    • 加速 = バネの力 / 重さ
  • 先程のプログラムに重さの要素を付加する
  • 重さの概念を追加した「ばね」
//2つの重さの異なる物体で、ばね運動を作成
float y1, y2; //現在位置
float velocity1, velocity2; //速度
float mass1 = 1.0; //物体1の重さ
float mass2 = 6.0; //物体2の重さ
float stiffness = 0.1; //バネの剛性
float damping = 0.9; //摩擦係数

void setup() {
    size(400, 400);
    noStroke();
}

void draw() {
    fill(0, 12);
    rect(0, 0, width, height);
    fill(255);
    float targetY = mouseY; //目標位置
    //物体1
    float forceA = stiffness * (targetY - y1); //フックの法則
    float accelerationY1 = forceA / mass1; //重さから加速度を算出
    velocity1 = damping * (velocity1 + accelerationY1); //加速度を加味して速度を計算
    y1 += velocity1; //位置を算出
    rect(0, y1, width/2, 15);
    //物体2	
    float forceB = stiffness * (targetY - y2); //フックの法則
    float accelerationY2 = forceB / mass2; //重さから加速度を算出
    velocity2 = damping * (velocity2 + accelerationY2);//加速度を加味して速度を計算
    y2 += velocity2; //位置を算出
    rect(width/2, y2, width/2, 15);
}

ばね3:2次元座標に展開、重力を付加

  • Y軸方向だけでなくX軸方向にも動けるように
    • 重力(gravity)の要素も付加
    • 常に一定の力をY軸方向に付加する
float stiffness = 0.05;
float damping = 0.9;
float mass = 3.0;
float gravity = 0.0;
float velocityX = 0.0, velocityY = 0.0;
float targetX, targetY;
float x, y;

void setup() {
    size(600, 600);
    smooth();

}

void draw() {
    background(0);

    float forceX = stiffness * (targetX - x);
    float accelerationX = forceX / mass;
    velocityX = damping * (velocityX + accelerationX);
    x += velocityX;

    float forceY = stiffness * (targetY - y);
    forceY += gravity;
    float accelerationY = forceY / mass;
    velocityY = damping * (velocityY + accelerationY);
    y += velocityY;

    noStroke();
    fill(255);
    ellipse(x, y, 40, 40);

    stroke(127);
    noFill();
    line(mouseX, mouseY, x, y);

    targetX = mouseX;
    targetY = mouseY;
}

ばね4:「ばね」をクラスとして定義する

  • ばねの動きを汎用的に使えるようにクラスとして定義する
    • Spring2Dクラス
  • メインプログラム
//Spring2Dクラスを利用して、ばねの動きを生成
Spring2D s1, s2;
float gravity = 15.0;
float mass = 2.0;

void setup() {
    size(400, 400);
    smooth();
    fill(0);
    //入力:x座標, y座標, 摩擦, 重力
    s1 = new Spring2D(0.0, width / 2, mass, gravity);
    s2 = new Spring2D(0.0, width / 2, mass, gravity);
}

void draw() {
    background(204);
    s1.update(mouseX, mouseY);
    s1.display(mouseX, mouseY);
    s2.update(s1.x, s1.y);
    s2.display(s1.x, s1.y);
}
  • 「ばね」を定義するクラス:Spring2D
class Spring2D {
    float vx, vy;
    float x, y;
    float gravity;
    float mass;
    float radius = 10;
    float stiffness = 0.2;
    float damping = 0.7;
    //コンストラクタ
    Spring2D(float xpos, float ypos, float m, float g) {
	x = xpos;
	y = ypos;
	mass = m;
	gravity = g;
    }
    //位置を計算
    void update(float targetX, float targetY) {
	float forceX = (targetX - x) * stiffness;
	float ax = forceX / mass;
	vx = damping * (vx + ax);
	x += vx;
	float forceY = (targetY - y) * stiffness;
	forceY += gravity;
	float ay = forceY / mass;
	vy = damping * (vy + ay);
	y += vy;
    }
    //表示
    void display(float nx, float ny) {
	noStroke();
	ellipse(x, y, radius*2, radius*2);
	stroke(255);
	line(x, y, nx, ny);
    }
}

ばね5:ばねを数珠繋ぎにしてみる

  • Spring2Dクラスで定義した「ばね」を直列に繋いでいく
int numSprings = 10;
Spring2D[] s = new Spring2D[numSprings];
float gravity = 5.0;
float mass = 5.0;
float stiffness = 0.2;
float damping = 0.8;

void setup() {
    size(600, 600);
    smooth();
    fill(0);
    for (int i = 0; i < numSprings; i++) {
	s[i] = new Spring2D(width / 2, i*(height / numSprings), mass, gravity, stiffness, damping);
    }
}

void draw() {
    background(204);
    s[0].update(mouseX, mouseY);
    s[0].display(mouseX, mouseY);
    for (int i = 1; i < numSprings; i++) {
	s[i].update(s[i-1].x, s[i-1].y);
	s[i].display(s[i-1].x, s[i-1].y);
    }
}

  • Morizono

    楽しく勉強させていただいてます。
    「ばね5」では Spring2D のクラス定義を少し変更しないといけませんね。コンストラクタの引数にstiffnessとdampingを追加する必要があります。