タイプ別にキャラクタ画像を変える

前項までのプログラムでは、どのタイプの敵キャラでも同じテクスチャを使っている。ここでは、タイプによってテクスチャ画像を切り替えて表示する方法を紹介する。

この章で使用する画像ファイル

考え方

タイプ別に敵キャラの画像を変えるのが一番シンプルであるが、そうとは限らない。例えば右上から現れて左下に移動する敵キャラと、左上から現れて右下に移動する敵キャラは、初期化データは違うためにタイプが違うと考えられるが、右から出るか左から出るかの違いだけで、動き自体に差はほとんど無い。こういった敵キャラは、同じ画像を使うことが多い。

よって、タイプが違えば画像も違うが、違うタイプでも同じ画像を使う場合もあると考えたほうが、汎用性のあるプログラムを作成できるだろう。

では実際に、プログラムに組み込む方法を解説する。この章で使用するビットマップファイルをダウンロード・解凍し、ソースファイルと同じフォルダ内へ配置しておくこと。

プログラムへの組み込み1・敵キャラ用テクスチャの作成

敵キャラ用の画像を3つ用意した。この3つの画像のテクスチャを作成し、5つのタイプの敵キャラで使用する。

今回は、どのステージでも同じ敵キャラ画像を使うため、ゲーム開始時初期化処理(GameInit関数)でテクスチャを作成するが、ステージごとに使う画像を変える場合、ステージ別初期化処理(GameStageInit関数)で行ったほうがよい。

NKC_Common.h

//-----------------------------------------------------------------------------
// 共通ヘッダ・ファイル
//  Copyright NKC Game Staff(←自分の名前) 
//-----------------------------------------------------------------------------
・
・
・
// マクロ
/* 頂点フォーマット(基本形)*/
#define FVF_TLVERTEX (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1)
/* ゲームの状態を識別する(フレーム番号) */
#define START_INIT      0
#define START_FRAME     1
#define GAME_INIT       10
#define GAME_STAGEINIT  11
#define GAME_FRAME      12
#define GAMEOVER_INIT   80
#define GAMEOVER_FRAME  81
#define GAMECLEAR_INIT  90
#define GAMECLEAR_FRAME 91
/* その他のマクロ */
#define SCREEN_WIDTH	640	// ウィンドウの幅
#define SCREEN_HEIGHT	480	// ウィンドウの高さ
#define RELEASE(x) if(x!=NULL){x->Release();x=NULL;} // オブジェクトの開放
#define DEBUG true      // デバッグ・モード(使わないときはコメントアウト)
#define DEAD 0          // 死亡フラグ
#define ENEMYBMPMAX 3   // 敵キャラ画像の最大
・
・
・

NKC_DGraphics.cpp

//=============================================================================
//  DirectX Graphics関係の自作関数群
//  Copyright NKC Game Staff(←自分の名前) 
//-----------------------------------------------------------------------------
#include "NKC_Common.h"

// グローバル変数
/* 他のソースからも利用されるもの */
LPDIRECT3DDEVICE8 gl_lpD3ddev = NULL;   // Direct3DDevice8インターフェイス
LPDIRECT3DTEXTURE8 gl_TXBack = NULL;    // 背景用テクスチャ
LPDIRECT3DTEXTURE8 gl_TXMenu = NULL;    // メニュー用テクスチャ
LPDIRECT3DTEXTURE8 gl_TXCursor = NULL;  // メニュー選択カーソル用テクスチャ
LPDIRECT3DTEXTURE8 gl_TXMyChara = NULL; // 自機用テクスチャ
LPDIRECT3DTEXTURE8 gl_TXEnemy[ENEMYBMPMAX] = {NULL,NULL,NULL}; // 敵用テクスチャ
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : CreateGameTexture()
// 機能概要: ゲーム画面用テクスチャの作成・再生成
//-----------------------------------------------------------------------------
void CreateGameTexture(void)
{
    HRESULT hr;
    char* szEnemyImg[] = {"enemy0.bmp","enemy1.bmp","enemy2.bmp"}; // 敵キャラファイル名
    int i;
    char buff[80];

    // 自機用テクスチャを生成
    hr = D3DXCreateTextureFromFileEx(
        gl_lpD3ddev,
        "mychara.bmp",                 // ファイル名
        0, 0, 0, 0,
        D3DFMT_A1R5G5B5,                // 色抜きを可能に
        D3DPOOL_MANAGED,
        D3DX_FILTER_LINEAR,
        D3DX_FILTER_LINEAR,
        D3DCOLOR_ARGB(255, 255, 255, 255),// 白色を透過色とする
        NULL,
        NULL,
        &gl_TXMyChara                   // テクスチャ名
    );
    if ( FAILED(hr) ) {
        MessageBox(hWnd, "mychara.bmp をテクスチャとして読み込めませんでした", "ERROR", MB_OK);
        return;
    }
    // 敵用テクスチャを生成
    for ( i=0 ; i<ENEMYBMPMAX ; i++ ) {
        hr = D3DXCreateTextureFromFileEx(
            gl_lpD3ddev,
            szEnemyImg[i],                  // ファイル名
            0, 0, 0, 0,
            D3DFMT_A1R5G5B5,                // 色抜きを可能に
            D3DPOOL_MANAGED,
            D3DX_FILTER_LINEAR,
            D3DX_FILTER_LINEAR,
            D3DCOLOR_ARGB(255, 21, 141, 91),// 透過色を指定
            NULL,
            NULL,
            &gl_TXEnemy[i]                  // テクスチャ名
        );
        if ( FAILED(hr) ) {
            wsprintf(buff, "%s をテクスチャとして読み込めませんでした", szEnemyImg[i]);
            MessageBox(hWnd, buff, "ERROR", MB_OK);
            return;
        }
    }
}

//-----------------------------------------------------------------------------
// 関数名 : ReleaseGameTexture()
// 機能概要: ゲーム画面用テクスチャ開放処理
//-----------------------------------------------------------------------------
void ReleaseGameTexture(void)
{
    int i;

    // テクスチャの開放
    RELEASE(gl_TXBack); // 背景
    RELEASE(gl_TXMyChara); // 自キャラ
    for ( i=0 ; i<ENEMYBMPMAX ; i++ ) RELEASE(gl_TXEnemy[i]); // 敵キャラ
}

NKC_DGraphics.h

//-----------------------------------------------------------------------------
// File: NKC_DGraphics.h
// Desc: NKC_DGraphics.cppで使用するもののうち、他のソースでも使うものを宣言
//-----------------------------------------------------------------------------
・
・
・
//-----------------------------------------------------------------------------
// グローバル変数
//-----------------------------------------------------------------------------
extern LPDIRECT3DDEVICE8 gl_lpD3ddev;   // Direct3DDevice8インターフェイス
extern LPDIRECT3DTEXTURE8 gl_TXBack;    // 背景用テクスチャ
extern LPDIRECT3DTEXTURE8 gl_TXMenu;    // メニュー用テクスチャ
extern LPDIRECT3DTEXTURE8 gl_TXCursor;  // メニュー選択カーソル用テクスチャ
extern LPDIRECT3DTEXTURE8 gl_TXMyChara; // 自機用テクスチャ
extern LPDIRECT3DTEXTURE8 gl_TXEnemy[ENEMYBMPMAX]; // 敵用テクスチャ
・
・
・

Game.cpp

//=============================================================================
//  ゲーム処理関係の自作関数群
//  Copyright NKC Game Staff(←自分の名前) 
//-----------------------------------------------------------------------------
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : EnemyDraw()
// 機能概要: 敵キャラ描画
//-----------------------------------------------------------------------------
static void EnemyDraw(void)
{
    int i;

    gl_lpD3ddev->SetTexture(0, gl_TXEnemy[0]);
    for ( i=0 ; i<ENEMYMAX ; i++ ) {
        if ( Enemy[i].life > DEAD )
            gl_lpD3ddev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, Enemy[i].Vertex, sizeof(TLVERTX));
    }
}
・
・
・

《POINT》

ここまでの修正を行い、敵キャラ用テクスチャの要素を0,1,2にそれぞれセットしてみて、テクスチャが切り替わるかを確認する。

プログラムへの組み込み2・敵キャラのタイプ毎に使用するテクスチャを切り替える

敵キャラの種類(タイプ)よって、使用するテクスチャが変化するよう、プログラムを修正する。

Game.cppの敵キャラ描画処理を修正

//-----------------------------------------------------------------------------
// 関数名 : EnemyDraw()
// 機能概要: 敵キャラ描画
//-----------------------------------------------------------------------------
static void EnemyDraw(void)
{
    int i, j;

    for ( i=0 ; i<ENEMYMAX ; i++ ) {
        if ( Enemy[i].life > DEAD ) {
            switch ( Enemy[i].type ) {
                case 0: j = 0; break;
                case 1:
                case 2: j = 1; break;
                case 3:
                case 4: j = 2; break;
            }
            gl_lpD3ddev->SetTexture(0, gl_TXEnemy[j]);
            gl_lpD3ddev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, Enemy[i].Vertex, sizeof(TLVERTX));
        }
    }
}

《POINT》

ここまでの修正を行い、テクスチャが正しく変化するかを確認する。

処理効率を考えたプログラミング

先に紹介したやり方の場合、プログラミングは楽かもしれないが、描画(ゲームループ)のたびに判定が行われるため、処理効率が悪い

描画処理が実行されるたびに使用するテクスチャの判定を行うのではなく、敵キャラ発生時に使用するテクスチャをセットする方法を紹介する。

1.キャラクタ構造体に、テクスチャオブジェクトを追加(common.h)

/* キャラクタ情報構造体 */
typedef struct _STATUS {
    TLVERTX Vertex[4];                      // 頂点情報配列
    RECT HitRect;                           // 当たり判定矩形情報
    float MoveX, MoveY;                     // 移動量
    int type;                               // タイプ
    LPDIRECT3DTEXTURE8 Texture;             // テクスチャ
    int flg;                                // フラグ(表示:1以上 非表示:0)
} STATUS, *LPSTATUS;

《POINT》

2.敵キャラ発生時初期化処理の修正(Game.cpp)

//-----------------------------------------------------------------------------
// 関数名 : SetEnemyInit()
// 機能概要: 敵キャラ発生用データ初期化
//-----------------------------------------------------------------------------
static void SetEnemyInit(LPSTATUS pEnemyInit,
                         float x1, float y1, float x2, float y2,
                         LONG left, LONG right, LONG top, LONG bottom,
                         float mx, float my,
                         int type, int life)
{
    InitVertex(pEnemyInit->Vertex, x1, y1, x2, y2);         // 表示位置
    SetRect(&pEnemyInit->HitRect, left, top, right, bottom); // 当たり判定矩形
    pEnemyInit->MoveX = mx;                                 // 移動量(X方向)
    pEnemyInit->MoveY = my;                                 // 移動量(Y方向)
    pEnemyInit->type = type;                                // タイプ
    switch ( type ) {                                       // テクスチャ
        case 0: pEnemyInit->Texture = gl_TXEnemy[0]; break;
        case 1:
        case 2: pEnemyInit->Texture = gl_TXEnemy[1]; break;
        case 3:
        case 4: pEnemyInit->Texture = gl_TXEnemy[2]; break;
    }
    pEnemyInit->life = life;                                // ライフ
}

《POINT》

3.敵キャラ描画処理の修正(Game.cpp)

//-----------------------------------------------------------------------------
// 関数名 : EnemyDraw()
// 機能概要: 敵キャラ描画
//-----------------------------------------------------------------------------
static void EnemyDraw(void)
{
    int i;

    for ( i=0 ; i<ENEMYMAX ; i++ ) {
        if ( Enemy[i].life > DEAD ) {
            gl_lpD3ddev->SetTexture(0, Enemy[i].Texture);
            gl_lpD3ddev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, Enemy[i].Vertex, sizeof(TLVERTX));
        }
    }
}

《POINT》

※ここまでの修正を行い、テクスチャが正しく変化するかを確認する。

どちらのやり方がいいのかは、処理効率を優先するのか、プログラムの見易さを優先するのか、メモリの使用量を優先するのかなど、何を優先するかによって変わる。自分が作成するゲームではどのやり方が一番いいのかを常に考えながら、Case by Caseで使い分けよう。


BACK(タイプ別に初期化データを変える) NEXT(自キャラから弾を出す)