【DXライブラリ】3Dモデルのアニメーションを再生する

DXライブラリで3Dモデルのアニメーションを再生する方法について説明しています。

アニメーション再生の基本

DXライブラリで3Dモデルのアニメーションを再生する方法は基本以下の通り。

  1. 再生したいアニメーションの番号を取得する
  2. 再生したいアニメーションを MV1AttachAnim() でアタッチする
  3. 再生したいアニメーションの総再生時間を MV1GetAttachAnimTotalTime() で取得する
  4. 再生時間を進める。総再生時間より大きくなった場合は0にする。
  5. 再生時間を MV1SetAttachAnimTime() セットする
  6. 別のアニメーションに切り替える場合は MV1DetachAnim() で現在のアニメーションをデタッチして1に戻る

アニメーションのアタッチ・デタッチについて、各アニメーションには番号が割り当てられているので任意の番号を指定する。アニメーション名や番号は DxlibModelViewer のアニメーションタブから確認できる。No.*が番号で横の文字列がアニメーション名。横のCountは総再生時間。

Blenderで作成したアニメーションの場合、Blenderで設定した名前の前に Armature| が付くので注意(他の3Dモデリングソフトでは未確認)。コード内でアニメーションを指定する際この Armature| をつけ忘れると再生できない。

またDXライブラリには自動でアニメーションを進める機能がないので自分で再生時間を設定する必要がある。

サンプルコード

では実装。今回は例として3Dモデルに以下の3つのアニメーションがあり、決定ボタンでそれらのアニメーションを切り替えていく内容を実装する。

  • Armature|Idle (入力が何もない時の待機モーション)
  • Armature|T-Pose (Tポーズ。実際のゲーム内で使う機会はまずない)
  • Armature|Walk (移動or歩行モーション)

あとゲーム上のフレームレートと3Dモデルのアニメーションのフレームレートは合わせておくこと。ここではどちらも60フレームレートとなっている前提で進めます。

Player.cpp/hにアニメーション関連の変数や関数を追加する。

Player.h

//以下の関数を追加
void Player_PlayAnimation();

Player.cpp

//プレイヤーの構造体を以下のように変更
typedef struct {
	int ModelHandle; //モデルハンドル
	VECTOR Position; //座標
	int AnimIndex; //任意のアニメーションの番号を取得するときに使う
	int AttachIndex; //アタッチするアニメーションの番号
	float TotalTime; //アニメーションの総再生時間
	float PlayTime; //アニメーションの再生時間
}player_t;

player_t player;

//以下の変数を追加
const static int AllPlayerAnimationNum = 2; //プレイヤーのアニメーション総数
static int test_count; //アニメーション切り替え用変数(今回限り)

//初期化関数に以下のコードを追加
void Player_Initialize() {
	//略

	//アニメーション関連の初期化
    //Idle という名前のアニメーションの番号を取得する
	player.AnimIndex = MV1GetAnimIndex(player.ModelHandle, "Armature|Idle");

	//取得したアニメーション番号のアニメーションをアタッチする
	player.AttachIndex = MV1AttachAnim(player.ModelHandle, player.AnimIndex, -1, FALSE);

	//アタッチしたアニメーションの総再生時間を取得する
	player.TotalTime = MV1GetAttachAnimTotalTime(player.ModelHandle, player.AttachIndex);

	//再生時間の初期化
	player.PlayTime = 0.0f;

	test_count = 0;
}

//プレイヤーのアニメーション処理を行う関数を追加
void Player_PlayAnimation() {
    //再生時間を進める
    player.PlayTime += 1.0f;

    //再生時間がアニメーションの総再生時間に達したら再生時間を0に戻す
    if (player.PlayTime >= player.TotalTime) player.PlayTime = 0.0f;

    //決定ボタンが押されたら再生するアニメーションを変更する
    if (Input_GetGamepadDown(config.confirm)) {
        test_count++;
        if (test_count > AllPlayerAnimationNum) test_count = 0;

        //今までアタッチしていたアニメーションのデタッチ
        MV1DetachAnim(player.ModelHandle, player.AttachIndex);

        if (test_count == 0) {
            //Idle という名前のアニメーションの番号を取得する
            player.AnimIndex = MV1GetAnimIndex(player.ModelHandle, "Armature|Idle");

            //取得したアニメーション番号のアニメーションをアタッチする
            player.AttachIndex = MV1AttachAnim(player.ModelHandle, player.AnimIndex, -1, FALSE);

            //アタッチしたアニメーションの総再生時間を取得する
            player.TotalTime = MV1GetAttachAnimTotalTime(player.ModelHandle, player.AttachIndex);
        }
        else if (test_count == 1) {
            //T-Pose という名前のアニメーションの番号を取得する
            player.AnimIndex = MV1GetAnimIndex(player.ModelHandle, "Armature|T-Pose");

            //取得したアニメーション番号のアニメーションをアタッチする
            player.AttachIndex = MV1AttachAnim(player.ModelHandle, player.AnimIndex, -1, FALSE);

            //アタッチしたアニメーションの総再生時間を取得する
            player.TotalTime = MV1GetAttachAnimTotalTime(player.ModelHandle, player.AttachIndex);
        }
        else if (test_count == 2) {
            //Walk という名前のアニメーションの番号を取得する
            player.AnimIndex = MV1GetAnimIndex(player.ModelHandle, "Armature|Walk");

            //取得したアニメーション番号のアニメーションをアタッチする
            player.AttachIndex = MV1AttachAnim(player.ModelHandle, player.AnimIndex, -1, FALSE);

            //アタッチしたアニメーションの総再生時間を取得する
            player.TotalTime = MV1GetAttachAnimTotalTime(player.ModelHandle, player.AttachIndex);
        }
        //再生時間の初期化
        player.PlayTime = 0.0f;
    }
    //再生時間をセットする
    MV1SetAttachAnimTime(player.ModelHandle, player.AttachIndex, player.PlayTime);
}

//更新関数に以下のコードを追加
void Player_Update() {
	//アニメーション処理
	Player_PlayAnimation();
}

//略

まず Player_Initialize() でアニメーション関連の変数を初期化しておく。再生するアニメーションを待機モーションにしておき、再生時間を0にする。

再生するアニメーションの番号について、ここではMV1GetAnimIndex でアニメーション名を検索して番号を取得する方法を採用。

宣言 int MV1GetAnimIndex( int MHandle, char *AnimName )
概要 指定名のアニメーション番号を取得する
引数 int MHandle 3Dモデルのハンドル
char *AnimName 番号を取得したいアニメーション名の名前
戻り値 -1以外 アニメーション番号
-1 エラー発生

アニメーション番号を取得したら MV1AttachAnim() でアニメーションをアタッチし、再生時間も MV1SetAttachAnimTime() で0に設定する。

宣言 int MV1AttachAnim(int MHandle,int AnimIndex,int AnimSrcMHandle,int NameCheck)
概要 アニメーションをアタッチする
引数 int MHandle アタッチする3Dモデルのハンドル
int AnimIndex アタッチするアニメーション番号
int AnimSrcMHandle アタッチするアニメーションを持っている3Dモデルのハンドル
int NameCheck 別のモデルのアニメーションをアタッチするときに使う。長いので本家ページを参照
戻り値 -1以外 アニメーションアタッチ番号
-1 エラー発生
宣言 int MV1SetAttachAnimTime( int MHandle, int AttachIndex, float Time )
概要 アタッチしているアニメーションの再生時間を設定する
引数 int MHandle 3Dモデルのハンドル
int AttachIndex 再生時間を設定するアニメーションのアタッチ番号
float Time 再生時間
戻り値 0 正常
-1 エラー発生

アニメーションの更新は Player_PlayAnimation() で行う。初期化の時同様アニメーションの番号を取得し、アタッチする。アニメーションを切り替える場合はデタッチを MV1DetachAnim() で行う。デタッチを忘れるとアニメーションがおかしくなるので注意。

宣言 int MV1DetachAnim( int MHandle, int AttachIndex )
概要 アニメーションをデタッチする
引数 int MHandle 3Dモデルのハンドル
int AttachIndex デタッチするアニメーションのアタッチ番号
戻り値 0 正常
-1 エラー発生

最後に MV1SetAttachAnimTime で再生時間を設定するのを忘れずに。

再生するアニメーションによってはポーズの切り替わるがはっきりとわかって違和感を覚えるかもしれない。その場合は MV1SetAttachAnimBlendRate を使って2つのポーズをブレンドしてあげると違和感が減る。

コメント

タイトルとURLをコピーしました