物理エンジンODEで学ぶC言語[Step4: 構造体]

step4

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;
}

では,演習をしながら落ち物系パズルゲームを作っていきましょう.

演習

  1. step4-140710.zipをダウンロードし,実行しよう!
  2. ブロックが各行一杯になったら,その行のブロックを全て消そう!
  3. 複数の結合したブロックが落ちるように改良しよう!
  4. 同じ色のブロックが上下左右4方向にあると,その色のブロックは消えるようにしよう!
  5. 得点表示をしよう!
  6. これを改良して最終課題を考えよう!

コメントを残す

メールアドレスが公開されることはありません。