チュートリアル / 読んで触ってよくわかる!Mayaを使いこなす為のAtoZ
第74回:デフォルト値を取っておくツールを作ろう!
- Maya
- ゲーム
- コラム
- スクリプト・API
- チュートリアル
- 中級者
夏ですね。ここのところ強風で、外に置いていたものがどこかに飛ばされることもしばしば。適当に元の位置に戻しますが、デジタルの世界では座標という便利な仕組みのため、位置の数値を入れれば、正確に元の位置に戻ります。
背景のオブジェクトやリグのコントロールなど、オブジェクトをデフォルトの位置に戻せると便利なシチュエーションが多々あります。特にリグは、アニメーションを付けていると元のポーズに戻したいときがあります。ところが元のポーズに戻すのは意外と難しいものです。
Translate/Rotate/Scaleの値が0.0と1.0になるように作られていればまだ良いですが、1.23など微妙な値になっていると戻すのが難しくなります。リグのアトリビュートをすべてメモしても、復活の呪文をメモした日々のように、いつか間違えるときが来ます!そもそもマウスから手を放させないで!!横においているコーヒーがこぼれるから!という思いです。
ではどうやって元の位置を記録しておくか…。実はMayaには位置を記録しておく方法があります。キャラを作っている方にとってはおなじみのあの機能、Bind Skinです。バインドを行うと、バインドポーズというのが作られます。ジョイントの位置を変えてもGo to Bind Poseを実行すれば元の位置に戻ります。
この仕組はバインド以外にも使えます。というわけで、まずはバインドポーズの仕組みを見てみましょう。
バインドポーズの仕組み
例えばシーンのオブジェクトを選択してから、次のスクリプトを実行します。
dagPose -save -name "myPose";
実行するとmyPoseというノードが作られます。
このmyPoseのノード種類は「dagPose」で、その名の通りDAG(座標を持つオブジェクト)のポーズを記録するものです。ノードのどこに位置が記録されているかというと、worldMatrix配列アトリビュートに記録されます。
次のようにgetAttrでworldMatrixを取ると、各オブジェクトの座標が取れます。
getAttr myPose.worldMatrix[0]; // 1 0 0 0 0 1 0 0 0 0 1 0 -3.133187 0 0 1 // getAttr myPose.worldMatrix[1]; // 1 0 0 0 0 1 0 0 0 0 1 0 3.097503 0 0 1 //
元の位置に戻すには、次のコマンドを実行します。
dagPose -restore "myPose";
指定したdagPoseノードに記録された位置に、オブジェクトが復元されます。ジョイントではないオブジェクトでも良いのです!
でも個別のオブジェクトを戻したり、回転だけ元に戻したり、ということは出来ません。しかもdagPoseといっているだけあって、座標しか記録できません。マテリアルの色などは記録できないのです。
というわけで、デフォルト値を取っておくツールを自作する時が来ます。(やったー!)
順番に機能を実装してみましょう。
デフォルト値の保存
さて、dagPoseはノードを作り、アトリビュートにすべての位置を保存していました。
この方法の利点は多いですが、dagPoseノードをゴミだと思って捨ててしまうとオジャンになってしまうので、今回はノードにデフォルト値を記録するアトリビュートを追加するようにします。
次のMELスクリプトを実行すると、選択しているオブジェクトのTranslate/Rotate/Scaleの値を、〇〇_defaultというアトリビュート名で保存します。細かい説明はスクリプト内のコメントをご覧ください。
//////////////////////////////////////////////////////////// // デフォルト値を保存するアトリビュートの追加。(既に登録があれば更新) //////////////////////////////////////////////////////////// // 単純なアトリビュートタイプにのみ対応。 string $nodes[] = `ls -sl`; // 選択しているノードの取得します。 //string $attrs[] = {"translateX", "translateY", "translateZ", "rotateX", "rotateY", "rotateZ", "scaleX", "scaleY", "scaleZ"}; // 対象となるアトリビュートのリストを作ります。 string $attrs[] = {"tx", "ty", "tz", "rx", "ry", "rz", "sx", "sy", "sz"}; // 対象となるアトリビュートのリストを作ります。 for ( $node in $nodes ){ // 各ノードごとに実行します。 for ( $attr in $attrs ){ // アトリビュートごとに実行します。 if ( `attributeQuery -node $node -exists $attr` == 0 ){ // 対象のアトリビュートがあるかどうか確認。無い場合は・・・ continue; // スキップして次のアトリビュートへ。 } string $nodeAttr = $node + "." + $attr; string $defaultAttr = $attr + "_default"; // デフォルト値用アトリビュートの名前はtranslateX_defaultなどとします。 string $nodeDefaultAttr = $node + "." + $defaultAttr; if ( `attributeQuery -node $node -exists $defaultAttr` == 0 ){ // デフォルト値アトリビュートがすでにあるかどうか確認。 // 無い場合は、作成。 string $type = `getAttr -type $nodeAttr`; addAttr -longName $defaultAttr -attributeType $type -keyable true $node; // とりあえず今回は-keyable trueにしてチャンネルボックスに表示します。 } // この時点でデフォルト値アトリビュートが必ずあるので、値を更新。 $v = `getAttr $nodeAttr`; setAttr -lock false $nodeDefaultAttr; // ロックを解除。 setAttr $nodeDefaultAttr $v; // 値を設定。 setAttr -lock true $nodeDefaultAttr; // 再びロック。 } }
下記はキューブを選択して実行した結果です。ずらっと「〇〇 Default」というアトリビュートが追加されます。(実際のアトリビュート名は「tx_default」といった名前ですが、表示用にMayaが変換するので見た目は「Tx Default」となります。先頭文字は大文字に、アンダーバーはなくして、アンダーバーの後ろの文字も大文字になります。)
デフォルト値は間違えて変えられると困るので、作成時にアトリビュートをロックしておきます。ツールが作りやすいので、今はアトリビュートをチャンネルボックスに表示していますが、実際に仕事で使うときは「setAttr -keyable false ノード.アトリビュート」を実行して、非表示にすると良いです。
デフォルト値のアトリビュートから値を復元する
デフォルト値を設定できるようになりました。次は値を復元する機能を作ります。次のMELスクリプトを実行すると「〇〇_default」という名前のアトリビュートがあれば値を復元します。割と単純です。
//////////////////////////////////////////////////////////// // デフォルト値のアトリビュートから値を復元する //////////////////////////////////////////////////////////// string $nodes[] = `ls -sl`; for ( $node in $nodes ){ string $attrs[] = `listAttr -string "*_default" $node`; // _defaultという名前のアトリビュートをリストします。 for ( $attr in $attrs ){ string $buffer[]; tokenize $attr "_" $buffer; // translateX_defaultなどの名前が$attrに来ているので、アンダーバーで文字列を分割します。 string $targetNodeAttr = $node + "." + $buffer[0]; string $cachedNodeAttr = $node + "." + $attr; // Default値を復元。 $v = `getAttr $cachedNodeAttr`; setAttr $targetNodeAttr $v; print("Set " + $targetNodeAttr + "\n"); } }
結果としては下のような感じで、Translate値を見ていただくとデフォルト値に戻っているのがわかります。
リグや背景のオブジェクト、小物の配置には十分な機能が既に実装されています。でもよく考えたら、アトリビュートを作ったのはいいけれど削除出来ないと気持ち悪いです…。(誰かにゴミアトリビュート呼ばわりされるかもしれないですし。)
デフォルト値のアトリビュートを削除するツールも作りましょう。
デフォルト値のアトリビュートを削除
こちらも簡単なものでして、下のMELスクリプトを実行すると、選択したノードに〇〇_defaultという名前のアトリビュートがあれば削除します。アトリビュートがロックされていると削除できないので、事前にロックを解除するところがポイントです。
//////////////////////////////////////////////////////////// // デフォルト値のアトリビュートを削除 //////////////////////////////////////////////////////////// string $nodes[] = `ls -sl`; for ( $node in $nodes ){ string $attrs[] = `listAttr -string "*_default" $node`; // _defaultという名前のアトリビュートをリストします。 for ( $attr in $attrs ){ string $nodeAttr = $node + "." + $attr; setAttr -lock false $nodeAttr; deleteAttr $nodeAttr; } }
実行するとこんな風になります。
…画像ではArnoldのアトリビュートが表示されていますが、気にしないようにしましょう!
もっと使い勝手を良くするために改良しよう!
とりあえずはこれでツールとしては使えますが、リグでは特定のアトリビュートのみデフォルト値に戻したい!ということがあります。チャンネルボックスで選択しているアトリビュートでのみ、デフォルト値の保存・復元ができると良いと思いませんか?
実は、割と簡単に出来ます。次のMELスクリプトを実行すると、選択しているアトリビュート名を得られます。
channelBox -q -selectedMainAttributes mainChannelBox;
このコマンドでいけそうですね!
何もチャンネルを選択していなければ、すべてのキー可能アトリビュートを対象にすることで、わざわざ対象のアトリビュートをスクリプトにハードコードしなくてもすむようになります。そういった諸々を反映させたMELスクリプトは以下の様になります。
//////////////////////////////////////////////////////////// // デフォルト値を保存するアトリビュートの追加。(既に登録があれば更新) //////////////////////////////////////////////////////////// // 単純なアトリビュートタイプにのみ対応。 string $nodes[] = `ls -sl`; // 選択しているノードの取得します。 string $attrs[] = `channelBox -q -selectedMainAttributes mainChannelBox`; // チャンネルボックスの選択を元に、対象となるアトリビュートのリストを作ります。 if ( size($attrs) == 0 ){ // チャンネルボックスに選択がなかった場合は、すべてのキー可能アトリビュートが対象。 $attrs = `listAttr -keyable -shortNames $nodes[0]`; } for ( $node in $nodes ){ // 各ノードごとに実行します。 for ( $attr in $attrs ){ // アトリビュートごとに実行します。 if ( `attributeQuery -node $node -exists $attr` == 0 ){ // 対象のアトリビュートがあるかどうか確認。無い場合は・・・ continue; // スキップして次のアトリビュートへ。 } string $nodeAttr = $node + "." + $attr; string $defaultAttr = $attr + "_default"; // デフォルト値用アトリビュートの名前はtranslateX_defaultなどとします。 string $nodeDefaultAttr = $node + "." + $defaultAttr; if ( `attributeQuery -node $node -exists $defaultAttr` == 0 ){ // デフォルト値アトリビュートがすでにあるかどうか確認。 // 無い場合は、作成。 string $type = `getAttr -type $nodeAttr`; addAttr -longName $defaultAttr -attributeType $type -keyable true $node; // とりあえず今回は-keyable trueにしてチャンネルボックスに表示します。 } // この時点でデフォルト値アトリビュートが必ずあるので、値を更新。 $v = `getAttr $nodeAttr`; setAttr -lock false $nodeDefaultAttr; // ロックを解除。 setAttr $nodeDefaultAttr $v; // 値を設定。 setAttr -lock true $nodeDefaultAttr; // 再びロック。 } }
チャンネルボックスを選択していると、こんな感じでアトリビュートが追加されます。
デフォルト値に戻すスクリプトも、チャンネルボックスでの選択に対応させましょう。こちらもチャンネルボックスで何も選択していない場合は、すべてのキー可能アトリビュートが対象となるようにします。
//////////////////////////////////////////////////////////// // デフォルト値のアトリビュートから値を復元する // 選択したアトリビュートの値を復元。(「選択したアトリビュート_default」というアトリビュートがあればそれをデフォルト値として使用。) //////////////////////////////////////////////////////////// string $nodes[] = `ls -sl`; string $attrs[] = `channelBox -q -selectedMainAttributes mainChannelBox`; // チャンネルボックスの選択を元に、対象となるアトリビュートのリストを作ります。 if ( size($attrs) == 0 ){ // チャンネルボックスに選択がなかった場合は、すべてのキー可能アトリビュートが対象。 $attrs = `listAttr -keyable -shortNames $nodes[0]`; } for ( $node in $nodes ){ for ( $attr in $attrs ){ string $defaultAttr = $attr + "_default"; if ( `attributeQuery -node $node -exists $defaultAttr` == 0 ){ // 対象のアトリビュートがあるかどうか確認。無い場合は・・・ continue; // スキップして次のアトリビュートへ。 } string $targetNodeAttr = $node + "." + $attr; string $cachedNodeAttr = $node + "." + $defaultAttr; // Default値を復元。 $v = `getAttr $cachedNodeAttr`; setAttr $targetNodeAttr $v; print("Set " + $targetNodeAttr + "\n"); } }
まとめ
今回作成したツールは、オブジェクトの位置だけでなく、マテリアルなどあらゆるノードで機能します。汎用的にいろいろと使って様子を見てください。もっと機能が欲しくなると思いますので、改造してより良いツールに仕上げてみましょう!
こういうツールは、コードが短い割に仕事ではずいぶん役に立つものです。コードの内容も難しいものではなく、アトリビュートをMELでどう操作するかがわかれば様々なツールに発展できると思います。
Mayaはカスタマイズしてなんぼですので、どんどんツールを作ってみましょう!
(そして、今回はツールとして使用しなかったdagPoseもうまく活かせば何かのツールにできるかもしれません)