//参考コード
/*
12.1 今週の内容
アニメーション
球の衝突判定
*/
//12.2 アニメーション
/*
アニメーションは,簡単に言うと描画を繰り返すことで作成できます.
たとえば,キャラクタが動くアニメーションを作りたい場合には,
キャラクタの描画→キャラクタの位置を少し動かす→キャラクタの描画
という作業を繰り返せばよいことになります.
glutではキー入力やマウス入力などのイベントがない時に呼ばれ続ける関数を
glutIdleFuncに登録すれば,アニメーション処理ができます.
*/
void glutIdleFunc( void (*func)( void ) )
/*
サンプルプログラム
main.h main.cpp
このプログラムは,2つの球のアニメーションを作成します.
簡単のため,xy平面で球を動かします.
変数として,球の速度と位置を用意します.
*/
//main.h
//球の速度
float vx[ 8 ];
float vy[ 8 ];
//球の中心の座標
float px[ 8 ];
float py[ 8 ];
const float dt = 0.01f;
//球の初期位置と初期速度を設定します.
//速度と位置の初期化
void initSphere( void )
{
vx[ 0 ] = 1.f;
vy[ 0 ] = 1.f;
vx[ 1 ] = 0.f;
vy[ 1 ] = 0.f;
px[ 0 ] = 0.f;
py[ 0 ] = 0.f;
px[ 1 ] = 3.f;
py[ 1 ] = 3.f;
}
//glutIdleFuncに渡す関数として以下の関数を用意します.
void update( void )
{
for( int i = 0; i < numSpheres; i++ ) {
px[ i ] += vx[ i ] * dt;
py[ i ] += vy[ i ] * dt;
}
glutPostRedisplay();
}
/*
update関数は,毎ステップ(時間間隔dt)の位置を更新しています.
これによって,青い球が動くアニメーションが作成できます.
今のままでは,青い球と赤い球が衝突してもすり抜けてしまい,不自然です.
*/
//12.3 衝突判定
/*
そこで,球の衝突判定を行い,衝突後の速度を計算する関数を追加します.
2つの球が衝突するかどうかは容易に判定できます.
2つの球の中心同士の距離dが,球の半径の和r1+r2 よりも長ければ衝突していないと判定できます.
(a) d < r1 + r2 (b) d > r1+r2
2つの球が衝突した場合,その後の球の速度は以下のように計算します.
2つの球の中心をP1,P2とし, 球の速度ベクトルをv1,v2とします.
速度ベクトルv1,v2を, 垂直方向(P1P2方向)成分と 接平面方向(P1P2方向に垂直)成分に分解します.
衝突後の速度は,垂直方向(P1P2方向)成分の速度のみが 変化し,接平面方向の速度は維持されます.
垂直方向の速度ベクトルは,速さ(speed)とP1P2方向の 単位ベクトルnによって以下のように記述できます.
衝突前の球1,2の速さと衝突後の球1,2の速さは 運動量保存の法則から以下の式が成り立ちます.
ここで,m1, m2はそれぞれ球1,2の質量とします.
衝突前の速さは既知であり,衝突後の速さを求めたいので,もう1つ関係式が必要です.
衝突前後の相対速度の比は,2つの球の種類によって一定であり,反発係数eで表現されます.
反発係数eは0から1の値をとります.
これらの連立方程式を解くと,衝突後の速さは以下の式で計算されます.
衝突後の垂直方向の速度ベクトルは,衝突後の速さに単位方向ベクトルnをかけたものになり,
接平面方向の速度ベクトルを足すことで衝突後の速度ベクトルは計算されます.
以上の衝突判定および衝突後の速度計算は以下のようにプログラミングできます.
*/
void update( void )
{
for( int i = 0; i < numSpheres; i++ ) {
px[ i ] += vx[ i ] * dt;
py[ i ] += vy[ i ] * dt;
}
for( int i = 0; i < numSpheres; i++ ) {
for( int j = i + 1; j < numSpheres; j++ ) {
if( collisionDetection( i, j ) ) {
calculateCollideVelocity( i, j );
}
}
}
glutPostRedisplay();
}
bool collisionDetection( const int id0, const int id1 )
{
float d;
d = ( px[ id0 ] - px[ id1 ] ) * ( px[ id0 ] - px[ id1 ] ) + ( py[ id0 ] - py[ id1 ] ) * ( py[ id0 ] - py[ id1 ] );
if( d <= ( radius[ id0 ] + radius[ id1 ] ) * ( radius[ id0 ] + radius[ id1 ] ) ) {
return true;
} else {
return false;
}
}
void calculateCollideVelocity( const int id0, const int id1 )
{
float nx, ny, l, vn[ 2 ], vnn[ 2 ];
float vtx[ 2 ], vty[ 2 ];
nx = px[ id1 ] - px[ id0 ];
ny = py[ id1 ] - py[ id0 ];
l = 1.f / ( float ) sqrt( nx * nx + ny * ny );
nx *= l;
ny *= l;
vn[ 0 ] = vx[ id0 ] * nx + vy[ id0 ] * ny;
vn[ 1 ] = vx[ id1 ] * nx + vy[ id1 ] * ny;
vtx[ 0 ] = vx[ id0 ] - vn[ 0 ] * nx;
vty[ 0 ] = vy[ id0 ] - vn[ 0 ] * ny;
vtx[ 1 ] = vx[ id1 ] - vn[ 1 ] * nx;
vty[ 1 ] = vy[ id1 ] - vn[ 1 ] * ny;
vnn[ 0 ] = 1.f / ( mass[ id0 ] + mass[ id1 ] ) * ( ( mass[ id0 ] - ecoef * mass[ id1 ] ) * vn[ 0 ] + mass[ id1 ] * ( 1.f + ecoef ) * vn[ 1 ] );
vnn[ 1 ] = 1.f / ( mass[ id0 ] + mass[ id1 ] ) * ( mass[ id0 ] * ( 1.f + ecoef ) * vn[ 0 ] + ( mass[ id1 ] - mass[ id0 ] * ecoef ) * vn[ 1 ] );
vx[ id0 ] = vnn[ 0 ] * nx + vtx[ 0 ];
vy[ id0 ] = vnn[ 0 ] * ny + vty[ 0 ];
vx[ id1 ] = vnn[ 1 ] * nx + vtx[ 1 ];
vy[ id1 ] = vnn[ 1 ] * ny + vty[ 1 ];
}
/*
これにより,球が衝突すると反発して移動する様子を表現できます.
これを応用すれば壁との反発なども考慮できます.
*/