複数のキャラクタを表示する

1つのキャラクタを複数表示するには?

先に学んだとおり、キャラクタを表示するには、1つのキャラクタに対して1つの四角形ポリゴンを用意しなければならない。つまり、複数のキャラクタを表示するには、その数分の四角形ポリゴンを用意しなければならないことになる。

四角形ポリゴンは、頂点情報を4つ持つ配列で表現される。つまり、配列を複数用意すれば、複数のポリゴンを描画できることになる。この考え方でプログラムすると、おおよそ次のようになる。

// グローバル変数
HWND hWnd;                              // ウィンドウハンドル
BOOL g_appActive = FALSE;               // ウィンドウの状態
char szWinName[] = "Exer004";           // ウィンドウクラス用文字列
char szWinTitle[] = "DirectX Graphicsでの2D表示"; // ウィンドウクラス用文字列
LPDIRECT3D8 gl_lpD3d = NULL;            // Direct3D8インターフェイス
LPDIRECT3DDEVICE8 gl_lpD3ddev = NULL;   // Direct3DDevice8インターフェイス
D3DPRESENT_PARAMETERS gl_d3dpp;         // ディスプレイパラメータ
LPDIRECT3DTEXTURE8 gl_Texture = NULL;   // テクスチャ・オブジェクト
BYTE g_FrameNo = START_INIT;            // フレーム選択用
BYTE gl_KeyTbl[256];                    // キーボードの状態を格納
TLVERTX VertexDataTbl1[4];              // キャラクタ1用頂点情報配列
TLVERTX VertexDataTbl2[4];              // キャラクタ2用頂点情報配列
TLVERTX VertexDataTbl3[4];              // キャラクタ3用頂点情報配列
TLVERTX VertexDataTbl4[4];              // キャラクタ4用頂点情報配列
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : StartInit()
// 機能概要: スタート画面初期化処理
//-----------------------------------------------------------------------------
void StartInit(void)
{
    HRESULT hr;

    //--------------------------------------------------- 各変数の初期化
    // スタート画面で使用するテクスチャの作成
    CreateStartTexture();
    // 頂点データを格納する
    InitVertex(VertexDataTbl1, 100.0, 100.0, 300.0, 300.0);
    InitVertex(VertexDataTbl2, 250.0, 50.0, 450.0, 250.0);
    InitVertex(VertexDataTbl3, 300.0, 200.0, 500.0, 400.0);
    InitVertex(VertexDataTbl4, 350.0, 250.0, 550.0, 450.0);

    //--------------------------------------------------- フレームナンバーセット
    g_FrameNo = START_FRAME;

}

//-----------------------------------------------------------------------------
// 関数名 : StartFrame()
// 機能概要: スタート画面処理
//-----------------------------------------------------------------------------
void StartFrame(void)
{

    /* 描画処理 */
    // 描画するテクスチャをデバイスにセット
    gl_lpD3ddev->SetTexture(0, gl_Texture);
    // キャラクタ1をバックバッファに転送
    gl_lpD3ddev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, VertexDataTbl1, sizeof(TLVERTX));
    // キャラクタ2をバックバッファに転送
    gl_lpD3ddev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, VertexDataTbl2, sizeof(TLVERTX));
    // キャラクタ3をバックバッファに転送
    gl_lpD3ddev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, VertexDataTbl3, sizeof(TLVERTX));
    // キャラクタ4をバックバッファに転送
    gl_lpD3ddev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, VertexDataTbl4, sizeof(TLVERTX));

    // リターンキーが押されたら、ゲーム開始
    if ( gl_KeyTbl[VK_SPACE] & 0x80 ) g_FrameNo = GAME_INIT;

}

《解説》

※上記変更を行い、同じキャラクタが4つ表示されることを確認すること。

頂点情報構造体を効率よく管理する

シューティングゲームの敵キャラや弾など、同じ画像を複数表示することはよくある。その場合、その数ごとに配列を用意するのはプログラム的に無駄である。上記プログラムの場合、次のように修正するべきである。

// マクロの定義
#define SCREEN_WIDTH	640	// ウィンドウの幅
#define SCREEN_HEIGHT	480	// ウィンドウの高さ
/* ゲームの状態を識別する(フレーム番号) */
#define START_INIT     0
#define START_FRAME    1
#define GAME_INIT      10
#define GAME_FRAME     11
/* 頂点フォーマット(基本形)*/
#define FVF_TLVERTEX (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1)
/* 表示するキャラクタの数*/
#define CHARACTER_MAX  4
・
・
・
// グローバル変数
HWND hWnd;                              // ウィンドウハンドル
BOOL g_appActive = FALSE;               // ウィンドウの状態
char szWinName[] = "Exer004";           // ウィンドウクラス用文字列
char szWinTitle[] = "DirectX Graphicsでの2D表示"; // ウィンドウクラス用文字列
LPDIRECT3D8 gl_lpD3d = NULL;            // Direct3D8インターフェイス
LPDIRECT3DDEVICE8 gl_lpD3ddev = NULL;   // Direct3DDevice8インターフェイス
D3DPRESENT_PARAMETERS gl_d3dpp;         // ディスプレイパラメータ
LPDIRECT3DTEXTURE8 gl_Texture = NULL;   // テクスチャ・オブジェクト
BYTE g_FrameNo = START_INIT;            // フレーム選択用
BYTE gl_KeyTbl[256];                    // キーボードの状態を格納
TLVERTX VertexDataTbl[CHARACTER_MAX][4];// キャラクタ用頂点情報配列
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : StartInit()
// 機能概要: スタート画面初期化処理
//-----------------------------------------------------------------------------
void StartInit(void)
{
    HRESULT hr;

    //--------------------------------------------------- 各変数の初期化
    // スタート画面で使用するテクスチャの作成
    CreateStartTexture();
    // 頂点データを格納する
    InitVertex(VertexDataTbl[0], 100.0, 100.0, 300.0, 300.0);
    InitVertex(VertexDataTbl[1], 250.0, 50.0, 450.0, 250.0);
    InitVertex(VertexDataTbl[2], 300.0, 200.0, 500.0, 400.0);
    InitVertex(VertexDataTbl[3], 350.0, 250.0, 550.0, 450.0);

    //--------------------------------------------------- フレームナンバーセット
    g_FrameNo = START_FRAME;

}

//-----------------------------------------------------------------------------
// 関数名 : StartFrame()
// 機能概要: スタート画面処理
//-----------------------------------------------------------------------------
void StartFrame(void)
{
    int i;

    /* 描画処理 */
    // 描画するテクスチャをデバイスにセット
    gl_lpD3ddev->SetTexture(0, gl_Texture);
    // ポリゴンをバックバッファに転送
    for (i=0 ; i<CHARACTER_MAX ; i++) {
        gl_lpD3ddev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, VertexDataTbl[i], sizeof(TLVERTX));
    }

    // リターンキーが押されたら、ゲーム開始
    if ( gl_KeyTbl[VK_SPACE] & 0x80 ) g_FrameNo = GAME_INIT;

}

《解説》

※上記変更を行い、同じ結果になることを確認すること。

キャラクタによって画像を変えるには?その1

4つのポリゴンそれぞれに違う画像を貼り付けるには、まず画像別にテクスチャを用意し、ポリゴンを描画する前にテクスチャを変えればよい。テクスチャは画像ファイルを読み込んで作成するため、4つの画像ファイルを用意し、それぞれの画像ファイルのテクスチャを作成することになる。

この考え方でプログラムを修正すると、次のようになる。(残り3つの画像ファイルsample2.bmpsample3.bmpsample4.bmpはここからダウンロードし、プログラムと同じフォルダに配置する)

// グローバル変数
HWND hWnd;                              // ウィンドウハンドル
BOOL g_appActive = FALSE;               // ウィンドウの状態
char szWinName[] = "Exer004";           // ウィンドウクラス用文字列
char szWinTitle[] = "DirectX Graphicsでの2D表示"; // ウィンドウクラス用文字列
LPDIRECT3D8 gl_lpD3d = NULL;            // Direct3D8インターフェイス
LPDIRECT3DDEVICE8 gl_lpD3ddev = NULL;   // Direct3DDevice8インターフェイス
D3DPRESENT_PARAMETERS gl_d3dpp;         // ディスプレイパラメータ
LPDIRECT3DTEXTURE8 gl_Texture1 = NULL;  // テクスチャ・オブジェクト
LPDIRECT3DTEXTURE8 gl_Texture2 = NULL;  // テクスチャ・オブジェクト2
LPDIRECT3DTEXTURE8 gl_Texture3 = NULL;  // テクスチャ・オブジェクト3
LPDIRECT3DTEXTURE8 gl_Texture4 = NULL;  // テクスチャ・オブジェクト4
BYTE g_FrameNo = START_INIT;            // フレーム選択用
BYTE gl_KeyTbl[256];                    // キーボードの状態を格納
TLVERTX VertexDataTbl[CHARACTER_MAX][4];// キャラクタ用頂点情報配列
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : ReleaseD3D()
// 機能概要: DirectX8オブジェクトの開放
//-----------------------------------------------------------------------------
void ReleaseD3D(void)
{
    // テクスチャの開放
    if ( gl_Texture1 != NULL ) {
        gl_Texture1->Release();
        gl_Texture1 = NULL;
    }
    if ( gl_Texture2 != NULL ) {
        gl_Texture2->Release();
        gl_Texture2 = NULL;
    }
    if ( gl_Texture3 != NULL ) {
        gl_Texture3->Release();
        gl_Texture3 = NULL;
    }
    if ( gl_Texture4 != NULL ) {
        gl_Texture4->Release();
        gl_Texture4 = NULL;
    }
    //デバイスオブジェクトの開放
    if ( gl_lpD3ddev != NULL ) {
        gl_lpD3ddev->Release();
        gl_lpD3ddev = NULL;
    }
    //DirectX8オブジェクトの開放
    if ( gl_lpD3d != NULL ) {
        gl_lpD3d->Release();
        gl_lpD3d = NULL;
    }
}
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : CreateStartTexture()
// 機能概要: スタート画面用テクスチャの作成・再生成
//-----------------------------------------------------------------------------
void CreateStartTexture(void)
{
    HRESULT hr;

    // キャラクタ1
    hr = D3DXCreateTextureFromFile(gl_lpD3ddev, "sample1.bmp", &gl_Texture1);
    if ( FAILED(hr) ) {
        MessageBox(hWnd, "sample1.bmp をテクスチャとして読み込めませんでした", "ERROR", MB_OK);
        return;
    }
    // キャラクタ2
    hr = D3DXCreateTextureFromFile(gl_lpD3ddev, "sample2.bmp", &gl_Texture2);
    if ( FAILED(hr) ) {
        MessageBox(hWnd, "sample2.bmp をテクスチャとして読み込めませんでした", "ERROR", MB_OK);
        return;
    }
    // キャラクタ3
    hr = D3DXCreateTextureFromFile(gl_lpD3ddev, "sample3.bmp", &gl_Texture3);
    if ( FAILED(hr) ) {
        MessageBox(hWnd, "sample3.bmp をテクスチャとして読み込めませんでした", "ERROR", MB_OK);
        return;
    }
    // キャラクタ4
    hr = D3DXCreateTextureFromFile(gl_lpD3ddev, "sample4.bmp", &gl_Texture4);
    if ( FAILED(hr) ) {
        MessageBox(hWnd, "sample4.bmp をテクスチャとして読み込めませんでした", "ERROR", MB_OK);
        return;
    }

}

//-----------------------------------------------------------------------------
// 関数名 : StartFrame()
// 機能概要: スタート画面処理
//-----------------------------------------------------------------------------
void StartFrame(void)
{
    int i;

    /* 描画処理 */
    for ( i=0 ; i<CHARACTER_MAX ; i++ ) {
        // 描画するテクスチャをデバイスにセット
        switch ( i ) {
            case 0: gl_lpD3ddev->SetTexture(0, gl_Texture1); break;
            case 1: gl_lpD3ddev->SetTexture(0, gl_Texture2); break;
            case 2: gl_lpD3ddev->SetTexture(0, gl_Texture3); break;
            case 3: gl_lpD3ddev->SetTexture(0, gl_Texture4); break;
        }
        // ポリゴンをバックバッファに転送
        gl_lpD3ddev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, VertexDataTbl[i], sizeof(TLVERTX));
    }

    // リターンキーが押されたら、ゲーム開始
    if ( gl_KeyTbl[VK_SPACE] & 0x80 ) g_FrameNo = GAME_INIT;

}

《解説》

※上記修正を行い、4つのポリゴンにそれぞれ違うテクスチャ画像が貼り付けられて表示されることを確認すること。

キャラクタによって画像を変えるには?その2

しかし、このプログラムもやはり美しくない。せっかく頂点情報構造体を配列にして、複数のキャラクタに対応させたのに、テクスチャが配列になっていないのでは意味がない。テクスチャも配列にして、美しいプログラムにするには次のように修正するといいだろう。

// グローバル変数
HWND hWnd;                              // ウィンドウハンドル
BOOL g_appActive = FALSE;               // ウィンドウの状態
char szWinName[] = "Exer004";           // ウィンドウクラス用文字列
char szWinTitle[] = "DirectX Graphicsでの2D表示"; // ウィンドウクラス用文字列
LPDIRECT3D8 gl_lpD3d = NULL;            // Direct3D8インターフェイス
LPDIRECT3DDEVICE8 gl_lpD3ddev = NULL;   // Direct3DDevice8インターフェイス
D3DPRESENT_PARAMETERS gl_d3dpp;         // ディスプレイパラメータ
LPDIRECT3DTEXTURE8 gl_Texture[CHARACTER_MAX]; // テクスチャ・オブジェクト
BYTE g_FrameNo = START_INIT;            // フレーム選択用
BYTE gl_KeyTbl[256];                    // キーボードの状態を格納
TLVERTX VertexDataTbl[CHARACTER_MAX][4]; // キャラクタ用頂点情報配列
char* gl_szCharFileName[CHARACTER_MAX] = { // キャラクタ画像ファイル名
    "sample1.bmp", "sample2.bmp", "sample3.bmp", "sample4.bmp"
};
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : ReleaseD3D()
// 機能概要: DirectX8オブジェクトの開放
//-----------------------------------------------------------------------------
void ReleaseD3D(void)
{
    int i;

    // テクスチャの開放
    for ( i=0 ; i<CHARACTER_MAX ; i++ ) {
        if ( gl_Texture[i] != NULL ) {
            gl_Texture[i]->Release();
            gl_Texture[i] = NULL;
        }
    }
    //デバイスオブジェクトの開放
    if ( gl_lpD3ddev != NULL ) {
        gl_lpD3ddev->Release();
        gl_lpD3ddev = NULL;
    }
    //DirectX8オブジェクトの開放
    if ( gl_lpD3d != NULL ) {
        gl_lpD3d->Release();
        gl_lpD3d = NULL;
    }
}
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : CreateStartTexture()
// 機能概要: スタート画面用テクスチャの作成・再生成
//-----------------------------------------------------------------------------
void CreateStartTexture(void)
{
    HRESULT hr;
    int i;
    char buff[80];

    // 画像ファイルを読み込み、テクスチャを生成
    for (i=0 ; i<CHARACTER_MAX ; i++) {
        hr = D3DXCreateTextureFromFile(gl_lpD3ddev, gl_szCharFileName[i], &gl_Texture[i]);
        if ( FAILED(hr) ) {
            wsprintf(msg, "%s をテクスチャとして読み込めませんでした", gl_szCharFileName[i]);
            MessageBox(hWnd, msg, "ERROR", MB_OK);
            return;
        }
    }

}

//-----------------------------------------------------------------------------
// 関数名 : StartFrame()
// 機能概要: スタート画面処理
//-----------------------------------------------------------------------------
void StartFrame(void)
{
    int i;

    /* 描画処理 */
    for (i=0 ; i<CHARACTER_MAX ; i++) {
        // 描画するテクスチャをデバイスにセット
        gl_lpD3ddev->SetTexture(0, gl_Texture[i]);
        // キャラクタ1をバックバッファに転送
        gl_lpD3ddev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, VertexDataTbl[i], sizeof(TLVERTX));
    }

    // スペースキーが押されたら、ゲーム開始
    if (KeyTbl[VK_SPACE] & 0x80) g_FrameNo = GAME_INIT;

}

《解説》

※上記変更を行い、同じ結果になることを確認すること。


BACK(画像の描画(テクスチャ・マッピング)) NEXT(1つの画像ファイルに複数の画像を載せるには?)