今回でこの講座は終わりです。先週作り始めたブロック崩し風ゲームを完成させましょう。
この手のゲームで必要になることは、ボールがブロックの当たったときにブロックの消す処理です。このサンプルプログラムではブロックを3個用意し、それぞれの当たりを判定する変数をblock1_hit, block2_hit, block3_hitとして0で初期化します。
ボールとブロックが衝突したらその変数を1に設定し、そのブロックを破壊し表示しないという処理を行っています。
// step6.cpp int block1_hit = 0, block2_hit = 0, block3_hit = 0; // ブロックにボールが衝突すれば1、しないと0 /*** シミュレーションループ ***/ void simLoop(int pause) { int i, j; dmWorldQuickStep(); // シミュレーションを1ステップ進める(高速版) dmDraw(&ball); dmDraw(&bar); dmDraw(&field); for (i =0; i < 3; i++) { dmDraw(&fence[i]); } // ブロックがボールに衝突していない場合はブロックを表示し // ブロックがボールに衝突した場合はブロックを破壊する。 if (block1_hit==0) dmDraw(&block1); else dmDestroyBox0(&block1); if (block2_hit==0) dmDraw(&block2); else dmDestroyBox0(&block2); if (block3_hit==0) dmDraw(&block3); else dmDestroyBox0(&block3); STEPS++; }
次に、今まで秘密のベールに包まれていたdm6.cppを説明します。このファイルでODEを簡単に使う関数を作っています。まず、2,3行目のexternで始まる変数の宣言は、dm6.cpp以外のファイルで宣言しているよとコンパイラーに教えています。これらの行がないとblock1, block1_hitが宣言されていないとエラーになり、externを付けないとstep6.cppでも宣言されているので2重定義でエラーになってしまいます。大きなプログラムは複数のファイルで構成されるのでこのexternは覚えておきましょう。
お次は、衝突検出を説明します。これはdm6.cpp内のnearCallback関数で行われています。if else文でブロック1のジオメトリ(block1.geom)、ブロック2のジオメトリ(block2.geom)、ブロック3のジオメトリ(block3.geom)がボールのジオメトリ(ball.geom)がo1, o2のどちらかなら衝突しているので、衝突判定用の変数block1_hit, block2_hit, block3_hitに1を代入しています。なお、ジオメトリとはODEで衝突判定に使われる物体の属性です。
また、nearCallback関数は2つの物体(正確にはジオメトリ)が接近して衝突しそうになったら自動で呼び出せれる関数です。このような関数をコールバック関数と呼びます。電話のコールバックをイメージしてください。引数のジオメトリo1, o2は衝突しそうな2つのジオメトリです。
このサンプルプログラムでたった3個のブロック崩し風ゲームは完成です。後は、いろいろ工夫して自分だけのゲームを完成させてください。
終わり。
// dm6.cpp extern dmObject ball, field, fence[3], bar, block1, block2, block3; extern int block1_hit, block2_hit, block3_hit; /*** 衝突検出関数(コールバック関数) ***/ void nearCallback(void *data, dGeomID o1, dGeomID o2) { static const int N = 10; // 接触点数の最大値 dContact contact[N]; // 接触点 // 衝突情報の生成 nは衝突点数 int n = dCollide(o1,o2,N,&contact[0].geom,sizeof(dContact)); for (int i = 0; i < n; i++) { contact[i].surface.mode = dContactBounce; // 接触面の反発性を設定 // ボールとバーが接触したら反発係数を1.5に設定 if(((ball.geom == o1) && (bar.geom == o2)) || ((ball.geom == o2) && (bar.geom == o1))) { contact[i].surface.bounce = 1.5; // 反発係数(実際の世界では0.0から1.0) } // ボールと床が接触したら反発係数を0.5に設定 else if (((ball.geom == o1) && (field.geom == o2)) || ((ball.geom == o2) && (field.geom == o1))) { contact[i].surface.bounce = 0.5; } // ボールとブロック1が接触したら。block1_hitを1に設定 else if (((ball.geom == o1) && (block1.geom == o2)) || ((ball.geom == o2) && (block1.geom == o1))) { block1_hit = 1; } // ボールとブロック2が接触したら。block2_hitを1に設定 else if (((ball.geom == o1) && (block2.geom == o2)) || ((ball.geom == o2) && (block2.geom == o1))) { block2_hit = 1; } // ボールとブロック3が接触したら。block2_hitを1に設定 else if (((ball.geom == o1) && (block3.geom == o2)) || ((ball.geom == o2) && (block3.geom == o1))) { block3_hit = 1; } // その他は反発係数を0.8に設定 else { contact[i].surface.bounce = 0.8; } // contact[i].surface.bounce_vel = 0.05; // 反発に必要な最低速度 contact[i].surface.mu = 0.0;// dInfinity; // 摩擦係数 // 接触ジョイントの生成 dJointID c = dJointCreateContact(world,contactgroup, &contact[i]); // 接触している2つの剛体を接触ジョイントにより拘束 dJointAttach(c,dGeomGetBody(contact[i].geom.g1), dGeomGetBody(contact[i].geom.g2)); } }
ホームワーク
- step6-120119.zipをダウンロードして実行しよう!
- ブロックの数を1個増やそう。
- 3で作ったブロックの色を黒に変更しよう。
- ブロックの数を増やし、競技盤の半分ぐらいは埋め尽くそう。配列を使うのが望ましい。
- ボールの初期位置と飛び出す方向をランダムに変えよう。
- バーが競技盤を飛び出してしまいます。飛び出さないようにしよう。
- 得点を表示する機能を付けよう。標準出力にprintfでもかまいません。
- いろいろ工夫して自分だけのブロック崩し風ゲームを作ろう!遊ぶためにゲームの説明を文字ウインドウに表示しよう。
コメント