| 目次 |
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