ODEで学ぶC言語の4回目です.ODEの物理計算の部分は使わず,drawstuffを使った表示だけにします.今回は前回までの演習を構造体を使い実装しています.
まず,10行目から18行目で構造体myObjectを定義しています.メンバとしては,位置p[3],回転行列R[12],質量m,半径r,サイズside[3],色*colorなどの変数です.
Step3との大きな違いは93行目のinitObject関数でゲームに使う物体を初期化しています.前回の演習で各ブロック色を割り当てる問題がありましたが,この関数でそれを解決しています.
また,前回のもう一つの課題であったCollision関数は136から148行目にかけて定義しています.ここで,block[i].p[j]はブロック(i番目)の位置(j座標)という意味です.nowは現在,着目しているブロックの番号でしたね.
それ以外は,Step3とあまり違いはありません.構造体の復習を兼ねて,ソースコードを読んで演習をしましょう.
/* step4 2014-07-10 */
#include "dm4.h"
#include <time.h>
#include <stdlib.h>
#define NUM 200 // ブロック数
#define ROW 22 // フィールドの行数
#define COLUMN 11 // フィールドの列数
typedef struct myObject
{
double p[3]; // x, y, z [m]
double R[12]; // 回転行列 要素数4x3
double m; // 質量 [kg]
double r,l; // 半径 [m], 長さ [m]
double side[3]; // サイズ x,y,z
float *color; // 色 r,g,b
} ;
myObject block[NUM]; // ブロック
myObject wall; // 壁
double R[12] = {1,0,0,0, 0,1,0,0, 0,0,1,0}; // 回転行列が格納される配列, 位置(x,y,z)[m]
double start_wall_p[3] = {5.5, 0.5, 0.0}; // 壁の初期位置
double start_block_p[3] = {10.5, 20.5, 0.5}; // ブロックの初期位置
double side[3] = {1.0, 1.0, 1.0}; // 直方体のサイズ(x, y, z)[m]
float black[3] = {0,0,0}, white[3] = {1.5,1.5,1.5}, red[3] = {1.5, 0, 0}; // 黒、白
int now = 0; // 現在、操作可能なブロック番号
int field[ROW][COLUMN] = // フィールド
{
{1,1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1,1} // 20
};
void command(int cmd)
{
switch (cmd)
{
case 'a':
if (block[now].p[0] > 1.5) block[now].p[0] -= 1.0;
break;
case 'd':
if (block[now].p[0] < 18.5) block[now].p[0] += 1.0;
break;
default:
printf("Key is %c: Input a,d,w,x,r\n",(char)cmd);
}
}
// フィールドのシミュレーション
void drawField()
{
int i, j;
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COLUMN; j++)
{
if (field[i][j] == 1)
{
wall.p[0] = j + start_wall_p[0]; // 位置のx成分
wall.p[1] = i + start_wall_p[1]; // 位置のx成分
wall.p[2] = 0.5; // 位置のz成分
dsSetColor(wall.color[0], wall.color[1], wall.color[2]);
dsDrawBox(wall.p, wall.R, wall.side); // 直方体の表示
}
}
}
}
// 物体の初期化
void initObject()
{
int i, j, k, c;
// ブロック
for (i =0; i < NUM; i++) {
for (j = 0; j < 3; j++) block[i].p[j] = start_block_p[j];
for (k = 0; k < 12; k++) block[i].R[k] = R[k];
// 乱数で色を生成
c = rand() % 2;
if (c == 0) block[i].color = black;
else block[i].color = white;
}
// 壁
for (j = 0; j < 3; j++) wall.side[j] = side[j];
for (k = 0; k < 12; k++) wall.R[k] = R[k];
wall.color = red;
}
// ブロックの描画
void drawBlock()
{
int i;
if (block[now].p[1] >1.5) // 下の壁を突き抜けない
{
block[now].p[1] -= 0.1; // ブロックの落下
}
else
{
now++;
}
dsSetColor(1.0, 1.0, 0.0); // 黄色
for (i=0; i <= now; i++) {
dsSetColor(block[i].color[0], block[i].color[1], block[i].color[2]);
dsDrawSphere(block[i].p, R, 0.5);
}
}
// ブロックが他のブロックと衝突したら1を返す,それ以外は0を返す
int collision()
{
int i,j;
double r=0;
for (i=0; i < now; i++) {
for (j=0; j < 3; j++) {
r += (block[i].p[j]-block[now].p[j]) *(block[i].p[j]-block[now].p[j]);
}
if (r < 1) return 1;
else r = 0;
}
return 0;
}
void simLoop(int pause) /*** シミュレーションループ ***/
{
if (collision()) {
now++;
//initBlock();
}
drawBlock(); // ブロックの表示
drawField(); // フィールドの表示
}
int main() /*** main関数 ***/
{
initObject();
srand(time(NULL)); // 乱数の初期化
dmLoop(800, 600); // シミュレーションループ ウインドウの幅,高
return 0;
}
では,演習をしながら落ち物系パズルゲームを作っていきましょう.
演習
- step4-140710.zipをダウンロードし,実行しよう!
- ブロックが各行一杯になったら,その行のブロックを全て消そう!
- 複数の結合したブロックが落ちるように改良しよう!
- 同じ色のブロックが上下左右4方向にあると,その色のブロックは消えるようにしよう!
- 得点表示をしよう!
- これを改良して最終課題を考えよう!

コメント