前回の「Vol.4:スクリプトの具体的な作り方(後編)」ではパーティクルフローを使用した、ちょっと特殊なスクリプトの使い方を紹介しました。今回からは MAXScript におけるアニメーションデータの扱い方を学習していきましょう。

3ds Maxにおけるアニメーションデータの扱い 実行結果

今回は上の画像のように、1万個のCube オブジェクトのアニメーションを MAXScript で制御してみます

アニメーションデータの扱い方 01

Autodesk 3ds Max(以下、3ds Max)でのアニメーションデータの扱いは、グラフとして見えている部分以外にも様々な処理を行なっているため、とても分かりにくくなっています。
ですので、まずは 3ds Max 内部でアニメーションデータがどのように扱われているのか、実際に Cubeオブジェクト を作成し、適当なアニメーションを付けて、確認してみましょう。

コントローラとモーションのカーブ

Cubeを置いた時のコントローラ(上)とモーションのカーブ(下)

上の画像には、使われているコントローラと、モーションのカーブが表示されています。
コントローラの表示に馴染みがない場合は、スケマティックビューの "表示(D)/フロータを表示" で各種表示状態を切り替えるフロータを表示して、エンティティのコントローラを ON にしてください。こうすると、オブジェクトがどのようなコントローラで動かされているのか確認できます。
また、P,R,S それぞれのボタンにチェックを入れると位置、回転、スケールそれぞれのコントローラの内容を見ることが可能です。

スケマティックビューの表示切替

スケマティックビューの表示切替で、コントローラの内容を確認できる

下の画像は、MAXScript のリスナーとエディタです。表示されている内容は、アニメーションデータをスクリプトから確認したものになります。

スクリプトリスナとエディタ

スクリプトリスナー(上)とエディタ(下)。アニメーションデータのスクリプトが表示されている

ここではコントローラの種類と、キーフレームが何フレーム目にあるのかが表示されています。


 s = selection[1]
 classOf s.pos.controller
 s.pos.controller.keys
 

確認に使用しているスクリプトは上のような内容です。位置コントローラには、 <オブジェクト>.pos.controller でアクセス。classOf で、指定したノードのクラス名を確認するために使用します。

[[SplitPage]]

アニメーションデータの扱い方 02

今度は、スケマティックビューを詳しく見ていきましょう。

スケマティックビュー

Cubeを置いた時のスケマティックビュー

Cube 1つだけなのに、何だか複雑なことになっていますね。これは、3ds Max のアニメーション情報が、幾つかのノードの集合になってるためです。
このスケマティックビューと同じ情報は、コントローラ情報でも確認をすることができます。

コントローラ情報

コントローラ情報から、スケマティックビューと同じ情報を確認できる

この中で、アニメーション情報をまとめる親玉になるのが "変換 : 位置/回転/スケール" というコントローラです。これはスクリプト側からは PRS コントローラとして見えます。
この親玉には位置、回転、スケールを操作する PRS 以外にもスクリプトで直接変換操作を行なえる変換スクリプトコントローラなど、様々な種類のものがあります。

変換:位置/回転/スケール コントローラ

アニメーション情報をまとめる "変換 : 位置/回転/スケール" コントローラ

この親玉コントローラは 位置・回転・スケールを統一して扱います。
これらを別々に操作する場合には、この親玉に関連づけられた位置・回転・スケールを扱うコントローラを使用してください。今回の例で言えば、XYZ 位置オイラーXYZベジェスケール というコントローラがそれです。

XYZ位置、オイラーXYZ、ベジェスケール

位置・回転・スケールを別々に扱うには、XYZ 位置、オイラーXYZ、ベジェスケールを操作

例えば、オブジェクトを別のオブジェクトに位置コンストレインしたければ、位置コントローラを位置コンストレインに差し替えて、位置ターゲットを追加してやればコンストレインできます。
メニューからコンストレインをしても同じことができますが、ひとつ異なるのが、位置リスト という、複数のコントローラを切り替えるためのコントローラが一段表示され、その下にそれまで有効だったコントローラとコンストレインコントローラが配置される点です。
個人的には、この場所に 位置リスト があると邪魔なので、いつもメニューから操作は行わず、直接コントローラの差し替えとターゲットの設定をするようにしています。

位置リスト

メニューから操作すると位置リストが上の方に表示される

この 位置リスト も、上手に使えば簡単に複数の入力を元にしたスイッチを作ることができ便利なのですが、正直私はあまり使わなかったりします。有効に使おうとするとパラメータワイヤリングなどを使って色々と組まないといけないので、面倒くさいんですよね......と、これは余談でした。

ちなみに、直接コントローラを割り当てる場合は、割り当てたいコントローラを選択した状態で、下図の赤丸で囲ったボタンを押下。すると、割り当て可能なコントローラの選択ダイアログが表示されるので、コントローラを選んで OK ボタンを押してください。

直接コントローラを割り当てる

コントローラを直接割り当てる場合は、赤丸ボタンをクリックしてダイアログを表示させる

[[SplitPage]]

MAXScript からアニメーションをコントロールする 01
~キーの一覧を取得~

3ds Max 内部でコントローラやアニメーションデータがどのように扱われているかを理解したところで、MAXScript からアニメーションデータを操作をしてみます。
まずは先ほども紹介した、キーの一覧を取得するコードを見てみましょう。



 s = selection[1]
 classOf s.pos.controller
 s.pos.controller.keys

実はこのコードは、実際の 3ds Max の内部データを理解した後に見ると奇妙なことになっています。なぜなら、先程の解説だと、階層は以下のようになっている筈だからです。



(オブジェクト)/(変換:位置/回転/スケール).pos/(位置:XYZ位置)/アニメーションデータ

これをそのままスクリプトに書くと、以下のようになります。


 s.controller.pos.controller.keys

実際、この方法でもキーを取得することができます。ただし、以下ような書き方ではアクセスできません。



 s.pos.keys
 s.controller.pos.keys

これは、pos だけだと "そのコントローラの値" を取得しようするためです。そのため、キーを取得してようとしてもエラーになってしまいます。混乱しがちですが、気をつけてください。

スクリプトからアニメーションをコントロールする 02
~位置コントローラのアニメーションを制御する~

次に、位置コントローラのアニメーションを、スクリプトから制御してみます(これも意外と曲者です)。
まず、PRS コントローラ を以下の方法で取得しましょう。



 prsCtrl = s.pos.controller

さて、ここからどうしたらいいでしょうか。先ほどの方法だと、keys でキーを取得して色々な操作できそうです。
MaxScript リファレンスを検索すると、keys"コントローラの共通プロパティ、演算子、メソッド" の項目で見つけることができます。そこには;



 .keys MAXKeyArray -- 読み込み専用、コントローラのキー配列

......という記述があります。どうやら MAXKeyArray というのが keys の値の種類みたいです。
そこで MAXKeyArray 値 の項目を見ると、何やら細々と書いてあるのですが;



 <key_array>[<index_integer>]

......『キー配列内のインデックスで指定されたキーを表す MAXKey 値を返します。インデックスは 1 から始まります』とあります。

ほうほう......配列と同じようにキーの値が取れるようです。つまり、 keys[1] のように指定すればコントローラの最初のキーを取得できるはずですね。
さらに、MAXKey への解説のリンクを辿ると、関連項目として "MAXKey の共通プロパティ、演算子、メソッド "へのリンクがあります。これを見れば、キー値を操作するときの機能を知ることができそうです。
と、このように、マニュアルを読んでいくと、以下のように書けば何かの値を得ることができそうな気がします;



 s.pos.controller.keys[1].value

 

......ところが、実はこれでは上手くいきません。



 -- ランタイム エラー: このコントローラのキーにアクセスできません :
    Controller:Position_XYZ

 

なぜ!? きちんとマニュアルを読んで、その通りに操作をしているはずなのに......しかし MAXScript では、こうしたトラブルはよくあるので、慌てず騒がず冷静に!

もう一度、マニュアルを読んでみましょう。
エラーメッセージを見ると、Position_XYZ というクラスのコントローラの値を取得しようとしてエラーになっているのが分かるので、早速、検索します。
すると、Position_XYZ コントローラのプロパティ名が判明しました。



 Position_XYZ:
 .x_position Float default: 0.0 -- アニメート可能
 .y_position Float default: 0.0 -- アニメート可能
 .z_position Float default: 0.0 -- アニメート可能

 

これを見ると、x,y,z それぞれ値を取得するためのプロパティが分かれていて、x_positiony_positioinz_position となっているようです。では、これを使って値を取得してみます。



 s.pos.controller.x_osition.keys[1].value

今回はエラーにならず、きちんと値を取得することができました。何だか納得いかないですが、こういうものだと思ってください。MAXScript にはデータにアクセスするためのショートカットが多く存在するのですが、その全てが意図した通りに動くとは限らないのです。
もし、行き詰ってしまった場合は、マニュアルに立ち返って、きちんとした方法でデータにアクセスするようにしましょう。

[[SplitPage]]

スクリプトからアニメーションをコントロールする 03
~スクリプトでアニメーションデータを生成~

やっとアニメーションデータを取得できたので、今度はスクリプトでアニメーションデータを生成してみます。ここまででマニュアルの色々な部分を見てきたので、何となく "あ、これかな?" という機能が思いつくかもしれません。

アニメーションデータを扱うための機能は、リファレンスマニュアルの "コントローラ キー関数" の項目にまとまっています。

アニメーションデータを設定する方法も幾つかありますが、今回はキーの作成と値の設定の二段階に分けてアニメーションデータを作成します。
まず、キーフレームを設定するためには addNewKey を使用します。選択したオブジェクトの X 位置に0フレームから 100 フレームまで1フレーム毎にキーを設定するには以下のように記述します。



 s = selection[1]
 for i=0 to 101 do (
   addNewKey s.pos.controller.x_position.controller i
 )

さらに、値も設定してみましょう。



 s = selection[1]
 for i=0 to 100 do (
   addNewKey s.pos.controller.x_position.controller i
   s.pos.controller.x_position.controller.keys[i+1].value = i
 )

これまでの解説で、何をしているのか一目瞭然だと思います。このスクリプトを実行すると、1フレーム毎に 1Unit(デフォルトでは1インチ) X 方向に移動するアニメーションができるわけです。
しかし、これだけでは面白みがないので、もうちょっと工夫してみます。



 s = selection[1]
 for i=0 to 100 do (
   addNewKey s.pos.controller.x_position.controller i
   s.pos.controller.x_position.controller.keys[i+1].value =cos(360.0/100*i)

   addNewKey s.pos.controller.z_position.controller i
   s.pos.controller.z_position.controller.keys[i+1].value = sin(360.0/100*i)
 )

X 軸と Y 軸に三角関数で計算した値を代入しています。これで、100 フレームで XZ 平面上で一回転するアニメーションができました。
さらに! 100×100 個のオブジェクトを作って、アニメーションを生成してみます...



 for i=0 to 99 do (
 	for j=0 to 99 do (
 		b = Box pos:[i, j, 0] length:1 width:1 height:1
 		b.Name = "Box_" + (i as String) + "_" + (j as String)
 
 		for k=0 to 100 do (
 			addNewKey b.pos.controller.z_position.controller k
 			b.pos.controller.z_position.controller.keys[k+1].value = 10*sin(2*360.0/100*(i+j+k))
 		)
 	)
 )

この処理はかなり重いです。100×100=1万個のオブジェクトを作って、それぞれに100 個づつキーフレームを割り当てているので無理もありませんね。ちょっと気長に待ってみてください。

単純なサインカーブを与えただけなのでシンプルな動きしかしませんが、それでも1万個のCube が動く様は壮観です。与える関数を工夫するともっと面白い動きを作ることができるので、色々と試してみてください。


今回の例で生成された Cube の波

今回は 3ds Max のアニメーションデータの扱いについて学習をしていきました。
普段はあまり意識をしないコントローラの構造や、スクリプトからどのようにアニメーションデータにアクセスをするのか、どのようにマニュアルから必要な情報を見つけるのかという感覚も掴むことができたかとできたかと思います。

次回からも引き続き 3ds Max 上のアニメーションデータの扱い方について、さらに掘り下げていきますよ。

TEXT_痴山紘史(JCGS)
映像制作のためのパイプライン構築をはじめ、技術提供を行なっていくエンジニア集団、「JCGS(日本CGサービス)」の代表取締役......というのは表の顔で、実態は飲み会とCG関連の技術が好きなただのCGオタク。
個人サイト 「PHILO式」