変更ハンドラは、シーン内のオブジェクトで特定のユーザ イベントが実行されるとき、その検出に使われます。スクリプトで記述できるので、オブジェクトの移動、頂点の編集、オブジェクトの削除、名前の変更などのユーザ操作に応答するスクリプトを作成できます。変更ハンドラは、各 MAXWrapper オブジェクトに適用され、そのオブジェクトで監視するアトリビュートやそのアトリビュートが変更されたときに評価する式を指定します。監視できるアトリビュートには、ジオメトリ、名前、変換、パラメータなどがあります。このハンドラは、3ds Max シーンには保存されません。
ChangeHandler : Value
クラスのインスタンスは、変更ハンドラのセットアップを表します。ChangeHandler 値は、when
構文を使用することにより作成されます。when
構文が実行されるたびに、新しい ChangeHandler インスタンスを作成して返します。この ChangeHandler を変数または配列に保存しておき、必要に応じてハンドラを解除できるようにしてください。
when
構文は、1 つ以上のオブジェクトで特定のイベントに対して変更ハンドラ関数を定義します。そのイベントが発生すると、システムは自動的にこの関数を呼び出します。
when
構文には、次の 2 つの形式があります。
when <attribute> <objects> change[s] [ id:<name> ] \
[handleAt: #redrawViews|#timeChange] \
[ <object_parameter> ] do <expr>
when <objects> deleted [ id:<name> ] \
[handleAt:#redrawViews|#timeChange] \
[ <object_parameter> ] do <expr>
最初の形式では、<attribute>
には、次のいずれかを使用できます。
topology geometry name[s] transform select parameters subAnimStructure controller children
これらは、変更を監視する必要があるオブジェクトのアトリビュートを指定します。詳細は以下を参照してください。
2 番目の形式は、ユーザまたは他のスクリプトによるオブジェクトの削除を監視します。通常、MAXScript オブジェクト値を含むテーブルや他の構造体を持ち、シーンの変更時に削除されたオブジェクトを破棄したい場合に使用します。
[#selectedNodesPreDelete
は、ユーザによるノードの削除を補足するために使用される、さらに優れたメソッドです。このメソッドはノード上に操作が何も行われていない時点で呼び出され、あらゆるノードの削除を報告します。これに対し when
構文は、コールバックで明示的に登録されたノードに対してのみ動作します。
mypot1 = teapot() --create a teapot
fn whendeleted = --define a callback function
(
local deletedobjects = callbacks.notificationParam()
format "Callback: %\n" deletedobjects
-- This will printevery object that was deleted.
-- By this point, the object isstillattached to it's parents
-- and is still in groups etc...
-- Nothing has been done to the nodeyet.
)
when mypot1 deleted id:#foo obj do --define a when construct
(
format "When Construct:%\n" obj
)
--unregisterany preDeletecallbacks
callbacks.removeScripts #selectedNodesPreDelete
--register the function as general callback
callbacks.addscript #selectedNodesPreDelete "whendeleted()"
3ds Max 9 に戻ると、when
構文の呼び出し方が変更されます。3ds Max 9 以前のリリースでは、when
構文はオブジェクトを実際に削除する前に呼び出されていました。3ds Max 9 で上記のコードを実行すると、when コールバックは
3ds Max 2008 ではこの動作が修正されました。
どちらの形式でも、引数 <objects>
には次のいずれかを使用できます。
単一の 3ds Max オブジェクト(シーン ノード、コントローラ、モディファイヤ、マテリアルなど)
オブジェクト セットの 1 つ(「選択」または「カメラ」など)
ワイルドカード パス名($foo*など)
3ds Max オブジェクトの配列
複数のオブジェクトが指定された場合、3ds Max コアによって任意のオブジェクトの特定のアトリビュートが変更され、シグナルが発行されるたびにハンドラが呼び出されます。
オプションの id:
パラメータは、1 つまたは複数のハンドラの ID を名前リテラルとして指定します。ID 名は、後でハンドラを削除する場合に使用できます。同じ名前のハンドラが複数ある場合は、それらをグループとして削除できます。
オプションの handleAt:
パラメータは、変更ハンドラの式が通知後すぐに実行されないようにシグナルを発行し、3ds Max ビューポートが再描画されるまで(#redrawViews
)、または現在の 3ds Max アニメーション時間が変更されるまで(#timeChange
)、実行を遅らせます。後述の「注意」で解説されているように、変更ハンドラの式の処理を遅らせることをお勧めします。handleAt:
パラメータの使用例は次のとおりです。when select $ changes id:#foo handleAt:#redrawViews do ...
オプションの <object_parameter>
を使用すると、do <expr>
内でアクセスでき、変更された実際のオブジェクトを保持するパラメータ変数の名前を指定できます。ハンドラが多数のオブジェクトの変更に応答する場合は、どのオブジェクトが変更されたかを決定できるように、このパラメータ名を指定します。<expr>
は、単一式でもブロック式でもかまいません。
例
b1 = box() b2 = box() b3 = box() when transform $box001 changes do $box002.pos = $box001.pos + delta max select all when names selection change obj do update_name_table obj when #($box001, $box002, $box003) deleted obj do ( messageBox "Warning!" deleteItem obj_table (findItem obj_table obj) )
変更アトリビュートは次のように解釈されます。
topology
メッシュ スムーズ、最適化、または頂点の削除などを通してオブジェクトのトポロジがモディファイヤ パネルで変更された場合に、このシグナルが発行されます。
geometry
頂点の移動、またはアニメートされたモディファイヤによってオブジェクトのジオメトリが変更された場合に、このシグナルが発行されます。
name or names
3ds Max コマンド パネルで、ユーザが名前を編集して、オブジェクトの名前が変更された場合に、このシグナルが発行されます。文字が変更されるたびにハンドラが繰り返し呼び出されます。
transform
移動、回転、またはスケールによってオブジェクトの変換が変更された場合に、このシグナルが発行されます。
select
シーン ノードが現在の選択セット中に移動した場合か、セット外に移動した場合に、このシグナルが発行されます。新規の状態を決定するには <node>.isSelected
プロパティを調べる必要があります。
parameters
オブジェクト内でパラメータが変更された場合に、このシグナルが発行されます。多くの場合、コアはこのイベントをシグナルとして発行するため、これは一括キャッチと似ています。
subAnimStructure
新しい頂点が編集可能メッシュ内でアニメート化されたり、新しいコントローラがリスト コントローラに追加された場合など、ダイナミック subAnim 構造が変更された場合に、このシグナルが発行されます。また、サブアニメート可能構造が再割り当てされた場合(たとえば、オブジェクト内でマテリアルが変更された場合)に呼び出されます。
controller
新しいコントローラがオブジェクトのトラックの 1 つに割り当てられた場合に、このシグナルが発行されます。
children
オブジェクトが直接子を追加または削除した場合に、このシグナルが発行されます。
変更ハンドラを削除するには次の 2 つのメソッドを使用できます。
deleteChangeHandler <change_handler>
指定された変更ハンドラを削除します。 change_handler
は、when
構文によって返される値です。
deleteAllChangeHandlers [ id:<name> ]
オプションの id:
パラメータが指定されていない場合、すべての変更ハンドラを削除します。 オプションの id:
パラメータが指定されている場合、指定された ID を持つ変更ハンドラをすべて削除します。
例:
deleteAllChangeHandlers id:#foo
効率を上げるために、関連性のない変更ハンドラをバックグラウンドで実行している必要はないため、不要になったものは削除することが大切です。
特に注意すべき点
変更ハンドラの実行中にその本文にランタイム エラーが発生した場合は、エラー メッセージが表示され、そのハンドラは永久的に使用不可になります。
変更ハンドラが追跡しているオブジェクトがすべて削除されると、変更ハンドラも削除されます。
do <expr>
変更ハンドラ コードは、それを作成したコードのコンテキストではなく、特別のコンテキストで実行されます。つまり、<expr>
コードは when
を囲む外側のコードのネスト内にあるローカル変数への参照を含めることはできません。これは、ローカル変数が when
ハンドラを呼び出したときに存在していない実行スタック上にあるためです。コンパイラは外側のローカルへの不正な参照をすべて検出し、コンパイラ メッセージを生成します。これに関する重要な例外は、ユーティリティとロールアウトのパネル ローカル(ローカル関数、ロールアウト変数、ネストされたロールアウトなど)です。これらは直接ロールアウトやユーティリティ オブジェクトと関連付けられているため、ロールアウト コード内の変更ハンドラで参照できます。
変更ハンドラは、コントローラ値の変化による結果の変更ではなく、ユーザが始めたイベントに対してのみ呼び出されます。たとえば、ノードの変換アトリビュートの変更ハンドラは、ユーザがノードを移動するときに呼び出されます。ノードの位置がアニメート化されている場合、アニメーションを再生しても変換アトリビュートの変更ハンドラは呼び出されません。
1 つの変更ハンドラを複数のオブジェクトのアトリビュートに割り当てた場合、オブジェクトのアトリビュートが変更されると、変更ハンドラは各オブジェクトにつき 1 度だけしか呼び出されません。
例:
when select $ changes obj do update_modifier_list obj
オブジェクトが選択および選択解除されたとき、
update_modifier_list
関数が呼び出されます。これは、[編集] (Edit) /[すべてを選択](Select All) を実行したときにもあてはまります。この場合、update_modifier_list
は、現在選択されている各オブジェクトに対して一度だけ呼び出され(オブジェクトの選択が解除されるとき)、続いて、シーン内のすべてのオブジェクトに対して一度だけ呼び出されます(オブジェクトが選択されるとき)。update_modifier_list
がプロセッサ集約処理を行う場合、システムの処理速度が著しく低下します。
変更ハンドラは、シーン内に既に存在するオブジェクトに対してのみ適用されます。変更ハンドラが、新規作成オブジェクトに自動的に適用されることはありません。
複数の変更ハンドラが定義されている場合、変更ハンドラが呼び出される順序は場合によって異なります。
3ds Max 内部の通知シグナルの処理方法のため、select 変更ハンドラでの $ 形式による選択オブジェクトへのアクセスはお勧めしません。選択オブジェクトにアクセスするにはオブジェクト セットを使用してください。$ は選択処理中にまだ設定されていない情報に依存しますが、selection では選択へのアクセスに異なるメソッドを使用し、すでに設定済みの情報を使用します。
警告 :
この変更ハンドラ システムは、3ds Max 内のすべてのアニメーションとインタラクティビティを実質的に駆動する 3ds Max の内部通知システムに基づいています。
コアが同じ変更に対して非常に多くのシグナルを送信する場合があり、大量の計算を行う多くのオブジェクトに変更ハンドラを設定すると、システムの処理速度が大幅に遅くなることがあります。
厳密には、変更ハンドラは、処理量が軽くて順序に依存しない「ダーティ フラグ」を設定し、再描画ビューや時間変更コールバックを使用して、実際にトリガされた処理の実行に使われます。
変更ハンドラは慎重に使用してください。スクリプト化コントローラを簡単に取得するなどのために使用することはお勧めしません。
使用したいセットアップが不要なシグナルであふれている場合は、
handleAt:
を使って redrawViews や timeChange イベントが起こるまでイベント ハンドラ スクリプトの実際の処理を遅らせます。