トップページ > 記事閲覧
コンストラクタ内の MV1AttachAnim が動かない
名前:sarrus3x3 日時: 2018/05/12 15:26

管理人殿 いつも質問に答えていただき、ありがとうございます。 ゲーム制作をしていたら、 DXライブラリがうまく動かないようにみえる事象に遭遇したため、 相談させてください。 DXライブラリの3D関係関数を使って、 ゲームを作らせていただいております。 事象内容についてですが、 アニメーション再生を管理するための構造体を定義して、 モーションブレンドなども考慮して、 現在再生中のアニメーションをこの構造体のvector配列として 保持させようとしました。 この構造体のコンストラクタの中で、 MV1AttachAnim を実行してアニメーションをアタッチする実装にしたのですが、 vector配列でinsertを使って構造体を追加すると、 コンストラクタ内で MV1AttachAnim がうまく動作しません。 (アニメーションアタッチ番号が全て 0 になってしまいます) コンストラクタ内で他のメンバ変数の初期化は 正しく行われておりました。 insert経由で、コンストラクタ内で MV1AttachAnim が実行されると 上手く動かないということがあるのでしょうか? なお構造体を new で生成した場合は、 MV1AttachAnim は正常に機能することを確認しました。 回避策はあるので困っているわけではないのですが、 なぜうまく動作しないのかが気になるので、 お手数ですがご解析いただくことは可能でしょうか。 当方の環境は、 環境はVS2017、DXライブラリバージョンはVer3.18a(Log.txtから)です。 以下に問題が発生したソースを記載します。 ------------------------------------------------------------------------------- #include "DxLib.h" // #### アニメーション再生管理構造体 struct AnimPlayBackInfo { // モーションのアタッチインデックス( MV1AttachAnim のリターン値 ) int m_AttachIndex; // アニメーション再生情報 float m_CurPlayTime; // 現在の再生時間 int m_iModelHandle; // モデルハンドル int m_iAttachedMotion; // 適用中のモーション( 初音ミクxxx.vmd ← xxxの番号 ) AnimPlayBackInfo(int ModelHandle, int AttachedMotion); // コンストラクタ ~AnimPlayBackInfo(); // デストラクタ void ReAttach(); // MV1AttachAnim を行うだけ。 }; // コンストラクタ AnimPlayBackInfo::AnimPlayBackInfo(int ModelHandle, int AttachedMotion) : m_iModelHandle(ModelHandle), m_iAttachedMotion(AttachedMotion), m_CurPlayTime(0.0) { m_AttachIndex = MV1AttachAnim(ModelHandle, AttachedMotion, -1, FALSE); } // デストラクタ AnimPlayBackInfo::~AnimPlayBackInfo() { MV1DetachAnim(m_iModelHandle, m_AttachIndex); // 古いアニメーションのデタッチ(デタッチしないとAnimationが混じって変なことになる) } // MV1AttachAnim を行うだけ。 void AnimPlayBackInfo::ReAttach() { m_AttachIndex = MV1AttachAnim(m_iModelHandle, m_iAttachedMotion, -1, FALSE); } // ############################################################################# // main // ############################################################################# int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { // ################## DXライブラリ関係の初期設定 ####################### if( ChangeWindowMode(TRUE) != DX_CHANGESCREEN_OK || DxLib_Init() == -1 ) return -1; //初期化処理 SetDrawScreen( DX_SCREEN_BACK ); // 裏画面に設定 SetCameraNearFar( 1.0f, 1500.0f ) ; // カメラの 手前クリップ距離と 奥クリップ距離を設定 // ################## 変数の定義・初期化 ####################### // タイマを生成 ★自作クラス★ PrecisionTimer timer; // 仮想コントローラーのインスタンス化 ★自作クラス★ VirtualController VController; VController.CheckAndSetGamePadMode(); // ゲームパッドが使えるのなら有効にする // AnimationManagerのインスタンス化 vector<AnimPlayBackInfo> AnimPlayInfoArray; int curAnimID = 3; int ModelHandle = MV1LoadModel( "..\\mmd_model\\初音ミク.pmd" ); // 新しいモーションを生成 AnimPlayInfoArray.insert(AnimPlayInfoArray.begin(), AnimPlayBackInfo(ModelHandle, curAnimID)); // ################## メインループの開始 ####################### timer.Start(); // タイマ活性化 while(!ScreenFlip() && !ProcessMessage() && !ClearDrawScreen()){ // タイムスライスを取得 double timeelaps = timer.TimeElapsed(); // ################## コントローラーを更新 ####################### VController.Update(timeelaps); // ################## キー入力受付 ####################### if( VController.ButA.isNowPush() ) { // 次のAnimationを表示 curAnimID++; if (!(curAnimID < 15)) curAnimID = 3; AnimPlayInfoArray.clear(); AnimPlayInfoArray.insert(AnimPlayInfoArray.begin(), AnimPlayBackInfo(ModelHandle, curAnimID)); // AnimPlayInfoArray[0].ReAttach(); // ← ★これを有効にすると、アニメーションが正常に再生される! } // ################## アニメーションの再生 ####################### AnimPlayInfoArray[0].m_CurPlayTime += 20.0 * timeelaps; // curplaytime : 次の再生時刻 MV1SetAttachAnimTime(AnimPlayInfoArray[0].m_iModelHandle, AnimPlayInfoArray[0].m_AttachIndex, AnimPlayInfoArray[0].m_CurPlayTime ); // 再生時間をセットする MV1DrawModel( ModelHandle ); // モデルの描画 // ################## カメラ設定 ####################### // ★ CameraWorkManager は自作クラス ★ CameraWorkManager::Instance()->Update( timeelaps ); CameraWorkManager::Instance()->setCamera(); } DxLib_End() ; // DXライブラリ使用の終了処理 return 0 ; // ソフトの終了 } -------------------------------------------------------------------------------
メンテ

Page: 1 |

Re: コンストラクタ内の MV1AttachAnim が動かない ( No.1 )
名前:C-- 日時:2018/05/12 19:22

コンストラクタが働かないのではなく、 コンストラクタのあと すぐにデストラクタが呼ばれているからではないでしょうか コンストラクタ、デストラクタ、AnimPlayInfoArray.insert(AnimPlayInfoArray.begin(), AnimPlayBackInfo(ModelHandle, curAnimID)); の行に ブレークポイントを置き、ステップ実行させてデストラクタがどう呼ばれるかを確認してみる必要があると思います
メンテ
Re: コンストラクタ内の MV1AttachAnim が動かない ( No.2 )
名前:sarrus3x3(解決) 日時:2018/05/13 13:40

C-- 様、 ご回答ありがとうございます。 ブレークポイントを仕掛けて確認したところ、 vector::insertを実行した直後に 確かにデストラクタが呼ばれていることが確認できました。 よく考えると、 vector::insertが呼ばれたときの動きとして、 一時的に構造体のインスタンスが生成されてそれがvector配列にコピーされ、 一時的に生成されたインスタンスは削除されるので、 その時にデストラクタが呼ばれているのですね。 insert時以外にも、vectorが確保したメモリの再配置などでも、 デストラクタが呼ばれるということなので、 デストラクタ内でデタッチ処理をするのは誤っていました。 もともと、アタッチ、デタッチ処理をコンストラクタ、デストラクタ内で 実行すればシンプルになっていいなと思っていたのですが、 諦めてアタッチ/デタッチ用の別のメソッドを作成して、 手動で制御するようにします。 ありがとうございました。
メンテ
Re: コンストラクタ内の MV1AttachAnim が動かない ( No.3 )
名前:yumetodo 日時:2018/05/14 09:18

諦めるのはまだ早いです。std::vector<AnimPlayBackInfo>ではなくstd::vector<std::shared_ptr<AnimPlayBackInfo>>とすればいいです
メンテ
Re: コンストラクタ内の MV1AttachAnim が動かない ( No.4 )
名前:sarrus3x3(解決) 日時:2018/05/14 23:13

yumetodo 様、 ご回答ありがとうございます。 std::shared_ptr について調べてみました。 > 複数のshared_ptrオブジェクトが同じリソースを共有し、所有者が0人、 > つまりどのshared_ptrオブジェクトからもリソースが参照されなくなると、リソースが自動的に解放される。 https://cpprefjp.github.io/reference/memory/shared_ptr.html おー、これはまさしくガベージコレクションではないですか! なるほど、 実体はshared_ptrに管理させて、vectorが操作するのはポインタにしておけば、 vectorの内部処理で生成削除が行われても、本当に実体を作ったときと削除するときしか コンストラクタ・デストラクタが呼ばれないようにするということですね! 以下のように修正してきちんと動くことを確認しました。 ありがとうございます。m(_ _)m ------------------------------------------------------------------------------- // ★★★★★追加★★★★★ #include <memory> // shared_ptr を使用するために必要(→https://msdn.microsoft.com/ja-jp/library/bb982026.aspx) // ★★★★★★★★★★★★ #include "DxLib.h" // #### アニメーション再生管理構造体 /* ここは変えてないので、省略 */ // ############################################################################# // main // ############################################################################# int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { // ################## DXライブラリ関係の初期設定 ####################### if( ChangeWindowMode(TRUE) != DX_CHANGESCREEN_OK || DxLib_Init() == -1 ) return -1; //初期化処理 SetDrawScreen( DX_SCREEN_BACK ); // 裏画面に設定 SetCameraNearFar( 1.0f, 1500.0f ) ; // カメラの 手前クリップ距離と 奥クリップ距離を設定 // ################## 変数の定義・初期化 ####################### // タイマを生成 PrecisionTimer timer; // ★自作クラス★ // 仮想コントローラーのインスタンス化 ★自作クラス★ VirtualController VController; VController.CheckAndSetGamePadMode(); // ゲームパッドが使えるのなら有効にする // AnimationManagerのインスタンス化 // ★★★★★変更★★★★★ vector<shared_ptr<AnimPlayBackInfo>> AnimPlayInfoArray; // スマートポインタのvector配列に変更。 // ★★★★★★★★★★★★ int curAnimID = 3; int ModelHandle = MV1LoadModel( "..\\mmd_model\\初音ミク.pmd" ); // 新しいモーションを生成 // ★★★★★変更★★★★★ AnimPlayInfoArray.insert(AnimPlayInfoArray.begin(), make_shared<AnimPlayBackInfo>(ModelHandle, curAnimID)); // ★★★★★★★★★★★★ // ################## メインループの開始 ####################### timer.Start(); // タイマ活性化 while(!ScreenFlip() && !ProcessMessage() && !ClearDrawScreen()){ // タイムスライスを取得 double timeelaps = timer.TimeElapsed(); // ################## コントローラーを更新 ####################### VController.Update(timeelaps); // ################## その他キー入力受付 ####################### if( VController.ButA.isNowPush() ) { // 次のAnimationを表示 curAnimID++; if (!(curAnimID < 15)) curAnimID = 3; AnimPlayInfoArray.clear(); // ★★★★★変更★★★★★ AnimPlayInfoArray.insert(AnimPlayInfoArray.begin(), make_shared<AnimPlayBackInfo>(ModelHandle, curAnimID) ); // ★★★★★★★★★★★★ } // ################## アニメーションの再生 ####################### // ★★★★★★★★★★★変更★★★★★★★★★★★★ // ★  格納物がポインタになるので"."→"->"に!   ★ AnimPlayInfoArray[0]->m_CurPlayTime += 20.0 * timeelaps; // curplaytime : 次の再生時刻 MV1SetAttachAnimTime(AnimPlayInfoArray[0]->m_iModelHandle, AnimPlayInfoArray[0]->m_AttachIndex, AnimPlayInfoArray[0]->m_CurPlayTime ); // ★★★★★★★★★★★★★★★★★★★★★★★★★ MV1DrawModel( ModelHandle ); // モデルの描画 // ################## カメラ設定 ####################### // ★ CameraWorkManager は自作クラス ★ CameraWorkManager::Instance()->Update( timeelaps ); CameraWorkManager::Instance()->setCamera(); } DxLib_End() ; // DXライブラリ使用の終了処理 return 0 ; // ソフトの終了 } -------------------------------------------------------------------------------
メンテ
Re: コンストラクタ内の MV1AttachAnim が動かない ( No.5 )
名前:yumetodo 日時:2018/05/15 16:17

話がそれますが、vectorの先頭への挿入は重い操作なので、vector::emplace_backを用いて末尾挿入に書き換えたほうがいいと思います。 その場合AnimPlayInfoArray[0]ではなくAnimPlayInfoArray.back()ですね ttps://cpprefjp.github.io/reference/vector/emplace_back.html ttps://cpprefjp.github.io/reference/vector/back.html
メンテ
Re: コンストラクタ内の MV1AttachAnim が動かない ( No.6 )
名前:sarrus3x3(解決) 日時:2018/05/16 20:14

yumetodo 様、 鋭い指摘ありがとうございます。 vector配列の場合、要素数が多いとinsert()処理が重くなることは承知していたのですが、 ここでは省いてしてまっていますが、ブレンド制御の都合上 前積みの方が実装が楽になるのでそこは割り切ってしまっていました。 いつか手が空いたらリファクタリングします。。。
メンテ

Page: 1 |

題名
名前
コメント
パスワード (記事メンテ時に使用)

   クッキー保存