チュートリアル > フローティング コントロールによるモーファー モディファイヤの拡張 |
3ds Max のモーファー モディファイヤには、最大 100 個のモーフ チャネルが用意されていますが、一度に表示できるのはそのうちの 10 個にすぎず、値の編集ボックスでのみコントロールできます。MAXScript を使用して、基本的なフローティング UI エクステンションをモーファー モディファイヤに追加します。 モーファー モディファイヤは、進行状況バーを使用してモーファー チャネルを表し、コントロールします。
また、スクリプトによる UI 要素のダイナミックな作成と時間の用途も示し、コールバックを変更して時間とプロパティの変化に反応するようにします。
macroScript は MorpherFloater と呼ばれます。スクリプトを使用する場合は、[カスタマイズ...](Customize...)に移動してスクリプトを[HowTo]カテゴリからツールバー、メニュー、またはクアッド メニューにドラッグするか、キーボード ショートカットを割り当てることができます。
モーファー コントロールを表示するために使用するロールアウトと、現在選択されている制御対象のモーファー モディファイヤを格納するために、一組のグローバル変数が必要です。
このスクリプトは、シーン内で単独のオブジェクトが選択され、そのオブジェクトがスタック上にモーファー モディファイヤを持っている場合にのみアクティブになります。
isEnabled ハンドラは、ステートメントを返した後に式を評価して、その式が true の場合にスクリプトのボタン、メニュー項目またはショートカットを有効にします。そうでない場合、スクリプトは無効になります。
最初のステートメントでは、シーン内で選択されたオブジェクトの数を 1 と比較します。 ただ 1 つのオブジェクトのみが選択されている場合、この式は true を返します。
次に、選択されたオブジェクトに適用されるモーファー モディファイヤがあるかどうかも尋ねます。これは、 try()catch() コンテキスト内で行われます。モーファー モディファイヤがある場合は、そのモーファー モディファイヤがこの式の結果として返されます。モーファーがない場合、通常はエラーになります。このエラーを捕捉して、代わりに undefined を返します。したがって、結果が undefined で「ない」かどうかがチェックされます。これで、モーファーが検出された場合は true の、そうでない場合は false の結果が返されます。
テストの最初の部分と 2 番目の部分が両方とも true を返したときにのみ、isEnabled ハンドラも true を返し、スクリプトがアクティブになります。
Macroscript_Body_Event_Handlers
execute ハンドラにはスクリプトの本体が含まれ、このスクリプトが割り当てられているボタンをユーザがクリックするか、メニュー項目を選択するか、キーボード ショートカットを押したときには常に実行されます。これは、 isEnabed が true を返したときにのみ発生します。
Macroscript_Body_Event_Handlers
最初に、スクリプトの先頭で定義したグローバル変数にモーファー モディファイヤを格納します。選択されているオブジェクトがただ 1 つであることを確認済みなので、 $ を selection[1] の代わりに使います。 また、 isEnabled のチェックによって、選択されたオブジェクト内にモーファー モディファイヤがあることが既にわかっているので、これ以上エラーの捕捉の必要はありません。
モーファー内のチャネルで使用されるインデックスを確認するための、ユーザ定義の配列が必要です。
ここで、かなり変わった操作を始めます。 最初に文字列変数内に必要なすべての定義を配置し、それが通常のスクリプト ファイルであるかのように実行することによって、新規のロールアウト全体をダイナミックに作成していきます。文字列内に引用符がある箇所には、必ず円記号「¥」 が必要であることに注意してください。また、「¥n」シーケンスを使用して改行を示す必要があります。
モーファー モディファイヤは、最大 100 個のチャネルを持つことができます。 for ループを使用して、それらのチャネルを巡回します。変数の i で、現在のモーフ ターゲット チャネルを表わす 1 から 100 までをカウントします。
3ds Max 5 では、モーファー チャネルにアクセスする新しい関数が実装されました。 WM3_MC_HasData 関数を呼び出してモーファーとチャネル インデックスを渡すことによって、チャネルにモーファー データがあるかどうかをチェックすることができます。データがある場合は if ステートメントのコンテキスト内部に進み、ない場合はこのチャネルをスキップします。
for ループの変数 i に格納されているチャネル インデックスを、使用されているチャネルの配列に追加します。 for ループの準備ができたら、使用されているすべてのチャネルが配列に含まれます。
txt +="progressbar mf_slider_"+ i as string txt +=" value:"+ (WM3_MC_GetValue mf_morpher_mod i) as string txt +=" width:150 height:18 across:2 align:#left\n"
ここで、ロールアウト文字列内部の新しい進行状況バーの UI 要素の作成について説明します。「+=」を使用して現在の文字列に新しい部分文字列を追加することに注意してください。すべての UI 要素にそれぞれ固有の名前を付けるために、チャネル番号を文字列として付加します。progressbar の現在の値をモーファー モディファイヤのチャネルの現在の値に設定します。 across:2 キーワードを使用して、2 つの UI 要素を同じ行に配置するようにします(後で、モーフ ターゲットの名前のテキスト ラベルを追加します)。
txt +="on mf_slider_"+i as string+" clicked val do (\n" txt +="WM3_MC_SetValue mf_morpher_mod " txt += i as string+" (val as float) \n" txt +="SliderTime +=0)\n"
progressbar UI 要素は通常はプロセスの進行状況を表示するために使用されますが、マウスで変更された値を返す変更ハンドラが提供されているため、スライダの代わりとしても使用することができます。 on ... clicked val do... ハンドラは、ユーザが進行状況バー上でマウスをクリックするごとに呼び出されます。変数 var には、0 から 100 の範囲の現在値が格納されます。これを 0.0 から 100.0 までの浮動小数点数として、インデックス i を使用してそれぞれのチャネルに代入します。
SliderTime += 0 は、後で定義する時間コールバックを欺くためのトリックです。ゼロを追加するのでスライダの時間は実際には変化しませんが、システムに変化を評価させ、時間コールバックを実行させます。
txt +="edittext mf_label_"+i as string txt +=" align:#right text:\""+i as string+": " txt +=(WM3_MC_GetName mf_morpher_mod i) +"\"\n"
さらに、編集テキストの UI 要素の作成について説明します。このテキストには、文字列としてのチャネル インデックスと i 番目のモーファー チャネルの名前が含まれます。ラベルは進行状況バーと同じ行に表示され、右寄せされます。
この時点で、i ループは終了します。このループで、空でないすべてのチャネルのためのコントロールが UI を記述する文字列に追加されました。あとは、ロールアウトの終了ブラケットを追加するだけです。
ここで、新しいロールアウトのソースとして配置した文字列を使用して、ダイアログ ボックスを作成します。外部のソース コード ファイルと同じようにして、実行関数で文字列を評価します。評価の結果は、スクリプト エディタを使用して入力したのと同じロールアウトになります。ダイアログ ボックスは、幅が 340 ピクセル、収集された総チャネル数を 24 倍してロールアウト内のコントロール数で割ったものになります。つまり、スクリプトが開始されるごとに、モディファイヤ内のモーファー チャネルの数によって UI の高さが変化します。
ここで再び、モーファーが変更されるごとに呼び出される新しい文字列定義の関数を開始します。
使用されている各モーファー チャネルに対応するスライダを更新する必要があります。変数 i には、ループの反復ごとに対応するチャネルのインデックスが含まれます。
txt +="mf_main.mf_slider_"+i as string txt +=".value = WM3_MC_GetValue mf_morpher_mod "+i as string+" \n"
ここで、グローバルに定義されたロールアウト mf_main のチャネル i の進行状況バーに、その値をグローバル変数 mf_morpher_mod 内に格納されたモーファー モディファイヤ内の i チャネルの値に変更するように指示します。
次に、コールバック内で使用できる評価された関数を指定する関数の定義テキストを実行します。それをグローバル変数に格納して、定義しようとしているすべてのコールバックでアクセスできるようにします。
最初のコールバックは、時間の変化に反応します。マウスでタイム スライダを動かしたり、アニメーションを再生したりなどしてシーン内の時間が変化するたびに、時間コールバックが指定された関数を呼び出します。 この例では、直前に定義した UI 更新の関数を呼び出します。前に説明した、この関数の SliderTime += 0 の部分を思い出してください。これで、このコールバックも呼び出されることになります。
このコールバックの結果、モーファー チャネルの値をアニメートしてからアニメーションを再生した場合、アニメーションが再生されている間にリアルタイムで進行状況バーが更新されます。
他のコールバックを登録する前に、まず、同じスクリプトによって登録された古いコールバックがメモリから削除されていることを確認します。この例のコールバックの固有の ID は、 #morpher_floater です(この名前はユーザ定義であり、固有であるかぎりどのような名前でもかまいません)。それで、その ID を持つ既存のコールバックがあればそれを削除するように MAXScript に指示します。
when parameters mf_morpher_mod changes \ HandleAt:#RedrawViews \ id:#morpher_floater do mf_update_slider()
最後に、パラメータ変更のコールバックを登録します。when コンストラクタで、指定されたオブジェクトの監視対象のプロパティ (この例ではモーファー モディファイヤのパラメータ) の値が変更されたときに常に実行されるコールバックを定義します。
ビューポートが再描画されているときにのみ更新するようにコールバックに指示します。これで、ユーザが気付くような障害なしに、内部的な呼び出しの実際の回数が減り、スクリプトの実行速度が上がります。
後でこのコールバックを削除できるように、固有の ID を与えます。
このコールバックが実行するのは、更新の関数を呼び出すことだけです。その結果、モーファー チャネルの値を変更したときに、ここで作成したフローティング UI が自動的にその変更を反映するようになります。
スクリプトを評価します。スクリプトを使用する場合は、[カスタマイズ...](Customize...)を使用して、スクリプトを[HowTo]カテゴリからツールバー、メニュー、またはクアッド メニューにドラッグするか、キーボード ショートカットを割り当てることができます。
モーファー モディファイヤ付きの単独オブジェクトとシーン内でアクティブなチャネルをいくつか選択し、このスクリプトを実行します。新しいフローティング ダイアログ ボックスが、進行状況バーとチャネル番号およびターゲット名を表示するテキスト フィールド付きで表示されます。
これは、非常に単純化された例です。シーン内の単独のターゲット オブジェクトを選択するためのスピナーやボタンなどを追加して、このフロータを拡張することができます。 また、進行状況バー要素を変更して、代わりに通常のスライダを使うこともできます。
上記のスクリプトは、使用されているモーファー チャネルの変更 (モーフ ターゲットの追加/削除) を検出しません。現時点では、スクリプトを再起動して変更を反映する必要があります。このような場合の処理を追加することもできます。