Melのglobal変数とoptionVarでメニューの値を保存



この記事はMaya Advent Calender 2019の10日目の記事です。


クリーク・アンド・リバー社 COYOTE CG STUDIO テクニカルチームの戦国で最強の色というと武田軍から連なる赤だと思う、戦国大好き人間の中林です。
そろそろクリスマスが近いですが、サンタクロースではなく赤い鎧の騎馬武者がプレゼントを配っても微笑ましい光景にはなりませんね。

今回はoptionVarとglobal変数というよりかは、作成したツールの設定の保存に関する話です。結論的にはglobal変数はツールの設定保存に向かないです。でも……

そもそものキッカケは?

これは僕がテクニカルアーティスト(以下、TA)と歩みだす前に社内でMelの自作ツールを提供し始めたころの話です。アーティストから提供したツールのメニュー設定が起動する度に初期値に戻るので、閉じる前の値を維持できないかと相談を受けました。
多分、この時の対応が僕のこの後の人生を変えたと思ってます。
 ⇒TA向きの対応「使う人が面倒なのでやってみる」
  TAに向かない対応「選択して数字打つだけだよ。大した手間じゃないだろ」
後者を選んでいたらTAになってなかったと思ってます。

比較用ソース:これはウィンドウを開くたびに入力フィールドが0になります。

{
    window;
    columnLayout;
    intField;
    showWindow;
}

global変数で値を保存する

global変数はMayaを立ち上げている限りは同名変数の内容を保存してくれます。

{
    global int $gInt;
    window;
    columnLayout;
    intField -v $gInt -cc "$gInt = `intField -q -v \"IFld\"`" "IFld";
    showWindow;
}

このソースは最初の実行こそは0ですが、好きな数値を打ち込めば開きなおすたびに、閉じた時の数値が表示されます。
数値が維持されるのはintFieldコマンドの-CCフラグで、数値が変わるたびに変数に数値を保存する命令が仕込んであるからです。
これで開くたびに数値を打ち直す必要が無いと思ったのですが……
Mayaを再起動すると0に戻ってしまいます。最初に書いた通りMayaを立ち上げている間しか覚えてないので仕方ないです。
ツールによっては都合の良い時もありますが、大抵のツールは再起動しても覚えていて欲しいものです。


optionVarで値を保存する

optionVarはuserPrefs.melに指定された変数型で値が保存されます。
こちらはMayaを落としても維持されますが当然、毎回呼び出す必要があります。

{
    global int $gInt;
    window;
    columnLayout;
    intField -v `optionVar -q "intOptvar"`
             -cc "optionVar -iv \"intOptvar\" `intField -q -v \"IFld\"`" "IFld;
    showWindow;
}

このソースも最初の実行こそは0ですが、それ以降は閉じる前の値が維持されます。
ここではintFieldの起動時の-vフラグにoptionVar -q "名前"で読み込んでいます。また、-ccフラグで変わるたびにoptinVarに保存してます。
optinVarは変数型が重要で決まった方で保存して、読み込まないとエラーの原因になります。
optionVar -iv "名前" 整数;
optionVar -fv "名前" 数値;
optionVar -sv "名前" "文字列";

ちなみに過去のブログ「Mayaのツールリセット状態を知るのに便利な『setOptionVars』
でも触れてますがMayaのメニューの設定はoptionVarです。
その意味でも自作メニューの値の保存はグローバル変数よりもoptionVar方が良いと思います。


optionVarの弱点

この2択ならoptionVarを勧めますが、全面的に凄いと思っているわけではありません。最も大きな弱点はリスト系の対応が弱いことです。フラグの-iva、-fva、-svaで複数の要素を収めることができるけど……
optionVar -sva "名前"{"要素1", "要素2", "要素3"};
こう書いた場合にリスト内の全要素が収まって欲しいのですが、最初の要素だけしか保存されません。一度で全ての要素が入らないので、for文などを利用して繰り返して入れる必要があります。

{
    for("要素X" in {"要素1", "要素2", "要素3", ……}){
        optionVar -sv "名前" "要素X";
    }
}

そして、途中の要素だけを消せないことです。要素1、要素2、要素3"を入れた後に要素2を消したい思ってもできません。
color=aqua>optionVar -ca "名前";
で空にした後に改めてfor文などで要素2を省いた要素を改めて収める必要があります。
この辺の処理が助長なのでできればoptionVarでリストは使いたくなかったりします。


第3の方法、外部ファイルに保存

メニューの保存要素の多さによるけど、実は外部ファイルに保存という手が一番のお勧めです。Melで考えると微妙な部分もありますが、僕も最近はpythonでツール作成が多くなってきてます。pythonだとJSON等の辞書型が使えるので普段は変数代わり使って、外部ファイルに保存というのをよくやります。
外部ファイルは「C:/Windows/temp」フォルダを間借りしてこっそりと保存してるけど、ちょっとエクスポートする場所を変えれば、アーティストがオブジェクト毎の設定に合わせて保存できるようになります。
これの大きなメリットはキャラAの設定を外部ファイルに保存。数日間キャラBを作業で設定を変更。再びキャラAに戻った時に設定を読み直せることです。
初めから外部ファイルを使っていれば起動時の値の読み込みも、アーティストが外部データを読み込んだ処理も共通化できるのでお勧めです。僕的にはメニューで値を保存する部分が7ヶ所を超えるか、リストをメニューに組み込むと判断した時点で外部ファイルにしています。

・セーブ例

{
    string $SaveCmnt = "セーブしたい文字列";
    int $fileId =`fopen "C:/Windows/temp/save.text" "w"`;
    fprint $fileId $SaveCmnt;
    fclose $fileId;
}

・ロード例

{
    int $fileId = `fopen "C:/Windows/temp/save.text" "r"`;
    $NextLine = `fgetline $fileId`; // 改行までを取得
    `fgetline $fileId`の中身が空になるまでfor文やwhile文で繰り返す
    fclose $fileId;
}

メニューの値保存は意外と大きい

ツールを作る側でも使う側でも値を毎回入力すれば良いと意見する人は一定数います。
でも、実際にツールを使う側が毎回手入力でメニューを変更すると手数がふえるし、どこかでケアレスミスすることもあります。なにより値入力派の大半は自動保存の便利さを知らないだけで、ツールで値保存に慣れると無かった時代には戻ってきません。
ツールを作る側の方では共通処理を作ればいくらでも組み込む手間は減らせます。もし自作ツールのメニューで値を保存をしてない人がいたら、ぜひ実装を試してみてください。


明日12/11は YokoUさんの「matrixコンストレイン」です、おたのしみに!

Author: 中林 伸和