テクスチャ・アニメーション

キャラクタのアニメーションは、アニメーションに必要な複数の画像を用意し、一定時間ごとに切り替えて表示することにより行われる。ここでは、「移動方向によって表示する画像を変える」で作成したプログラムを元に、自キャラのアニメーションを行う方法を解説する。

用意する画像について

アニメーションに必要な画像を1ファイルずつ用意すれば、頂点座標を変化させる必要はない。しかし、ファイルの読み込みとテクスチャの作成など、面倒になる。
よって、1つのキャラクタに必要なすべての画像をまとめて1つのファイルとして管理したほうがよい

ただし、頂点情報のtu,tvの値は0.0から1.0までの数値で指定しなければならず、左から何ドット・・・といった指定はできない。よって、例えばアニメーションで必要な画像を横に7つ並べてしまうと、7で割り切れないためにtuの値を指定できなくなってしまう
ひとつの画像ファイルに複数の画像を並べる場合、必ず割り切れる数値になるように数を調整しなければならないことに注意しよう。

この章で用意している自キャラの画像データは次のようになっている。

キャラクタの移動方向によって表示する画像を変えるため、縦に「静止」「右」「左」「上」「下」の順に並べてある。同時に、それぞれの移動方向で使用するアニメーション画像を横に5つ並べてある
先のページによって、移動方向別に表示する画像を変化させるようにプログラムしてあるので、このプログラムをさらに改造し、それぞれの移動方向でアニメーションが行われるようにする。

考え方

現在のプログラムは、入力したキーの情報によって移動方向を調べ、その方向によって頂点情報のtvの値を変化させている。アニメーションに使用する画像は横に並んでいるため、アニメーションを行うには一定時間ごとにtuの値を変化させるようにプログラムすればよい。

今回用意した自キャラ画像の場合、横に5つの画像を用意しているため、一定時間ごとにtuの値を次のように変化させればよい。

時間v[0].tuの値
ゲーム開始直後0.0
500ミリ秒経過0.2
1000ミリ秒経過0.4
1500ミリ秒経過0.6
2000ミリ秒経過0.8
2500ミリ秒経過0.0

《POINT》

実際のプログラム

では実際に、アニメーションを行うようにプログラムを修正する方法を解説する。

1.キャラクタ構造体の修正(NKC_Common.h)

今何枚目を表示しているかという情報と、画像を切り替えたときの時間をキャラクタ構造体に持つ。これにより、tuの値を計算できるため、アニメーションの数が増えてもプログラムの修正を必要最小限に抑えることができる。

//-----------------------------------------------------------------------------
// 共通ヘッダ・ファイル
//  Copyright NKC Game Staff(←自分の名前) 
//-----------------------------------------------------------------------------
・
・
・
/* その他のマクロ */
#define WINMODE FALSE // ウィンドウモードの指定(TRUE:ウィンドウモード/FALSE:フルスクリーン)
#define SCREEN_WIDTH	640	// ウィンドウの幅
#define SCREEN_HEIGHT	480	// ウィンドウの高さ
#define RELEASE(x) if(x!=NULL){x->Release();x=NULL;} // オブジェクトの開放
#define DEBUG true              // デバッグ・モード(使わないときはコメントアウト)
#define ANIMATION_TIMING 500    // アニメーションのタイミング

// 構造体宣言
・
・
・
/* キャラクタ情報構造体 */
typedef struct _STATUS {
    TLVERTX Vertex[4];                      // 頂点情報配列
    RECT HitRect;                           // 当たり判定矩形情報
    float MoveX, MoveY;                     // 移動量
    int life;                               // ライフ(表示:1以上 非表示:0)
    int AnimeMax;                           // アニメーションの最大数
    int AnimeNo;                            // アニメーション番号
    DWORD time;                             // 経過時間
} STATUS, *LPSTATUS;
・
・
・

《POINT》

2.マイキャラデータの初期化を修正(Game.cpp)

キャラクタ情報構造体にメンバを追加したため、それらの初期化を行う。

//-----------------------------------------------------------------------------
// 関数名 : GameInit()
// 機能概要: ゲーム画面初期化処理
//-----------------------------------------------------------------------------
void GameInit(void)
{

    //--------------------------------------------------- 各変数の初期化
    // ゲーム画面で共通して使用するテクスチャの作成
    CreateGameTexture();
    // ポリゴンの初期化
    /* 背景 */
    InitVertex(BackVertex, (float)gl_rcScreen.left, (float)gl_rcScreen.top, (float)gl_rcScreen.right, (float)gl_rcScreen.bottom, 255); // 頂点データ
    /* 自キャラ */
    MyCharaInitVertex(MyChara.Vertex, 272.0f, 380.0f, 368.0f, 476.0f);// 表示位置
    SetRect(&MyChara.HitRect, 10, 10, 10, 10);                       // 当たり判定矩形
    MyChara.MoveX = 2.0f;                                            // 移動量(X方向)
    MyChara.MoveY = 2.0f;                                            // 移動量(Y方向)
    MyChara.AnimeMax = 5;                                            // アニメーション最大数
    MyChara.AnimeNo = 0;                                             // アニメーション番号

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

}

3.アニメーション番号によってtuの値を変化させる関数を作成(Game.cpp)

アニメーションは様々なキャラクタで行うため、最初から関数化しておいたほうが便利である。キャラクタ情報を引数とし、アニメーション番号によってtuの値を求め、セットする関数を、Game.cppに作成する。プロトタイプ宣言も行うこと。

//----------------------------------------------------------------------------------------
// 関数名 : ChangeAnime() 
// 機能概要: アニメーション番号によって転送する画像を切り替える
//----------------------------------------------------------------------------------------
static void ChangeAnime(LPSTATUS lpStatus)
{
    float tu, width;

    /* アニメーション時間のクリア */
    lpStatus->time = gl_nowTime;

    /* アニメーション番号のカウントアップ */
    lpStatus->AnimeNo++;
    if (lpStatus->AnimeNo >= lpStatus->AnimeMax) lpStatus->AnimeNo = 0;

    /* アニメーション番号によって、tuの値を再計算する */
    width = 1.0f / (float)lpStatus->AnimeMax;
    tu = lpStatus->AnimeNo * width;
    lpStatus->Vertex[0].tu = tu;
    lpStatus->Vertex[1].tu = tu + width;
    lpStatus->Vertex[2].tu = tu + width;
    lpStatus->Vertex[3].tu = tu;

}

《POINT》

4.一定時間ごとにアニメーションさせる処理を追加(Game.cpp)

3で作成した関数を、一定時間ごとに呼び出す処理を、GameFrame関数内に追加する。

//-----------------------------------------------------------------------------
// 関数名 : GameFrame()
// 機能概要: スタート画面処理
//-----------------------------------------------------------------------------
void GameFrame(void)
{
    //--------------------------------------------------- 前処理
    /* 自キャラのアニメーション */
    if ( gl_nowTime - MyChara.time > ANIMATION_TIMING ) ChangeAnime(&MyChara);

    //--------------------------------------------------- 描画
    BackDraw(); // 背景
    MyCharaDraw(); // 自機

    //--------------------------------------------------- 移動処理
    MyCharaMove(); // 自機

    //--------------------------------------------------- 当たり判定

#ifdef DEBUG
    //--------------------------------------------------- ステージ切り替え(削除予定)
    if ( gl_KeyTbl[VK_F10] & 0x80 ) {
        ReleaseGameTexture();
        g_FrameNo = START_INIT;
    }
#endif

}

※以上の修正を行い、アニメーションが正しく行われるかを確かめる。


BACK(移動方向によって表示する画像を変える) NEXT(フォント・オブジェクト)