チュートリアル / 読んで触ってよくわかる!Mayaを使いこなす為のAtoZ
第51回:現在のUVセット名をメニューに表示してみよう!(2/2)
- Maya
- アニメーション
- ゲーム
- コラム
- チュートリアル
- 中級者
- 学生・初心者
- 教育
- 映画・TV
前回で「現在のUVセット名を取る」方法と「GUIを作っている場所を探す」方法を紹介しました。
まだツールのパーツが手に入った状態です。どのようにパーツを組み立てるかが、後々のメンテナンス性に関わってきます。
MELは基本的にバージョン依存しません。上手くスクリプトが作られていればMayaのバージョンが上がった時、ツールそのまま流用できます。今回はメンテナンス性の部分も考慮しながら、ツールを仕上げていきます。
ではさっそく続きを作っていきましょう!
次の図の様にUV Texture Editorのメニューに、選択しているメッシュの現在のUVセット名表示させることが目標です。
Mayaの次のバージョンで「texturePanel.mel」が変更されたとします。このファイルを直接編集した場合、前のバージョンとの差分を見て、編集し直す事になります。これはかなりの手間ですし、バグを誘発する可能性もあります。なるべくなら避けたいところです。
デフォルトのmelファイルを変更せず、自分で作るスクリプトからGUIを操作するようにできればとてもメンテナンスが簡単になります。Mayaのバージョンが上がっても「texturePanel.mel」の更新をチェックしないで済むようになります。
外からメニューを操作するためにはGUI名を利用します。GUI名とはメニューやボタンなどのGUI要素「そのもの」の名前です。例えば、次のMELを実行するとウインドウが立ち上がります。
GUIを作るMELコマンドの最後に書かれている文字列がそのGUIの名前になります。
上の例では、ウインドウの名前は
「koregaGUIName」
となります。
「text」はちょっとトリッキーです。MELコマンドの最後の文字列は「kore-ja-nai!」なので、 これがGUI名と思いそうですが、実は違います。
これは「-l」フラグのための文字列です。ラベル表示としての「kore-ja-nai!」という文字列を指定しているだけなので、これはGUI名じゃないです。
このようにGUI名が指定されていない場合、Mayaがかってにいい感じの名前をつけています。
さて「showWindow」のコマンドを見てみると、ウインドウのGUIを名が指定されています。このように後からGUI名を指定してMELを実行することができます。GUI名がわかっていれば、あらゆるところから操作することが可能になります。
…というわけでUV Sets メニューのGUI名を調べましょう。
メニューを作るのに「menu」コマンドが使われています。コマンドの最後に書かれているのがGUI名ですので、次の図の赤線の所がGUI名です。
うーむ、「($editor+"UVSetsMenu")」となっています…。
「$editor」が不明なので、とりあえず次の様なスクリプトを仕込んで、プリントしてみましょう。
「texturePanel.mel」のテキストをスクリプトエディタにまるごとコピペし、実行します。これでスクリプトが更新されます。
UV Texture Editorを再び立ち上げると
「polyTexturePlacementPanel1Window|TearOffPane|polyTexturePlacementPanel1|polyTexturePlacementPanel1UVSetsMenu」というのがプリントされます。長い名前です。
でもこれがUV SetsメニューのGUI名です。
「|」文字が何度か出てきますが、これはGUIの階層を表しています。GUIもシーン内のオブジェクトと同様に階層を持っています。こんな感じで。
「polyTexturePlacementPanel1Window」 → UV Texture Editor ウインドウ名
+「TearOffPane」 → ペインレイアウト名
+「polyTexturePlacementPanel1」 → パネル名
+「polyTexturePlacementPanel1UVSetsMenu」 → メニュー名
GUI名がわかったので、これで「texturePanel.mel」外からでもメニューをいじれるようになりました。テストしてみましょう。
UV Texture Editorを開いて、次のスクリプトを実行します。
すると「UV Sets」メニューのラベル名が「Click Me!」に変わります。
(ここでのサンプルコードは、GUI名が長すぎてブラウザで上手く表示されないため、文字列を連結するようにしています。)
ついでに、複数メッシュを選択している時の表示の工夫を行いましょう。複数オブジェクトを選択しているとき、それぞれのUVセット名をメニューに表示するとメニューが「UV Sets(map1, map1, map1)」という感じでものすごく横長になってしまいます。これではUVセット名がどのメッシュのことかわからないです…。
次のルールでラベル名を見やすくしてみます。
・「UV Sets(currentUVSet)」というフォーマットで表示する。
・複数メッシュで、カレントUVセット名が同じなら、その名前を表示。「UV Sets(map1)」
・複数メッシュでカレントUVセット名がマチマチな場合、始めのメッシュのUVセット名は表示するがその他は「…」と表示して、沢山のUVセットがあるということをほのめかす。「UV Sets(firstMeshUVSet…)」
実装するとこんな感じになります。
メッシュを選択して次の関数を実行すると、UV Setsメニュー名が更新されます。
そこで、スクリプトジョブという機能を利用して、オブジェクトの選択が変わったら自動で関数が実行されるようにしましょう。
スクリプトジョブとは、一般的にはコールバックと呼ばれる機能です。「scriptJob」というMELコマンドで作成します。要はMayaで何か操作をした時に、指定したスクリプトを実行する機能です。
例えば次の瞬間にスクリプトを実行出来ます。
・オブジェクトの選択を変更した時
・GUIを削除した時
・カレント時間が変わった時
・Undo/Redoした時
他にも様々な実行タイミングがあります。今回は「オブジェクトの選択を変更した時」に関数を呼ぶようにしましょう。
設定は簡単です。次のスクリプトを実行するだけです。
シーン内のオブジェクトの選択を変えてみて、UV Setsメニュー名が自動で変更するのを確認すれば完成です!
これでUV編集が一段とやりやすくなりました!
メッシュを選択すれば現在のUVセットを素早く確認できるようになりました。ちょっとした事ですが、データを正確にキレイに作るためには重要なツールです。
ゲーム制作でよくあることとして、いつのまにか2つ目のUVセットがカラーテクスチャのUVになっていたりします。普通は1つ目のUVセットがカラーテクスチャに使われます。実機に出してみると、テクスチャの表示がおかしくなる原因になったりします。こういう問題を事前に確認しやすくすることで、データがキレイになる、なりやすくなる、というわけです。
スクリプトジョブはノードをシーン内に作るわけではありません。Mayaに登録するものです。見た目に確認しづらいので注意が必要です。何度も「scriptJob」を実行すると上書きされずに、実行した分ジョブが増えていきます。
うっかりすると同じ関数が何十回も実行されていたりすることがあります。段々とMayaが重くなったり、なぜがメモリの消費が増え続けたりしている時はその可能性があります。
スクリプトジョブを上手く管理するには、次のスクリプトを参考にしてみてください。グローバル変数でスクリプトジョブのIDを保存しておいて、複数登録しないようにしたり、後で削除したりするのに使います。
なおスクリプトジョブはMayaが終了したら全て消えます。保存されることはありません。Mayaを終了するときにわざわざスクリプトジョブを削除しなくても大丈夫です。また、間違っていっぱいジョブを登録してしまった時もMayaを再起動させれば大丈夫です。
・UVセット名を取得する方法。
・Mayaの既存のメニューが作成されるところの見つけ方。
・Mayaの既存melを変更せず、メニューを賢く変更する方法。
・スクリプトジョブの使い方。
同じノリでUV Texture Editor以外でも好きなメニューを追加・変更することができます。
ツールの考え方として、「情報」を取ることと「GUI」に表示することに分けて考えました。お互いに自由に組み合わせることが出来ますから、例えば前回の株価表示の様に、HUDにUVセット名を表示しても良さそうですね。逆にメニューに株価を出してもいいですけど…。
今回のツールでは、オブジェクトの選択を変えた時にスクリプトジョブが実行されて、GUIが更新されますが、これでは場合によってGUIが更新されないこともあります。例えば現在のUVセットを変更してもGUIが更新されません。オブジェクトの選択が変わらないので。
どういう操作を行っても、確実にGUIが更新されるようにするには、沢山のスクリプトジョブを登録する必要があります。これは悩ましいところでして、抜けがないようにスクリプトジョブを登録する作業は、結果としてツール本体を作るよりも時間がかかることがあります。
ですので、完璧を目指すと危険です。必要十分なところで止めて、後は制限事項として周知するほうが現実的で、費用対効果も上がります。ツールを作っているとどうしても完璧な動作にしたい気持ちが出てくると思いますが、完璧に仕上げるよりは、新しいツール作成に着手するほうが、結果としてプロジェクトへの貢献につながります。ツールの問題点はあとから必要に応じて直していけばそれでいいのです。
バグを潰すよりも、「Mayaで新しいことができるようになる」という点に注目してツールを作ると、テクニカルアーティストとしての価値が上がっていきます。
まだツールのパーツが手に入った状態です。どのようにパーツを組み立てるかが、後々のメンテナンス性に関わってきます。
MELは基本的にバージョン依存しません。上手くスクリプトが作られていればMayaのバージョンが上がった時、ツールそのまま流用できます。今回はメンテナンス性の部分も考慮しながら、ツールを仕上げていきます。
ではさっそく続きを作っていきましょう!
次の図の様にUV Texture Editorのメニューに、選択しているメッシュの現在のUVセット名表示させることが目標です。
メンテナンスの手間を減らすGUI変更方法
ここまでで「texturePanel.mel」を書き換えてラベル名を変更することが出来ました。でもこのファイルはMayaのデフォルトのmelファイルです。あまりいじると後でメンテナンス等面倒になってきます。Mayaの次のバージョンで「texturePanel.mel」が変更されたとします。このファイルを直接編集した場合、前のバージョンとの差分を見て、編集し直す事になります。これはかなりの手間ですし、バグを誘発する可能性もあります。なるべくなら避けたいところです。
デフォルトのmelファイルを変更せず、自分で作るスクリプトからGUIを操作するようにできればとてもメンテナンスが簡単になります。Mayaのバージョンが上がっても「texturePanel.mel」の更新をチェックしないで済むようになります。
外からメニューを操作するためにはGUI名を利用します。GUI名とはメニューやボタンなどのGUI要素「そのもの」の名前です。例えば、次のMELを実行するとウインドウが立ち上がります。
window -title "test" koregaGUIName; columnLayout; text -l "kore-ja-nai!"; showWindow "koregaGUIName";
GUIを作るMELコマンドの最後に書かれている文字列がそのGUIの名前になります。
上の例では、ウインドウの名前は
「koregaGUIName」
となります。
「text」はちょっとトリッキーです。MELコマンドの最後の文字列は「kore-ja-nai!」なので、 これがGUI名と思いそうですが、実は違います。
これは「-l」フラグのための文字列です。ラベル表示としての「kore-ja-nai!」という文字列を指定しているだけなので、これはGUI名じゃないです。
このようにGUI名が指定されていない場合、Mayaがかってにいい感じの名前をつけています。
さて「showWindow」のコマンドを見てみると、ウインドウのGUIを名が指定されています。このように後からGUI名を指定してMELを実行することができます。GUI名がわかっていれば、あらゆるところから操作することが可能になります。
…というわけでUV Sets メニューのGUI名を調べましょう。
メニューを作るのに「menu」コマンドが使われています。コマンドの最後に書かれているのがGUI名ですので、次の図の赤線の所がGUI名です。
うーむ、「($editor+"UVSetsMenu")」となっています…。
「$editor」が不明なので、とりあえず次の様なスクリプトを仕込んで、プリントしてみましょう。
print ($editor+"UVSetsMenu");
「texturePanel.mel」のテキストをスクリプトエディタにまるごとコピペし、実行します。これでスクリプトが更新されます。
UV Texture Editorを再び立ち上げると
「polyTexturePlacementPanel1Window|TearOffPane|polyTexturePlacementPanel1|polyTexturePlacementPanel1UVSetsMenu」というのがプリントされます。長い名前です。
でもこれがUV SetsメニューのGUI名です。
「|」文字が何度か出てきますが、これはGUIの階層を表しています。GUIもシーン内のオブジェクトと同様に階層を持っています。こんな感じで。
「polyTexturePlacementPanel1Window」 → UV Texture Editor ウインドウ名
+「TearOffPane」 → ペインレイアウト名
+「polyTexturePlacementPanel1」 → パネル名
+「polyTexturePlacementPanel1UVSetsMenu」 → メニュー名
GUI名がわかったので、これで「texturePanel.mel」外からでもメニューをいじれるようになりました。テストしてみましょう。
UV Texture Editorを開いて、次のスクリプトを実行します。
すると「UV Sets」メニューのラベル名が「Click Me!」に変わります。
(ここでのサンプルコードは、GUI名が長すぎてブラウザで上手く表示されないため、文字列を連結するようにしています。)
menu -e -l "Click Me!" ("polyTexturePlacementPanel1Window|TearOffPane|" + "polyTexturePlacementPanel1|polyTexturePlacementPanel1UVSetsMenu");
スクリプトをまとめる
では前回作ったUVセット名を取ってくるスクリプトと、メニューラベルを更新するスクリプトを一緒にして、ひとつの関数にまとめてみましょう。ついでに、複数メッシュを選択している時の表示の工夫を行いましょう。複数オブジェクトを選択しているとき、それぞれのUVセット名をメニューに表示するとメニューが「UV Sets(map1, map1, map1)」という感じでものすごく横長になってしまいます。これではUVセット名がどのメッシュのことかわからないです…。
次のルールでラベル名を見やすくしてみます。
・「UV Sets(currentUVSet)」というフォーマットで表示する。
・複数メッシュで、カレントUVセット名が同じなら、その名前を表示。「UV Sets(map1)」
・複数メッシュでカレントUVセット名がマチマチな場合、始めのメッシュのUVセット名は表示するがその他は「…」と表示して、沢山のUVセットがあるということをほのめかす。「UV Sets(firstMeshUVSet…)」
実装するとこんな感じになります。
global proc currentUVSetOnUVTextureEditor(){ string $currentUVSets[] = `polyUVSet -q -currentUVSet`; // 現在のUVセット名が全てで同じ かどうかチェック。 // 異なる場合、UVセット名は"..."にする。 string $uvSet; if ( size($currentUVSets) > 0 ){ $uvSet = $currentUVSets[0]; // その他メッシュのUVセット名と比べる。 for ( $i = 1; $i < size($currentUVSets); $i++ ){ if ( $uvSet != $currentUVSets[$i] ){ $uvSet += "..."; break; } } } // メニューの更新。 string $menu = "polyTexturePlacementPanel1Window|TearOffPane" + "|polyTexturePlacementPanel1" + "|polyTexturePlacementPanel1UVSetsMenu"; // 決め打ちの名前。 string$origMenuLabel=uiRes("m_texturePanel.kUVSets"); // = "UV Sets" string $label; if ( $uvSet == "" ){ // UVセットがない、もしくは何も選択されていない場合。 // 通常のメニュー名で表示。 $label = $origMenuLabel; // "UV Sets" } else { $label = $origMenuLabel + " (" + $uvSet + ")"; // "UV Sets(map1)" } if ( `menu -q -ex $menu` ){ menu -e -l $label $menu; } }
メッシュを選択して次の関数を実行すると、UV Setsメニュー名が更新されます。
currentUVSetOnUVTextureEditor();
自動的にメニューを更新させる
メッシュを選択する度に関数を実行して表示を更新していては、UV SetsメニューをクリックしてUVセットを確認するのと全然手間が変わりません…。そこで、スクリプトジョブという機能を利用して、オブジェクトの選択が変わったら自動で関数が実行されるようにしましょう。
スクリプトジョブとは、一般的にはコールバックと呼ばれる機能です。「scriptJob」というMELコマンドで作成します。要はMayaで何か操作をした時に、指定したスクリプトを実行する機能です。
例えば次の瞬間にスクリプトを実行出来ます。
・オブジェクトの選択を変更した時
・GUIを削除した時
・カレント時間が変わった時
・Undo/Redoした時
他にも様々な実行タイミングがあります。今回は「オブジェクトの選択を変更した時」に関数を呼ぶようにしましょう。
設定は簡単です。次のスクリプトを実行するだけです。
scriptJob -event "SelectionChanged" "currentUVSetOnUVTextureEditor()";
シーン内のオブジェクトの選択を変えてみて、UV Setsメニュー名が自動で変更するのを確認すれば完成です!
これでUV編集が一段とやりやすくなりました!
メッシュを選択すれば現在のUVセットを素早く確認できるようになりました。ちょっとした事ですが、データを正確にキレイに作るためには重要なツールです。
ゲーム制作でよくあることとして、いつのまにか2つ目のUVセットがカラーテクスチャのUVになっていたりします。普通は1つ目のUVセットがカラーテクスチャに使われます。実機に出してみると、テクスチャの表示がおかしくなる原因になったりします。こういう問題を事前に確認しやすくすることで、データがキレイになる、なりやすくなる、というわけです。
スクリプトジョブの注意点
今回ツールを作っている時に「scriptJob」コマンドを実行すると、スクリプトエディタに「// Result: 117 //」と表示されました。この数字はスクリプトジョブのIDです。スクリプトジョブを実行するとMayaにジョブが登録されてIDが決まります。実行するタイミングにより異なる数字になります。後からジョブを削除するときにこの番号を指定して削除します。スクリプトジョブはノードをシーン内に作るわけではありません。Mayaに登録するものです。見た目に確認しづらいので注意が必要です。何度も「scriptJob」を実行すると上書きされずに、実行した分ジョブが増えていきます。
うっかりすると同じ関数が何十回も実行されていたりすることがあります。段々とMayaが重くなったり、なぜがメモリの消費が増え続けたりしている時はその可能性があります。
スクリプトジョブを上手く管理するには、次のスクリプトを参考にしてみてください。グローバル変数でスクリプトジョブのIDを保存しておいて、複数登録しないようにしたり、後で削除したりするのに使います。
global int $uvSetsLabelUpdateJob; // スクリプトジョブ番号を保持するための変数 if ( $uvSetsLabelUpdateJob == 0 ){ // まだジョブが作られていないので、作る。 $uvSetsLabelUpdateJob = `scriptJob -event "SelectionChanged" "currentUVSetOnUVTextureEditor()"`; } // スクリプトジョブの削除 if ( $uvSetsLabelUpdateJob != 0 ){ scriptJob -kill $uvSetsLabelUpdateJob; $uvSetsLabelUpdateJob = 0; // リセット }
なおスクリプトジョブはMayaが終了したら全て消えます。保存されることはありません。Mayaを終了するときにわざわざスクリプトジョブを削除しなくても大丈夫です。また、間違っていっぱいジョブを登録してしまった時もMayaを再起動させれば大丈夫です。
まとめ
前回から二回にわたってツールを作ってきました。次の項目の辺りを紹介してきました。・UVセット名を取得する方法。
・Mayaの既存のメニューが作成されるところの見つけ方。
・Mayaの既存melを変更せず、メニューを賢く変更する方法。
・スクリプトジョブの使い方。
同じノリでUV Texture Editor以外でも好きなメニューを追加・変更することができます。
ツールの考え方として、「情報」を取ることと「GUI」に表示することに分けて考えました。お互いに自由に組み合わせることが出来ますから、例えば前回の株価表示の様に、HUDにUVセット名を表示しても良さそうですね。逆にメニューに株価を出してもいいですけど…。
今回のツールでは、オブジェクトの選択を変えた時にスクリプトジョブが実行されて、GUIが更新されますが、これでは場合によってGUIが更新されないこともあります。例えば現在のUVセットを変更してもGUIが更新されません。オブジェクトの選択が変わらないので。
どういう操作を行っても、確実にGUIが更新されるようにするには、沢山のスクリプトジョブを登録する必要があります。これは悩ましいところでして、抜けがないようにスクリプトジョブを登録する作業は、結果としてツール本体を作るよりも時間がかかることがあります。
ですので、完璧を目指すと危険です。必要十分なところで止めて、後は制限事項として周知するほうが現実的で、費用対効果も上がります。ツールを作っているとどうしても完璧な動作にしたい気持ちが出てくると思いますが、完璧に仕上げるよりは、新しいツール作成に着手するほうが、結果としてプロジェクトへの貢献につながります。ツールの問題点はあとから必要に応じて直していけばそれでいいのです。
バグを潰すよりも、「Mayaで新しいことができるようになる」という点に注目してツールを作ると、テクニカルアーティストとしての価値が上がっていきます。