| 目次 | 1ページ | 2ページ | 3ページ | 4ページ | 5ページ |


クラスの設計 1999/02/27 (初版)  以下では、Windows 95/98 用のグラフィックス・クラスライブラリ GLIBW32 を 使ってコードを書きます。ただし、ここで使うグラフィックス機能は、直線と円の描 画だけです。  まずは、プログラムの概略のスケッチから始めましょう。ボールを何個か生成して それを1秒間隔で次の時刻でのそれぞれのボールの位置を求めて、グラフィックス表 示します。その際に、まずはすべてのボールが衝突しないとして1秒後の位置を求め ボールの位置関係を決定しておき、それから衝突が起こらなかったかを調べ、もし衝 突があればそのボールの1秒後の位置を修正します。  擬似コードで書くと、
// ボールの衝突運動の擬似コード 

    int no; 
    cin >> no; // ボールの個数を入力 

    no 個のボールを初期化する 
    (おそらくここでボールを表すクラスのオブジェクトを生成する) 

    while( 終了する条件が充たされるまで ){ 

        for(int i = 0; i < no; ++i){ 

            まずボールは衝突しないとして次の位置を求める 
        } 

        for(int i = 0; i < no; ++i){ 

            衝突の有無を調べてもし衝突するならば次の位置を修正する 
        } 

        すべてのボールの描画を1秒後のものに更新する 

    } 
といったものになるでしょう。  では、このコードを実現するような、ボールを描くクラスの設計を始めます。 ボールを表現するのに必要な属性を表すデータメンバーと、データの値を変化 させるためのメンバー関数を用意します。データメンバーとは、ここではボー ルの質量、半径、色、位置、速度などです。これらは private メンバーにし て外から見えないように隠蔽します。  メンバー関数は、上の擬似コードにある、3つの動作です。衝突なしのボー ルの変化、衝突によるボールの変化、ボールの描画です。メンバー関数を publicインターフェイスに置くことで、これらの関数を通じてデータメンバー にアクセスすることができます。それが次の Ball クラスです。
class Ball{ 

public: 
    Ball();                // デフォルト・コンストラクタ 
    ~Ball() {--count;}     // デストラクタ 
    void draw(GRAPH *);    // ボールの描画 
    void non_coll();       // 衝突しないボールの次の位置を計算 
    void collision_w();    // 壁との衝突を調べ次の位置を計算 
    void collision(Ball &); // ボール同士の衝突を調べ次の位置を計算 

private: 
    static int count; // ボールの個数 
    int x_size;        // ウィンドウサイズ(x方向) 
    int y_size;        // ウィンドウサイズ(y方向) 
    int coll;          // 次の1秒間に他のボールと衝突するかどうか 
    int color;         // ボールの色 
    double x;         // ボールのx座標 
    double y;         // ボールのy座標 
    double v;         // ボールの速度 
    double angle;     // ボールの運動方向(度) 
    double r;         // ボールの半径 
    double m;         // ボールの質量 
    double x_next;    // ボールの次のx座標 
    double y_next;    // ボールの次のy座標 
    double dt;        // 時間間隔(次のステップまでの時間) 
    // ユーティリティ関数 
    int inside(double, double) const; // 領域の判定(0:外、1:内)   
}; 
 メンバー関数で重要なのは、衝突の扱いです。詳しくはソースコードを見てもらうこと にして、ここではアウトラインだけを述べておきます。  まず最初に、オブジェクトの個数 count の初期化を行います。 int Ball::count = 0; // 静的(static)データメンバーの初期化 非 satic データメンバーはクラスの各オブジェクトごとに生成されますが、static デー タメンバーはすべてのオブジェクトで共有され、その記憶場所は1つだけです。この場合 ボールの個数を static とするのは、自然なことです。またsatic データメンバーはクラ ス定義の外で初期化しなければならないことに注意しましょう(グローバルデータにする ため)。  次にデフォルトコンストラクタ Ball::Ball()でデータメンバーの初期化を行います。 ここでは乱数で与えることにしています(ソースを参照)。
// 衝突しない場合の次の位置を計算する
void Ball::non_coll() 
{ 
    x_next = x + v*cos(PI*angle/180.0)*dt;          
    y_next = y + v*sin(PI*angle/180.0)*dt;          
}
ここで、PIは円周率です。  壁との衝突の場合を、擬似コードを交えて示したのが次のものです。
// 壁との衝突
void Ball::collision_w() 
{ 
    double x_min = r, x_max = x_size - r; 
    double y_min = r, y_max = y_size - r; 

// 2回衝突(壁の角で2回衝突する場合) 
// 左下の角での衝突 
    if( ( x_next <= x_min )  && ( y_next <= y_min ) ){ 
        // 衝突時刻  
        double tx = ( x - x_min) / (x - x_next); 
        double ty = ( y - y_min) / (y - y_next); 
        // 次の位置 
        x_next = x_min - dt*(1.0-tx)*v*cos(PI*angle/180.0); 
        y_next = y_min - dt*(1.0-ty)*v*sin(PI*angle/180.0); 
        angle = 180.0 + angle; 
    } 
    if( 右下の角で衝突 ){ 
        // 次の位置を求める  
    } 

    if( 左上の角で衝突 ){ 
        // 次の位置を求める  
    } 

    if( 右上の角で衝突 ){ 
        // 次の位置を求める  
    } 

// 1回衝突 
// 左壁と衝突 
    if( ( x_next <= x_min )  && ( x >= x_min ) ){ 
        // 衝突時刻 
        double t = ( x - x_min) / (x - x_next); 
        angle = 180.0 - angle; 
        x_next = x_min + dt*(1.0-t)*v*cos(PI*angle/180.0); 
    } 

    if( 右壁と衝突 ){ 
        // 次の位置を求める  
    } 

    if( 上の壁と衝突 ){ 
        // 次の位置を求める  
    } 

    if( 下の壁と衝突 ){ 
        // 次の位置を求める  
    } 
}
この関数では、他のボールと衝突した直後に壁と衝突する場合も考慮されています。 ボールとの衝突がないときは、dtは1.0(秒)ですが、他のボールと衝突してい る場合は、dtは衝突後から次のステップ(1秒)までの時間間隔となります。
// ボール同士の衝突
void Ball::collision(Ball &B) 
{ 
    if( this == &B) return;// 相手は自分自身 
    if( coll == 1 ) return; // すでに他のボールと衝突した 

    重心と重心速度を求める 

    if( 重心の速さ < ZERO ) return; 

    重心系に変換し、衝突径数と衝突距離を求める 

    // 離れていて衝突できない 
    if( 衝突径数 >= 衝突半径 ) return; 

    衝突位置と時刻を計算する 

    if( 衝突時刻は次の1秒まで以外でおきた ) return; 

    実験室系に戻る 

  if( 衝突位置は領域外にある ) return; // 残念 

   // ここまでくれば衝突していることになる 

   2つのボールの現在の位置を衝突した位置に更新する 

    dt = 1.0 - 衝突時刻;//このあとで壁との衝突を調べるため     

   2つのボールの次の位置を衝突後の位置に更新する 

    coll = 1; 
}
1行目のコード、 if( this == &B) return;// 相手は自分自身 に注意しましょう。衝突の相手が自分でないことをチェックしています。自分自 身のオブジェクトのアドレス( this ポインタ)と相手のオブジェクトのアド レスを比較して等しくないことを確かめます。 (注)この関数では、各ボールは1つのボールとだけ衝突するものとして計算して います。3つ以上のボールが同時に衝突するような効果は入れていません。これ はボールの個数かそれほど多くはない(粒子数密度が小さい)とした場合は充分 良く記述できると考えられます。


ソース・ファイルはこちらです。 BALL.CPP (10KB) または、HTMLファイルで見る場合ははこちら  実行ファイルとソース・ファイルのダウンロードはこちらです。
ボールの衝突シミュレーション(1)
   実行ファイルとソース・ファイル
   Windows 95/ 98上で動作します。
  ダウンロード
  ball_11.lzh (52KB)
 * このソフトウェアは、SYMANTEC. NORTON AntiVirus 5.0 でウィルス検査を   行っています。  * ファイルは吉崎栄泰氏による LHA (Copyright(c)1988-92 H.Yoshizaki)を   使って圧縮しています。

目次 前のページへ 次のページへ

Copyright(c) 1999 Yamada,K