FLASH

2010年12月09日

modoからFLASHへ書き出してみた その36 modo 401 SP5 ActionScript 3.0

気がつけば今年もあと20日ばかり・・・。忙しい時に限ってトラブルも湧いて来て右往左往しております。

さて、基点やピボットなどの関係がわかったのでこんなシーンを作ってPaperVision3Dに書き出す事を考えてみたい。

fig02

fig01アイテムの構成としてはこんな感じで、各パーツは中心点を動かさずにピボットだけそれぞれの根元に移動してある。

このアイテムリストの一番根元の「tekubi」アイテムを選択してスクリプトを実行した時にペアレント関係にある下位のアイテム全てをペアレント関係を保ちながらPaperVision3Dに移し、アニメーションデータも書き出せるようにするのが当座の目標だ。 まず選択されているアイテムが何であるかを特定するために

query sceneservice selection ? all

を実行する。セレクタをallにすると必要ないものまで選択されてしまうので、取得した中で

query sceneservice item.type ? アイテムID

を使ってから「locator 、groupLocator 、mesh、 meshInst」のタイプであるかどうかを調べる。この時アイテムが1つに絞り込めなかったらエラーを出して終了する。

アイテムが1つ見つかったら、そのアイテムがメッシュだったら頂点座標値、ポリゴン構成データ、UV座標値を調べる。メッシュインスタンスだったらオリジナルアイテムを見つけ出す。

このアイテムの変換アイテムの構成を

query sceneservice item.xfrmItems ? アイテムID

で調べ、 各チャンネルのキーデータを取得する。次に

query sceneservice item.children ? アイテムID

を使って子のアイテムにペアレントされている子のアイテムのリストを取得し、それらリスト中のアイテムに対して同様の処理をしてアイテムの

  • 形状データ
  • 形状データのピボットポイントに対するローカル変位データ
  • ピボットポイントのアニメーション
  • このアイテムにペアレントされている子のアイテムリスト
  • 子のアイテムのデータ

を取得する。これで得られた形状データごとに3Dメッシュオブジェクトのインスタンスを作成して、それをDisplayObject3Dのインスタンスにペアレントして、ローカル座標位置をピボット座標の符号をひっくり返した値にして、ピボット座標系がオブジェクトの座標変換の基準になるようにするようにする。そしてDisplayObject3Dのインスタンスの方に変換チャンネルのクラスのインスタンスを関連付けて、そこにキーデータを格納し、オブジェクト全体をまとめたクラスに格納し、アニメーションコントローラに登録する。

こんな動作をするオブジェクトを生成するコードをスクリプトで出力させてFLASHに読み込ませ、そのクラスからインスタンスを生成させてシーンにscene.addChild( )で追加し、アニメーションさせたい時にオブジェクトのアニメーションコントローラのplay( )メソッドを呼び出して再生出来たらOKだ。

まずはこんな方針で行こうと思う。コードはまだ書いてないけどね。

それではまた次回。

modoカテゴリー別ページ



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)

2010年12月01日

modoからFLASHへ書き出してみた その35 modo 401 SP5 ActionScript 3.0

前回はmodoのアイテムの回転やスケールの基準になるピボットとか、その補正のためのピボットコンペンセイションとかを調べた。
実はmodoにはもうひとつ変換パラメータを補正する仕組みがある。それがゼロ補正のチャンネルだ。
例えば底面の直径1m、高さ1.5mの円錐を原点に作成して、

fig01

アイテムの位置Yに750mmを設定すると

fig04

底面が原点の位置にくる。

fig03

ここでゼロプルダウンメニューから「位置」を選ぶと

fig02

円錐の位置はそのままでアイテム座標だけ(0,0,0)になる。

fig05

これは一見すると位置をフリーズさせた場合や中心点アイテムを円錐の底面に移動させた場合、メッシュの頂点を移動ツールでY軸方向に0.75移動させた場合と同じように見える。

しかし円錐の一番上のポイントの座標値をクエリしてみると、

fig14

fig06

となり、高さ1.5mの円錐の頂点がアイテムローカル座標の原点から0.75mの高さにあることがわかる。ちなみにこの頂点のワールド座標をとってみるとY座標が1.5mになっている。

fig07

ここでチャンネルを見てみると、「Position Zero」というチャンネルが追加され、ここにアイテムの変位が隠蔽されている事がわかる。

fig08

ゼロ化は回転やスケールも出来るので、ピボットも含めるとmodoのアイテムに対する変換のチャンネルは、

  • Position
  • Position Zero
  • Rotation
  • Rotation Zero
  • Scale
  • Scale Zero
  • Pivot Position
  • Pivot Position Compensation

の8通りも存在する事がわかった。ややこしすぎる・・・。で、結局PaperVIsion3Dに出す時にこれらをどう扱えばいいのかだけど、次の2つの方法が考えられる。

  1. ピボットの位置がオブジェクトのローカル座標原点になるように座標変換する
  2. メッシュオブジェクトをDisplayObject3Dの子にしてDisplayObject3Dのローカル座標原点がピボットポイントになるようにする

アイテムの親座標系から見たピボットの座標は、Position、Position Zero、Pivot Position、Pivot Position Conpensationを全て足したものなので、1の方法を使うならその合計をポイントのローカル座標値から引けばいい。

2の場合はその座標の合計値をDisplayObject3Dの座標値にして、それにペアレントするメッシュオブジェクトの方はPivot Positionの座標値の符号を斑点したものをセットすればいい。スケールや回転は全て親のDisplayObject3Dに対して行えば、ピボットポイントを基準に変換されるのと同じ効果になるはずだ。

ちなみに特定のアイテムのこれらのチャンネルにアクセスするにはまず

query sceneservice item.xfrmItems ? アイテムID

のクエリをして、リンクアイテムを探し出す必要がある。以前のバージョンでは下の例のようにアイテムタイプとアイテムIDがイコールで結ばれた形で出力されていたけど、

fig09

現在は単にアイテムIDが列挙されるだけになったようだ。

fig10

変換アイテムはメッシュアイテムの初期状態では存在せず、アイテムの移動や回転など、これらのアイテムが必要になった時に自動的に追加される。だから最大でこれだけ出るけど、何個のアイテムが見つかるかはそのアイテム次第だ。

これら列挙されたアイテムIDは名前を見ればどのタイプかわかるけど、一応アイテムタイプを調べるには、

fig11

などとしてやればいい。ただし、これだけだとピボットポイントもポジションも全部タイプが「translation」で区別が付かない。必要なら以下のクエリでそれぞれのアイテムIDを入手すればいい。ZeroについてはxhurmItemsで得られたリストの中でこれらのクエリで得られないものを探せば見つかるはずだ。

  • query sceneservice item.xfrmPiv ? アイテムID
  • query sceneservice item.xfrmPivC ? アイテムID
  • query sceneservice item.xfrmPos ? アイテムID
  • query sceneservice item.xfrmRot ? アイテムID
  • query sceneservice item.xfrmScl ? アイテムID

アイテムが見つかったらそのアイテムの名前とかをクエリしてやることで作業対象のアイテムとし、チャンネルに対してクエリすればいい。

fig12

そのアイテムにいくつのチャンネルがあるかはchannel.Nのアトリビュートで求まるし、その番号内でchannel.nameアトリビュートをクエリすればチャンネル名を知る事が出来る(チャンネルリストをクリックしてコマンド履歴を見ても読み取れる)。このチャンネル名を使ってchannel.valueアトリビュートをクエリすれば現在時刻の座標値などを求める事が出来る(もちろんチャンネル番号でもOK)。

それではまた次回。

modoカテゴリー別ページ




take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)

2010年11月18日

modoからFLASHへ書き出してみた その34 modo 401 SP5 ActionScript 3.0

前回はPapervision3Dの位置・回転・スケールを扱うクラスを確認した。そこで今回はmodo側の方を調べてみたい。

modoは3D形状をメッシュアイテムという入れ物の中で管理していて、その中に頂点やエッジ、ポリゴンなどのデータが収められている。そしてこれとは別に、このアイテムの変換情報を管理するアイテムが複数存在し、メッシュアイテムとリンクする格好になっている。これらの構成はチャンネルリストを見るとだいたい表示されていて、Meshアイテムの状態によって、Meshアイテム単独のものもあれば、下の画像のようにMeshアイテムの他に5つの変換アイテムがリンクされているような場合もある。

fig01

このリストの中で、Position:位置、Rotation:回転、Scale:倍率は名前から容易に機能がわかる。残りのPivot〜はMeshアイテムの回転やスケールの基準となる位置とその補正値だ。

例えば下の画像のように中心間距離1mの直径1mの球体が円柱でつながれたようなオブジェクトがあったとして、このオブジェクトのローカル座標の原点が円柱の真ん中にあった場合、

fig02

アイテムプロパティの回転Zを90にすると、

fig04

ローカル座標原点を基準に90度回転する。

fig03

これを左側(X軸マイナス側)の球体の中心を軸に回転させたい場合、ポリゴンメッシュを移動ツールでX軸プラス側へ1m移動させて左側の球体の中心がローカル座標の原点にくるように移動させ、

fig05

今度はアイテムプロパティの位置Xに−1mを設定してアイテム全体を左に1m移動させると、もとの状態のままローカル座標の原点が左の球体の中心に移るので、

fig06

アイテムプロパティの回転Zに90を設定すると、左側の球を中心に回転してこのようになる。

fig07

この中心点移動は中心点アイコンの移動によって行う事も出来て、プルダウンメニューから「センター」を選ぶとビューポートにアイテムのローカル中心を示す小さい丸が出てくるので、

fig08

これを選択してX軸方向に−1m移動させればアイテムのローカル中心を左の球体の中心に持って行く事が出来る。この時アイテムプロパティの位置Xが−1に補正される。要するに先にやった結果と同じ状態になったわけだ。

fig09

アイテムプロパティの回転Zを90にすれば左の球体を中心に回転する。

fig10

このようにローカル座標の原点の移動+アイテムの移動による移動の打ち消しによって回転やスケールの中心を移動させる替わりに回転やスケールの中心だけを扱えるのがピボットだ。アイコンは白黒のツートンカラーだ。

fig11

これを中心点を移動したのと同じ要領で移動してやれば、今度はアイテムプロパティの位置Xの値が補正される事もなく、ピボットポイントだけが移動し、

fig12

アイテムプロパティの回転Zを90にするとこの通り。ピボットポイントを中心に回転する。

fig13

この状態でチャンネルリストを見るとこのようにPivotPositionが(−1,0,0)に、Rotationが(0,0,90)なっている。

fig14

この状態でセンターを表示してみると、ピボットを中心とした回転によって下の画像のように移動している。

fig15

この時点でワールド座標原点とローカル座標原点はずれているわけだ。しかしアイテムプロパティの位置XYZは(0,0,0)のままだ。

ここでピボットポイントを上の図のセンターに合わせたらどうなるだろう?

fig16


ピボットポイントの移動はアイテムプロパティの位置パラメータには影響を与えない。だからアイテムプロパティの位置パラメータは(0,0,0)を示したままだ。

さらにアイテムプロパティの回転Zを0に戻してもとの位置と比較してみると、ピボットとローカル中心が一致し、位置・回転・スケールのパラメータも同じであるにも係わらずこれだけの差が出た。

fig17

この変位部分がPivot Position Compensationだ。チャンネルリストを開けてみると、このように現在のアイテムの変位がPivot Position Compensationに出ている事が確認できる。

fig18

一見おかしな状態にも思えるけど、もしこのPivot Position Compensationが無かったらどうなるだろう?上記のようにピボットポイントを移動しながら回転させた場合に、ローカルセンターと親座標との間に矛盾が生じないようにするためにはアイテムを移動しなくちゃならなくなるはずだ。それを補正してアイテムを見かけ上移動していないように見せているのがPivot Position Compensationの役割なわけだ。

modoからアイテムの状態を読み取ってPaperVision3Dに持ち込むにはこれらの関係をきちんと抑えておく必要があるわけだね。

続きはまた次回。

modoカテゴリー別ページ



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)

2010年11月11日

modoからFLASHへ書き出してみた その33 modo 401 SP4 ActionScript 3.0

ムービークリップアニメーションについてはなんとかなったので、改めてスキンメッシュアニメーションに戻りたいところだけど、その前にオブジェクトの位置・回転・スケールについてAnimationControllerで扱う方法について調べてみたい。そうしないとスキンメッシュ変形させる時のジョイントのアニメーションが出来ないからね。

で、調べてみたら位置・回転・スケールのアニメーションデータを扱うチャンネルクラスとして、

  • org.papervision3d.core.animation.channel.transform.TranslationChannel3D
  • org.papervision3d.core.animation.channel.transform.RotationChannel3D
  • org.papervision3d.core.animation.channel.transform.ScaleChannel3D

があることがわかった。

ChannelクラスはAnimationControllerにaddChannelメソッドで追加し、playメソッドで再生すると、AnimationControllerによって管理された時刻によってChannelクラスのupdateメソッドが呼び出され、その時刻におけるチャンネルデータが得られる仕組みになっている。その出力がChannelクラスのtransformプロパティで、Matrix3Dクラスのインスタンスになっている。

Papervision3Dの扱う球体や立方体、TraiangleMesh3Dなどのオブジェクトは全てDisplayObject3Dクラスがベースクラスにあって、このtransformプロパティを持っている。そして位置・回転・スケールは最終的にこのtransformプロパティのMatrix3Dクラスのインスタンスの4行4列の行列データとして扱われる。

だからChannnelクラスの出力であるtransformプロパティをアニメーションさせたいオブジェクトのtransformプロパティにコピーしてやれば、オブジェクトをアニメーションさせることが出来るわけだ。ただし位置・回転・スケールそれぞれのアニメーションをする場合、コピーだと最後にコピーしたチャンネルの変位しか実現できない。そこでコピーする前に3つのチャンネルの変位を合成する必要がある。

この4行4列の行列は平行移動や回転、スケールなどの座標変換を記述でき、行列を掛け合わせてるとそれらの座標変換が合成できる性質をもっている。だからX方向に30移動させる行列とY軸まわりに60度回転させる行列を掛け合わせると、X方向に30移動してY軸まわりに60度回転させる行列を作る事が出来る。この場合、移動と回転の順番は掛け合わせる順番に依存する。

話がややこしいのでまずは位置が変化するだけのアニメーションを作ってみた。

まずはアニメーションコントローラを内臓させたConeの拡張クラスtest_transform_object

package  {
 import org.papervision3d.objects.primitives.Cone;
 import org.papervision3d.core.proto.MaterialObject3D;
 import org.papervision3d.core.controller.AnimationController;
 import org.papervision3d.core.animation.channel.transform.TranslationChannel3D;
 import org.papervision3d.objects.DisplayObject3D;
 import org.papervision3d.core.render.data.RenderSessionData;
 import org.papervision3d.core.math.Matrix3D;

 public class test_transform_object extends  Cone{
  public var animation:AnimationController=new AnimationController();
  public function test_transform_object(material:MaterialObject3D=null,radius:Number=100,height:Number=100,segmentsW:int=8,segmentsH:Number=6) {
   super(material,radius,height,segmentsW,segmentsH);
  }
  
  public override function project(parent:DisplayObject3D, renderSessionData:RenderSessionData):Number
  {
   animation.update();
   this.transform = TranslationChannel3D(animation.channels[0]).transform;
   return super.project(parent, renderSessionData);
  }  
 }
}

オブジェクトのprojectメソッドはオブジェクトが描画される時に呼び出されるメソッドで、FLASHがフレームを更新するたびにシーンが書き換えられ、そのシーンに入っているオブジェクトがみんな更新されるから、projectメソッドはフレーム更新のたびに呼び出される。ここにAnimationControllerのupdateメソッド書いておけば、AnimationControllerは機能するわけだ。そしてplayメソッドでアニメーションが開始されると、登録しているチャンネルのupdateメソッドを呼び出してチャンネルの出力を現在時刻で更新してくれるわけだ。そこでanimation.update()のあとなら更新したチャンネル出力が得られる。これを取り出して自分自身のtransformプロパティに代入しているのが次の行なわけだ。チャンネルは1つしか登録してないので、AnimationController内臓のchannels配列の0番にアクセスすれば得られる。それをTranslationChannel3Dクラスとしてキャストして、そのtransformプロパティにアクセスしているわけだ。

次にこのクラスのインスタンスを作成してシーンに追加して表示するクラスtest_transform

package  {
 import org.papervision3d.view.BasicView;
 import org.papervision3d.objects.primitives.Sphere;
 import org.papervision3d.materials.MovieMaterial;
 import org.papervision3d.materials.MovieAssetMaterial;
 import org.papervision3d.core.animation.clip.AnimationClip3D;
 import flash.display.MovieClip;
 import flash.geom.Rectangle;
 import flash.events.Event;
 import flash.events.MouseEvent;
 import org.papervision3d.core.animation.channel.Channel3D;
 import org.papervision3d.materials.utils.MaterialsList;
 import org.papervision3d.core.proto.MaterialObject3D;
 import org.papervision3d.materials.ColorMaterial;

 import org.papervision3d.core.animation.channel.Channel3D;
 import org.papervision3d.core.animation.channel.transform.MatrixChannel3D;
 import org.papervision3d.core.animation.curve.Curve3D;
 import org.papervision3d.core.animation.key.LinearCurveKey3D;
 import org.papervision3d.core.animation.channel.transform.TranslationChannel3D;

 public class test_transform extends BasicView{
  public function test_transform() {
   var material:ColorMaterial = new ColorMaterial();
   var obj:test_transform_object = new test_transform_object(material,100,150,24,4);

   var chTr:TranslationChannel3D = new TranslationChannel3D(null);
   var cvTrX:Curve3D=new Curve3D();
   var cvTrY:Curve3D=new Curve3D();
   var cvTrZ:Curve3D=new Curve3D();
   cvTrX.addKey(new LinearCurveKey3D(0,-50));
   cvTrX.addKey(new LinearCurveKey3D(1,50));
   cvTrX.addKey(new LinearCurveKey3D(2,-50));
   cvTrY.addKey(new LinearCurveKey3D(0,0));
   cvTrY.addKey(new LinearCurveKey3D(0.5,50));
   cvTrY.addKey(new LinearCurveKey3D(1,0));
   cvTrZ.addKey(new LinearCurveKey3D(0,0));
   chTr.addCurve(cvTrX);
   chTr.addCurve(cvTrY);
   chTr.addCurve(cvTrZ);
   obj.animation.addChannel(chTr);

   scene.addChild(obj);
   
   camera.x=0;
   camera.y=200;
   camera.z=-300;

   startRendering();

   obj.animation.play();
  }
 }
}

内容はこんな感じ。

TranslationChannel3DはXYZ3つのカーブデータでアニメーションを表わす必要があるので、cvTrX、cvTrY、cvTrZの3つのカーブを作り、

var cvTrX:Curve3D=new Curve3D();
var cvTrY:Curve3D=new Curve3D();
var cvTrZ:Curve3D=new Curve3D();

それぞれのカーブにキーを追加して時刻とその時のカーブの値をセットした。

   cvTrX.addKey(new LinearCurveKey3D(0,-50));
   cvTrX.addKey(new LinearCurveKey3D(1,50));
   cvTrX.addKey(new LinearCurveKey3D(2,-50));

   cvTrY.addKey(new LinearCurveKey3D(0,0));
   cvTrY.addKey(new LinearCurveKey3D(0.5,50));
   cvTrY.addKey(new LinearCurveKey3D(1,0));

   cvTrZ.addKey(new LinearCurveKey3D(0,0));

カーブXは時刻0秒で−50、1秒で50、2秒で−50に変化する。カーブYは0秒で0、0.5秒で50、1秒で0、カーブZは時刻0で0になり、そ例外の部分は直線補完された値が使われる。

これらをTranslationChannel3Dのインスタンスに追加し、

var chTr:TranslationChannel3D = new TranslationChannel3D(null);

        :

chTr.addCurve(cvTrX);
chTr.addCurve(cvTrY);
chTr.addCurve(cvTrZ);

そしてこのチャンネルをアニメーションコントローラに追加する。

var obj:test_transform_object = new test_transform_object(material,100,150,24,4);

        :

obj.animation.addChannel(chTr);

そしてこのオブジェクトをシーンに追加して、アニメーションをplayメソッドで開始すれば、移動アニメーションになるわけだ。

   scene.addChild(obj);

     :   

   obj.animation.play();

これがその結果。

fig01


それではまた次回。

modoカテゴリー別ページ



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)

2010年11月04日

modoからFLASHへ書き出してみた その32 modo 401 SP4 ActionScript 3.0

前回に引き続きムービークリップをPaperVision3Dのマテリアルとしてオブジェクトに貼り付ける話だ。

ムービークリップもアニメーションなので管理はAnimationControllerにやってもらえる。そして、貼り付けたムービークリップを再生する時には、どのムービークリップを再生するかをアニメーションコントローラにplayメソッドで指示してやればいい。

前回はSphereクラスを拡張してClipTestObjectクラスを作り、そのオブジェクトに複数のムービークリップを割り当てておいて、それらのムービークリップをAnimationControllerで制御するコードを作ってみた。

まずはこのClipTestObjectクラスのコードを見ていこう。このクラスはSphereを継承したクラスなのでクラスの宣言はこのようになっている。

 public class ClipTestObject extends Sphere{

そしてメンバー変数としてAnimationControllerクラスのインスタンスを格納するanimation、マテリアルリストを格納するメンバー変数materiallistを宣言した。

  public var animation:AnimationController;
  private var materiallist:MaterialsList;

前回掲載したリストではここは

private var clipByName:Dictionary;

になっていて、materiallistの中の辞書に直接アクセスするようにしてあったんだけど、呼び出したいマテリアルの名前がリストに無い時の処理を自前で書かなくちゃならなくなるので、今回はmateriallistへの参照をインスタンス内に保存しておいて、そこからインスタンスメソッドのgetMaterialByName( )を使ってマテリアルを呼び出すように変更した。

getMaterialByName( )はMaterialListクラスの中で下のように定義されている。

  public function getMaterialByName( name:String ):MaterialObject3D
  {
   return this.materialsByName[name] ? this.materialsByName[name] : this.materialsByName["all"];
   //return this.materialsByName[ name ];
  }

要するに辞書の中に該当する名前が無い時は「all」という名前のマテリアルを返す事になっている。この事からデフォルトのマテリアル名は「all」であればいいことがわかる(前回のコードをこのように書き直してもいいわけだけどね)。

次にコンストラクタ。ClipTestObjectのコンストラクタは継承したSphereのものと大差ないが、唯一違っているのは第一引数でSphereがMaterialを受け取るのに対してClipTestObjectはMaterialListを受け取ることだ。

public function ClipTestObject(materiallist:MaterialsList,radius:Number=100,segmentsW:int=8,segmentsH:int=6) {

そこで受け取ったmateriallistへの参照をメンバー変数materialにコピーしてとっておく。thisが付いてるのがこのインスタンスのメンバー変数であるmateriallistで、何も付いていない方はこのコンストラクター内のローカル変数materiallist(コンストラクタの引数として宣言されている)だ。

this.materiallist = materiallist;

次にこのオブジェクト(Sphere)のマテリアルをその辞書から「all」という名前で引いてセットする。

   var material:MaterialObject3D=materiallist.getMaterialByName("all");
   super(material,radius,segmentsW,segmentsH);

superは継承元のSphereのコンストラクタを示している。これで継承元の球体は「all」の名を持つマテリアルが貼り付けられた状態で初期化されるわけだ。

あとはこのインスタンス用にAnimationControllerのインスタンスを生成し、アニメーションがスタートした時とストップした時に起きるイベントに対するハンドラメソッドをAnimationControllerにセットした。

   animation=new AnimationController();
   animation.addEventListener(AnimationEvent.START,clipHandler);
   animation.addEventListener(AnimationEvent.STOP,clipHandler);

コンストラクタによる初期化はここまで。

AnimationControllerはupdate( )メソッドを繰り返し呼び出されることで経時的に処理を継続しているので、定期的にこのメソッドを呼び出さないと機能しない。この定期的呼び出しにふさわしいのがこのオブジェクトを書き換えるタイミングで、ステージ上にあるオブジェクトはフレームが更新されるたびに書き換えが発生している。この機を捕らえてupdate()を呼び出してやれば、フレームが更新されるたびにAnimationControllerのupdate()メソッドが呼ばれる事になる。そしてこのオブジェクトの更新のエントリーポイントが各オブジェクトのproject( )メソッドで、これをoverrideで上書きしている。

  public override function project(parent:DisplayObject3D, renderSessionData:RenderSessionData):Number
  {
   animation.update();
   return super.project(parent, renderSessionData);
  }

ここでやっているのは継承もとのprojectメソッドを呼び出す前にanimationcontrollerのupdateメソッドを呼び出しているだけだ。

アニメーションのスタート時と終了時に発生するイベントを処理するためにリスナー登録したハンドラーclipHandler( )メソッドは、引数eのtypeを調べてanimationStartイベントとanimationCompleteイベントそれぞれに反応するように作ってある。

  private function clipHandler(e:AnimationEvent){
   var clip:MaterialObject3D;
   switch(e.type){
    case "animationStart":
     clip=materiallist.getMaterialByName(e.clip);
     this.material=clip;
     MovieClip(MovieAssetMaterial(clip).movie).gotoAndPlay(0);
     break;
    case "animationStop":
     clip=materiallist.getMaterialByName("all");
     this.material=clip;
     MovieClip(MovieAssetMaterial(clip).movie).gotoAndStop(0);
     break;
   }
  }

AnimationControllerのplayメソッドにムービークリップ名を引数で渡して呼び出すと、animationStartイベントが発生する。そしてこのハンドラが呼び出されてcase "animationStart":のブロックが実行され、playメソッドの引数として渡されたムービークリップ名がハンドラーの引数eの中にclipというメンバー変数に格納されて渡ってくる。それを使ってmateriallistのgetMaterialByNameメソッドで名前からマテリアルオブジェクトを見つけ出し、それを自身のマテリアルとして設定し、さらにそのマテリアルをMovieAssetMaterialとして型キャストした上でMovieAssetMaterialのmovieプロパティにアクセスし、それをさらにMovieClipとして型キャストして、MovieClipのgotoAndPlayメソッドを呼び出してムービークリップの0フレーム目から再生を開始している。

AnimationControllerのupdate()は呼び出されるたびに再生しているアニメーションの経過時間とムービークリップの長さを比較して、ムービーの終了時間に到達したかを監視している。ムービーが最後まで到達した時にはanimationCompleteイベントが発生し、そしてムービーがループ再生の設定になっていない時はさらにanimationStopイベントが発生する。このイベントハンドラではanimationStopイベントに対して自身のマテリアルをマテリアル名「all」のものに設定しなおし、フレーム0に再生位置を移動させて再生をストップさせている。

以上がムービークリップを貼り付けて再生させるClipTestObjectクラスの内容だ。

このクラスのオブジェクトをステージ上に置いて表示させるのがMcTest04クラスで、BasicViewクラスを継承して作られている。

public class McTest04 extends BasicView{

このクラスはフレーム更新ごとにシーンに追加されているオブジェクトを自動的に更新する。要するにフレーム更新ごとにClipTestObjectクラスでオーバーライドしたprojectメソッドを呼び出してくれるわけだ。

ClipTestObjectに貼り付けるためにライブラリに用意したmc_all、mc_smile、mc_angryの3つのムービークリップはMoviewAssetMaterialクラスのインスタンスとしてオブジェクトにラッピングされてMaterialListに登録される。

   var mlist:MaterialsList=new MaterialsList();

   material=new MovieAssetMaterial("mc_all",false,false,false,true);
   mlist.addMaterial(material,"all");
   
   material=new MovieAssetMaterial("mc_smile",false,true,false,true);
   mlist.addMaterial(material,"smile");

   material=new MovieAssetMaterial("mc_angry",false,true,false,true);
   mlist.addMaterial(material,"angry");

その複数のマテリアルを格納したマテリアルリストをClipTestObjectのインスタンスを生成する時に引数として渡す。

   obj=new ClipTestObject(mlist,400,24,12);

smileとangryはムービークリップとしてAnimationControllerに管理してもらわなくちゃならないから名前と開始時刻、終了時刻を持ったタグの役割をするAnimationClipクリップクラスのインスタンスを生成してaddClipメソッドでAnimationControllerに登録する。ムービークリップ自体はMovieAssetMaterialとしてMaterialListに入っているので、こっちのAnimationClip3Dクラスは単なる管理情報だけで充分なわけだ。

   var clip:AnimationClip3D=new AnimationClip3D("smile",0,MovieClip(material.movie).totalFrames/Number(stage.frameRate));
   obj.animation.addClip(clip);

   clip=new AnimationClip3D("angry",0,MovieClip(material.movie).totalFrames/Number(stage.frameRate));
   obj.animation.addClip(clip);

MovieClipクラスにはtotalFrameというプロパティがあるけどこれはムービークリップの総フレーム数で、ムービーの再生時間はこのフレーム数とムービーを再生する時のフレームレートで決まるから、暗黙のプロパティstageのframeRateプロパティから得た値を整数から実数に変換して総フレーム数を割って継続時間を秒単位で取得する。開始時刻は0にすれば終了時刻はムービーの継続時間と同じになる。

AnimationControllerは1つでも有効なチャンネルが無いと機能しないので、ダミーのチャンネルを作って追加した。

obj.animation.addChannel(new Channel3D());

生成したオブジェクトをシーンに追加して、位置と回転角度を設定してレンダリングを開始した。

   scene.addChild(obj);
   obj.y=50;
   obj.z=300;
   obj.rotationY=-90;
   startRendering();

本当はオブジェクトのY軸回転チャンネルにアニメーションをつけてAnimationControllerに追加すれば良かったんだけど、まだちゃんと調べて無いのでEnter_FRAMEイベントでloopメソッドをイベントハンドラとして呼び出すように設定して、

addEventListener(Event.ENTER_FRAME,loop);

そこでY軸の回転角度を−110度から−70度で往復するようにしてみた。

  private function loop(e:Event){
   if (obj.rotationY<-110) dy=1;
   else if (obj.rotationY>-70) dy=-1;
   obj.rotationY+=dy;
  }

ステージにボタンを2つ配置して、それらのボタンに対してMOUSE_UPイベントハンドラを設定して、

   btn_smile.addEventListener(MouseEvent.MOUSE_UP,mouse_handler);
   btn_angry.addEventListener(MouseEvent.MOUSE_UP,mouse_handler);

ボタンが押されてマウスボタンがリリースされた時にそれぞれのボタンによってsmileアニメーションまたはsngryアニメーションが再生されるようにplayメソッドを呼び出した。2番目の引数はループ再生するかどうかの設定。falseでループ再生しない。

  private function mouse_handler(e:MouseEvent){
   switch(e.currentTarget){
    case btn_smile:
     obj.animation.play("smile",false);
     break;
    case btn_angry:
     obj.animation.play("angry",false);
     break;
   }
  }

以上が前回からちょこっと変えたけどムービークリップを貼り付けて再生させるコードの内容だ。

それではまた次回。

modoカテゴリー別ページ



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)
Archives