水族館を作ろう:フレームワーク


~まずは、環境整備~




■はじめに

とりあえず、何かまとまった物を作ろうと思いました。
群集アルゴリズムで有名な、Boid を作ろうと思います。
できれば、いろいろなエフェクトを合わせて、きれいなものが作れればなぁと思っています。

今回のソースは、次のものです。

とりあえず、魚を泳がせて見ました。

■そーす

ヘッダ、CPPファイル、ともに7個づつです。
多いです。
でも、今後さらに増える予定です。

とりあえず、fps.cpp(.h)、font.cpp(.h)は、フレーム数表示用です。出したくないなら、無視してかまいません。

おニュ~は、api.cpp(.h)です。このファイルは、DirectX の依存性を下げるために用意しています。
最近の流行は、ソウルキャリバー2、CAPCOMvsSNK2 等のマルチプラットホームですが、 それを実現するために、3Dの環境に依存しない造りにしないといけません。
その一歩として、LPDIRECT3DDEVICE8 等の他の機種では絶対に出てこないオブジェクトをまとめて、一つのクラスにして、 くさいものにふたをします。
理想的には、api.cpp(.h) 及び、描画関数を差し替えるだけで、他の機種に移植できるのが良いのですが、 D3DXMATRIX など、typedef だけで、すぐに変更ができそうなものは、今回はそのままにしています。
もちろん、DirectX9 以降への対応も視野に入れています。

次は、mesh.cpp(.h)です。これは、モデル描画します。
似たような関数に、model.cpp(.h) があります。これは、登場するオブジェクトの位置などの管理をします。
将来的には、model.h で定義された CMyModel クラスから派生して、AI で動くキャラクターを実現したいと思っています。
区別としては、mash.cpp は、DirectX バリバリのソースですが、model.cpp は、どの機種でも使い易い構造になっています。

draw.cpp は全体的な流れ管理及び、シーン管理をします。

main.cpp は今までのものとそんなに変わっていません。
管理が他に移った分、簡単になっています。

■ざっと見る

まぁ、チラッと見ておきましょう。
モデルのクラス CMyModel は、位置、速度及び向きを持っています。 後は、表示の為の CMyMesh クラスです。
CMyMesh は、いままで見てきたメッシュの描画用のオブジェクトをメンバーに持ちます。

class CMyModel{
private:
public:
    CMyModel();
    D3DXVECTOR4         x;        // 位置座標
    D3DXVECTOR4         v;        // 速度
    D3DXVECTOR4         rot;      // 向き
    CMyMesh             mesh;
};
/////////////////////////////////////////////////////////////////////////////
class CMyMesh{
private:
    LPD3DXMESH               pMesh;           // メッシュ
    D3DMATERIAL8            *pMeshMaterials;  // メッシュの質感
    LPDIRECT3DTEXTURE8      *pMeshTextures;   // メッシュのテクスチャー
    DWORD                    dwNumMaterials;  // マテリアルの数
public:
    CMyMesh();
    HRESULT Load(PDIRECT3DDEVICE8 lpD3DDEV, LPSTR filename);     // XFILEのロード
    void    Render(PDIRECT3DDEVICE8 lpD3DDEV);                   // 描画
    void    Clean();                                             // 後片付け
};

次に、そのモデルを必要な分だけ確保します。
今回は、泳いでいる魚と、枠のモデルを確保します。

#define NUM_BOID	32
static CMyModel	field;
static CMyModel	model[NUM_BOID];

次は、初期化です。
それぞれ XFILE をロードして、初期位置を適当に決めます。

HRESULT InitRender(C3dApi &api)
{
    // 背景
    if(FAILED(field.mesh.Load(api.lpD3DDev, "frame.x"))) return E_FAIL;

    // BOID
    for(int i = 0; i < NUM_BOID; i++){
        if(FAILED(model[i].mesh.Load(api.lpD3DDev, "fish.x"))) return E_FAIL;
    }

    api.lpD3DDev->SetRenderState( D3DRS_ZENABLE, TRUE);
    api.lpD3DDev->SetRenderState( D3DRS_AMBIENT, 0xff808080);
    
    InitMove();

    return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
#define FIELD_X		(0.5f)
#define FIELD_Y		(0.7f)
#define FIELD_Z		(0.5f)

void InitMove()
{
    for(int i = 0; i < NUM_BOID; i++){
        model[i].x[0] =  2.0f * FIELD_X * (float)rand()/RAND_MAX -FIELD_X;
        model[i].x[1] =  FIELD_Y * (float)(i+1)/(NUM_BOID+2);
        model[i].x[2] =  2.0f * FIELD_Z * (float)rand()/RAND_MAX-FIELD_Z;
        model[i].v[0] = -0.001f;
        model[i].rot[1] = PI/2.0f;
    }
}

描画は、それぞれ動かしてから、ビュー、射影行列をセットして、 後は、ワールド行列の変更で書き換えます。

void Render(C3dApi &api)
{
    Move();
    
    // ビュー、射影行列の生成
    D3DXMATRIX mView, mProj, mWorld;
    D3DXMatrixLookAtLH( &mView, &D3DXVECTOR3( 0.0f, 0.35f, -1.2f ), 
                                &D3DXVECTOR3( 0.0f, 0.35f,  0.0f ), 
                                &D3DXVECTOR3( 0.0f, 1.0f,  0.0f ) );
    D3DXMatrixPerspectiveFovLH(&mProj
        ,60.0f*PI/180.0f                          // 視野角
        ,(float)WIDTH/(float)HEIGHT               // アスペクト比
        ,0.01f                                    // 最近接距離
        ,10.0f                                    // 最遠方距離
        );
    
    api.lpD3DDev->SetTransform(D3DTS_VIEW,  &mView);
    api.lpD3DDev->SetTransform(D3DTS_PROJECTION, &mProj);
    
    // 背景の描画
    D3DXMatrixIdentity(&mWorld);                     // 中心に描画
    api.lpD3DDev->SetTransform(D3DTS_WORLD, &mWorld);
    field.mesh.Render(api.lpD3DDev);
    
    // Boid の描画
    CMyModel *pModel = &model[0];
    for(int i = 0; i < NUM_BOID; i++){
        D3DXMATRIX mWorld, m;
        D3DXMatrixTranslation(&mWorld
                            , pModel->x[0]
                            , pModel->x[1]
                            , pModel->x[2]);
        D3DXMatrixRotationYawPitchRoll(&m
                            , pModel->rot[1]
                            , pModel->rot[0]
                            , pModel->rot[2]);
        mWorld = m * mWorld;
        api.lpD3DDev->SetTransform(D3DTS_WORLD, &mWorld);
        pModel->mesh.Render(api.lpD3DDev);
        pModel++;
    }

}
/////////////////////////////////////////////////////////////////////////////
void Move()
{
    for(int i = 0; i < NUM_BOID; i++){
        model[i].x[0] += model[i].v[0];
        model[i].x[1] += model[i].v[1];
        model[i].x[2] += model[i].v[2];
        // 突き抜けたら、反対からでてくる
        while(model[i].x[0] < -FIELD_X) model[i].x[0] += 2.0f*FIELD_X;
        while( FIELD_X < model[i].x[0]) model[i].x[0] -= 2.0f*FIELD_X;
        while(model[i].x[1] <     0.0f) model[i].x[1] += FIELD_Y;
        while( FIELD_Y < model[i].x[1]) model[i].x[1] -= FIELD_Y;
        while(model[i].x[2] < -FIELD_Z) model[i].x[2] += 2.0f*FIELD_Z;
        while( FIELD_Z < model[i].x[2]) model[i].x[2] -= 2.0f*FIELD_Z;
    }
}

■最後に

今回は、単品単品はすでに出てきたものですので、書き方に慣れれば、分かりやすいはずです。
とりあえず、魚が泳いで、突き抜けたら反対からでてきます。
今後、AIをいれて、さらに衝突判定もしていこうと思います。





もどる

imagire@gmail.com