チュートリアル / 読んで触ってよくわかる!Mayaを使いこなす為のAtoZ
第74回:デフォルト値を取っておくツールを作ろう!

2018.07.19

  • 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もうまく活かせば何かのツールにできるかもしれません)

Maya Learning Channel
製品購入に関するお問い合わせ
オートデスク メディア&エンターテインメント 製品のご購入に関してご連絡を希望される場合は、こちらからお問い合わせください。