物体の運動

12月 11th, 2011

Processing Advent Calendar 2011(11日目)

物体の運動

今回はモノの動かし方について簡単にまとめようと思いますので、 ボール(「物体」だと表現が硬いので、ここではボールとします)の運動について書きます。

横方向の移動から始まり、最後にはボールのバウンドの方法までをそれぞれ実装します。

等速直線運動

“等速直線運動”と感じが並ぶと物理の実験が始まってしまいそうですが、言っていることは簡単で「ボールが同じ速さでまっすぐに動く」ということです。
Processingでは、ボールを描く位置を毎回同じ分だけ変えて行けば表現できます。

アプレットはこちら

float halfWidth;
final int BALL_SIZE = 20;  //  ボールの大きさ
float y = 0;  //  ボールを描く位置
 
void setup(){
  size(100, 400);
  smooth();
  halfWidth = width / 2;  //  縦の真ん中を計算する
}
 
void draw(){
  background(0);
 
  //  ボールの位置を動かす
  y += 3;
 
  //  ボールが右端に達したら左端に戻す
  if(y > height){
    y = 0;
  }
 
  //  ボールを描く
  ellipse(halfWidth, y, BALL_SIZE, BALL_SIZE);
}

減速

実際の世界では、等速直線運動は、基本的には起こりません。
なぜならば、摩擦や空気の抵抗などによって、徐々に速度が落ちるからです。

このような”減速”を表現するには、変数として”位置”の他に”速度”を用意する必要があります。
等速直線運動では”位置”の変数を変えていましたが、減速では”速度”の変数を変えてゆき、”位置”には”速度”を加えて行きます。

アプレットはこちら

float halfWidth;
final int BALL_SIZE = 20;
final float DECCELE_SPEED = -0.05; //  減速度
final float INIT_SPEED = 6;      //  初めの速度
 
float y = 0;            //  ボールの位置
float vy = INIT_SPEED;  //  ボールの速度
 
void setup(){
  size(100, 400);
  smooth();
  halfWidth = width / 2;
}
 
void draw(){
  background(0);
 
  //  ボールの位置を動かす
  vy += DECCELE_SPEED;  //  速度を変化させる
  y += vy;              //  位置には速度を加える
 
  //  速度が0になったら最初からやり直す
  if(vy < 0){
    y = 0;
    vy = INIT_SPEED;
  }
 
  //  ボールを描く
  ellipse(halfWidth, y, BALL_SIZE, BALL_SIZE);
}

加速

加速は、減速と同じように、”位置”に”速度”を加えます。
減速では”速度”を減らしていったのに対し、”加速”は速度を増やしていきます。

アプレットはこちら

float halfWidth;
final int BALL_SIZE = 20;
final float ACCELE_SPEED = 0.05; //  加速度
final float INIT_SPEED = 0;      //  初めの速度
 
float y = 0;            //  ボールの位置
float vy = INIT_SPEED;  //  ボールの速度
 
void setup(){
  size(100, 400);
  smooth();
  halfWidth = width / 2;
}
 
void draw(){
  background(0);
 
  //  ボールの位置を動かす
  vy += ACCELE_SPEED;   //  速度を変化させる
  y += vy;              //  位置には速度を加える
 
  //  ボールが右端に達したら左端に戻す
  if(y > height + BALL_SIZE){
    y = 0;
    vy = INIT_SPEED;
  }
 
  //  ボールを描く
  ellipse(halfWidth, y, BALL_SIZE, BALL_SIZE);
}

比較

上記三つの動き(等速直線運動・減速・加速)に軌跡をつけて、1つにまとめてみました。

アプレットはこちら

final int BALL_SIZE = 20;  //  ボールの大きさ
final float DECCELE_SPEED = -0.015; //  減速度
final float ACCELE_SPEED = 0.04; //  加速度
final float INIT_SPEED = 3;      //  初速度
float y1 = 0;  //  ボールを描く位置
float y2 = 0;  //  ボールを描く位置
float y3 = 0;  //  ボールを描く位置
float vy2 = INIT_SPEED;  //  ボールの速度
float vy3 = INIT_SPEED;  //  ボールの速度
boolean live1 = true;           //  等速ボールの動作中フラグ
boolean live2 = true;           //  減速ボールの動作中フラグ
boolean live3 = true;           //  加速ボールの動作中フラグ
 
void setup(){
  size(300, 400);
  smooth();
  noStroke();
}
 
void draw(){
  fill(0, 24);
  rect(0, 0, width, height);
  fill(255);
 
  draw1();
  draw2();
  draw3();
 
  //  3つのボールの動作が完了したら、それぞれやりなおす
  if(!(live1 || live2 || live3)){
    y1 = 0;
    live1 = true;
 
    y2 = 0;
    vy2 = INIT_SPEED;
    live2 = true;
 
    y3 = 0;
    vy3 = INIT_SPEED;
    live3 = true;
  }
}
 
//  等速直線運動
void draw1(){
  if(live1){
    //  ボールの位置を動かす
    y1 += INIT_SPEED;
 
    //  ボールが右端に達したら動作終了とする
    if(y1 > height){
      live1 = false;
    }
 
    //  ボールを描く
    ellipse(50, y1, BALL_SIZE, BALL_SIZE);
  }
}
 
//  減速
void draw2(){
  if(live2){
    //  ボールの位置を動かす
    vy2 += DECCELE_SPEED;  //  速度を変化させる
    y2 += vy2;              //  位置には速度を加える
 
    //  速度が0になったら動作終了とする
    if(vy2 < 0){
      live2 = false;
    }
  }
 
  //  ボールを描く
  ellipse(150, y2, BALL_SIZE, BALL_SIZE);
}
 
//  加速
void draw3(){
  if(live3){
    //  ボールの位置を動かす
    vy3 += ACCELE_SPEED;   //  速度を変化させる
    y3 += vy3;             //  位置には速度を加える
 
    //  ボールが右端に達したら左端に戻す
    if(y3 > height + BALL_SIZE){
      live3 = false;
    }
 
    //  ボールを描く
    ellipse(250, y3, BALL_SIZE, BALL_SIZE);
  }
}

放物運動

今までの内容を組み合わせると放物運動(物を投げた時の動き)を作ることができます。
放物運動は、横方向は等速直線運動(※現実世界では空気によって減速になりますが、微々たるものなのでここでは割愛します) 縦方向は加速になります。この加速は現実世界では重力になります。

アプレットはこちら
最初は、縦方向の速度「vy」は-4なので上に向かって飛んでいきます(Processingでは、下方向がプラス、上方向がマイナスなので。)が、 重力の力によって「vy」が少しずつ増えてゆき、最後には地面に落ちるようになります。

final int BALL_SIZE = 10;
final int BALL_COUNT = 3;          //  ボールの個数
final float GRAVITY_POWER = 0.05;  //  重力の強さ
final float INIT_SPEED = 4;
final float[] INIT_SPEED_X =  {cos(radians(30)) * INIT_SPEED, cos(radians(45)) * INIT_SPEED, cos(radians(60)) * INIT_SPEED};      //  横方向の初めの速度(30度,45度,60度)
final float[] INIT_SPEED_Y = {sin(radians(30)) * -INIT_SPEED, sin(radians(45)) * -INIT_SPEED, sin(radians(60)) * -INIT_SPEED};     //  縦方向の初めの速度(30度,45度,60度)
 
float[] x = new float[BALL_COUNT];   //  ボールの現在の横位置
float[] y = new float[BALL_COUNT];   //  ボールの現在の縦位置
float[] vx = new float[BALL_COUNT]; //  横方向の現在の速度
float[] vy = new float[BALL_COUNT]; //  縦方向の現在の速度
boolean[] live = new boolean[BALL_COUNT];  //  ボールが画面内にあるかどうかのフラグ
 
void setup(){
  size(400, 200);
  smooth();
  noStroke();
 
  //  各ボールの変数の初期化
  for(int i = 0; i < BALL_COUNT; i++){
    x[i] = 0;
    y[i] = height;
    vx[i] = INIT_SPEED_X[i];
    vy[i] = INIT_SPEED_Y[i];
    live[i] = true;
  }
}
 
void draw(){
  //  軌跡を作るために、全体を少し暗くする
  fill(0, 32);
  rect(0, 0, width, height);
 
  fill(255);  //  ボールの色
 
  for(int i = 0; i < BALL_COUNT; i++){
    if(live[i]){
      //  ボールの位置を動かす
      x[i] += vx[i];
      y[i] += vy[i];
 
      //  縦方向には重力を加える
      vy[i] += GRAVITY_POWER;
 
      //  ボールが下方に行ってしまったら死亡フラグを立てる
      if(y[i] > height + BALL_SIZE / 2){
        live[i] = false;
      }else{
        //  ボールを描く
        ellipse(x[i], y[i], BALL_SIZE, BALL_SIZE);
      }
    }
  }
 
  //  全てのボールが画面外に消えたら、ボールを初期位置にセットしなおす
  boolean tmpLive = false;
  for(int i = 0; i < BALL_COUNT; i++){
    tmpLive |= live[i];
  }
  if(!tmpLive){
    for(int i = 0; i < BALL_COUNT; i++){
        x[i] = 0;
        y[i] = height;
        vx[i] = INIT_SPEED_X[i];
        vy[i] = INIT_SPEED_Y[i];
        live[i] = true;
    }
  }
}

バウンド

実際の世界では、地面に落ちたボールは鉄の塊でもない限りはバウンドします。
これは、縦方向の速度にマイナスの数を掛けことで表現できます。
-1なら元の高さまで跳ね返ってきます。-0.1ならほとんど跳ね返ってきません。
実際の世界では、この数のことを「跳ね返り係数」などと呼びます。スーパーボールなどのゴムは高い数値、ボーリングのボールなど固くて重いものは低い数値になります。

アプレットはこちら

final int BALL_SIZE = 20;
final float GRAVITY_POWER = 0.05;  //  重力の強さ
final float INIT_SPEED_X = 1;      //  横方向の初めの速度
final float INIT_SPEED_Y = -3;     //  縦方向の初めの速度
final float RESTITUTION = 0.75;    //  跳ね返り係数
 
float x = 0;
float y = 0;   //  ボールの位置
float vx = INIT_SPEED_X; //  横方向の加速度
float vy = INIT_SPEED_Y; //  縦方向の加速度
 
void setup(){
  size(400, 200);
  smooth();
 
  //  初めの位置に移動する
  x = 0;
  y = height;
}
 
void draw(){
  background(0);
 
  //  ボールの位置を動かす
  x += vx;
  y += vy;
 
  //  縦方向には重力を加える  
  vy += GRAVITY_POWER;
 
  //  バウンド
  if(y > height){
    y = height;
    vy *= -RESTITUTION;
  }
 
  //  ボールが右端に達したら最初に戻す
  if(x > width){
    x = 0;
    y = height;
    vx = INIT_SPEED_X;
    vy = INIT_SPEED_Y;
  }
 
  //  ボールを描く
  ellipse(x, y, BALL_SIZE, BALL_SIZE);
}

Tags:
Posted in Processing Advent Calendar2011 | No Comments »

Comments

Leave a Reply

 Comment Form