2011-02-05

Alternativa3Dで、アニメーションがフリップしてしまう問題(もしくはtimeパラメータがズレる問題)について

今個人的にしているゲーム制作で、かなりハマった問題をメモ代わりに報告。
Alternativa3Dのバージョンは7.6
状況としては、20fpsのゲームを作ろうとして、LightWaveで作ったアニメーションを、.x形式で出力した後、Ultimate Unwrap3Dで.daeに変換。
んで、そのアニメーションする3Dをゲームに使いたかったわけなのだけど、手足があっちこっち変な方向へブレてしまう問題が起きてしまったというもの。


Alternativa3Dのアニメーションは、かなり隠蔽されている。
Alternativa3Dの隠蔽というのは、ブラックボックス化して、アンタッチャブルな秘密主義にするってことで、ここの操作には触れることはもちろん、何をしているのか見ることもできない。
この悪い性質は本当に何とかしてほしい。
オープンソースにして規約で守る状態でも、特に悪いことがあるとも思えないのだけど。
話が逸れた。
とにかく、アニメーションは直接操作するわけではなく、どのチュートリアルを見ても、AnimationClipと、AnimationControllerを使えとある。
AnimationClipはアニメーションのデータで、daeファイルからパースすると、配列に入っているものを得ることができる。(全部動かさないと正しい動きにならない?)
んで、AnimationControllerをnewして、関連付けて、update()してやれば動く。
最小コードは多分以下。animationClipに既にデータがあるものとしている。

// 初期化
var animationController:AnimationController = new AnimationController();
animationController.root = animationClip;
// フレームごとの処理
animationController.update();

んで、これでアニメーションするのはいいんだけど、ゲームで使うんだから、特定箇所でループしたり、いきなりモーションが変わったりしたい。
それを実現するために、AnimationCoupleとか、AnimationSwitcherとか、高度なクラスが用意されている。
でも、僕が欲しいのは、秒数かフレーム数かを指定して、それをモデルに反映する機能だ。
だってそうでしょ、ループとか、切り替えとか、こっちでやるよ。
必要なら専用のクラスを作る。
中の見えないコントローラーとか使ってたら、いざバグった時に、原因を追えないじゃない。
というわけで、animationClipの中に、timeという項目を発見して、以下のようにしてみた。

// 初期化
var animationController:AnimationController = new AnimationController();
animationController.root = animationClip;
// フレームごとの処理
animationClip.time = frame / frameRate;
animationController.update();

ゲームだからね、管理は当然フレームベースでやってるので、フレームレートで割った値を代入している。
これで一見上手くいったのだけど、アニメーションの速度がちょっと変で、しかもよく見るとキャラクターの手足が微妙に変な方向を向いている瞬間ができてしまった。
これを、フリップと言うらしいのだけど、何しろLiteWave、Ultimate Unwrap3D、.dae、Alternativa3Dと、どいつもこいつも一癖ある段階を通ってきた3Dデータなので、どこで問題が起きたのかも分からず、随分と引っかかった。
結局、3つの問題が複合していたことがわかった。

フレーム間のモーションが、逆回転している箇所があった

これはつまり、フレームとフレームの間で、例えば本来右回転のはずが急に左回転して、360度ぐるっと回って戻ってきていることがあったというわけ。
今回のフリップ現象はこのフレーム間のモーションが見えたものらしい。
ただ、フレーム間でフリップを起こしていたとしても、Flashの表示でフレームの間が見えていなければ何も問題ないはずなのだ。
つまり、正確にフレームのタイミングで表示されていない・・・。

LightWave→Alternativa3Dの過程で、fpsが30になっている

ゲームを低めの20fpsで作るつもりだったので、LightWave上での設定もそうしていて、Flashでも1time = 20frameとして計算していた。
ところがなんと、timeの指定は30fpsを基準としていたようだ。
どこかの出力でfpsが30になっていたのか、Alternativa3Dが設定を無視してなぜか30fpsで解釈をするのかわからない。
とにかく、コードを以下にしてみた

// 初期化
var animationController:AnimationController = new AnimationController();
animationController.root = animationClip;
// フレームごとの処理
animationClip.time = frame / 30; // なぜか30固定
animationController.update();

これで、アニメーションの速度は大体期待通りになった。
ところが、フリップはまだ表示されていた。

update()の内部処理で、timeがズレている!

これ、超衝撃的だった。
update1度目はそんな処理していないらしいのだけど、2度目の処理から「timeに1/fpsを追加」→「モデルに反映」という処理をしているらしい。
で、普通に考えたら1コマズレるだけで何も問題ないようだけど、上記の30fpsと20fpsの問題で、本来1/30timeごとに追加される値が、1/20time追加されていたのだ。
それで、常にフレームとフレームの間の微妙なズレが生じてしまったというわけ。
つか、「モデルに反映」してから、「timeに1/fpsを追加」するんじゃダメなんですかね?
で、回避策なのだけど、animationController.freeze()を使用するか、animationClip.animated = false;で、timeの自動追加を止めることができる。
ここでもう1個罠があって、freeze()は、update()で自動的に解除される。なんだこの気持ち悪い仕様。
結局処理も軽そうなanimated = false;を使うことにした。
最終的なコードは以下

// 初期化
animationClip.animated = false; // 勝手にアニメーションやめて!
var animationController:AnimationController = new AnimationController();
animationController.root = animationClip;
// フレームごとの処理
animationClip.time = frame / 30; // なぜか30固定
animationController.update();

これで期待通りに動いた。
色々とツギハギで気持ち悪いし、他の3Dソフトで作っている人はこんな問題が起きないかもしれない。
とりあえず、Alternativa3Dのアニメーションに関する情報は少なすぎるので、誰かの何かの助けになったらと思う。

  • コメント
  • 0
  • トラックバック
  • 0

コメントする

ブログトップへ