執筆者:広森
映像業界を経てモノリスソフトへ入社。 以来、テクニカルアーティストとして主にリギング・セットアップ関連の業務を担当。 好きな飲み物は赤ワイン。
TECH BLOG
![]()
こんにちは。モノリスソフト テクニカルアーティストの広森です。
今回はCGやゲームを制作する際に頻繁に耳にする「ネーミングルール」について、実際に決める際の基準を、ツールを作成する立場の目線でまとめてみました。
これから新規のネーミングルールの仕様を切らなければならない人や、既存のネーミングルールに合わせてスクリプトを作成する際に力になればと思います。
スクリプト自体はcmdsで記述している為Mayaでしか動作しませんが、UE4や3dsMaxなどゲームエンジンやDCGツールでも運用することができる考え方になっています。
初めてスクリプトを作る方にも分かるよう、ツールの基本操作やコツもまとめていますので、ぜひお読みください。
※既にプロジェクトにネーミングルールがある場合は必ずそのルールを守ってください。あくまでも新規で決める際の参考にお願いします。
ネーミングルールを決める際、スクリプトの作成や使用時のことも考慮できるようになる。
ネーミングルールだけでなく、別の手段を用いたオブジェクト管理の仕方を学ぶことができる。
ネーミングルールを利用してオブジェクトを選択したり、追加の処理を行うなどのスクリプトの知識を身に着けることができる。
プログラマーやTAに対して、効率的なツール作成依頼などができるようになる。
まず2枚の画像を用意しました。
| 良い例 | 悪い例 |
|---|---|
![]() |
![]() |
|
|
上記2つを見比べてみて左の方が綺麗な名前になっていますね。右の場合だと読みづらい仕様になっており、スクリプト目線以前にかなり扱いづらそうです。
今回はかなり極端な例を用意しましたが、実際に現場で見かけることも多い問題です。以降の章で、ネーミングルールを整理するコツを紹介します。
先ほどの悪い例を見てみると、オブジェクト名に含む必要がなさそうな要素がいくつかみられますね。必要な情報でも、すべてを名前に埋め込んで管理するようなことは避け、名前以外の管理方法を検討しましょう。
名前以外の管理方法が気になる方は、おまけの章にいくつかのやりかたを用意しておきます。
今回は以下の要素は名前に含まなくて良いものとしています。理由としてオブジェクトを構成する要素には関係がなく、アウトライナーを用いて作業する際に使用しない為です。
大きさ
今回の名前ではheight171やsice1_cm、size3cmが該当します
キャラクター名
今回の名前ではcharacterNameが該当します。
性別
今回ではboyが該当します。
キャラクター名は不要な要素にして良いのか?と疑問に持った方はとても良い目線です。1つのシーンで複数のキャラクターを配置した場合見分けが付かなくなるので、区別がつく名前が必要になりますね。
ただ、オブジェクトを作成する際に毎回名前を付けてあげたりする手間がかかってしまいとても面倒くさそうです。そこで名前で管理せずネームスペースで運用してあげましょう。
ネームスペースで管理することでキャラクター専用の名前を含まず同じルールで骨の名前を運用できるので、オブジェクトを作成するスクリプトの使いまわしができるようになります。
小文字だけ、大文字だけなどで表記すると単語の区切りの判別が難しく視認性が下がります。そこで、プログラミングでも使われている表記方法を利用することで視認性を高めましょう。
筆者はMayaでオブジェクトを作成した際と同じルールである、ローワーキャメルケースを採用しています。
スネークケースをMayaオブジェクトの名前に採用できない理由は後の章で紹介します。
ローワーキャメルケースとは、最初の頭文字を小文字でスタートし、単語ごとの頭文字を大文字で記載する書き方です。
アッパーキャメルケースとは、最初の頭文字を大文字でスタートし、単語ごとの頭文字を大文字で記載する書き方です。
なぜキャメルケースと呼ばれるの?
頭文字が大きい様子をラクダの背中のこぶに例えて表現したからと言われています。
スネークケースとは大文字は使用せず全て小文字で記載し、単語間をアンダースコア(_)で区切る手法です。視認性が一番高い手法でPythonでも一部スネークケースを推奨の書き方としています。
なぜスネークケースと呼ばれるの?
小文字で構成された文字が平板な様子を蛇に例えて表記したからと言われています。
名前の視認性が高まったので、実際にスクリプト側からオブジェクトを取得してみましょう。cmdsに強い方は「何故抽出しやすくなるの?」まで読み飛ばして頂ければと思います。
melという名前を聞いたことがある方は、なぜcmdsを選択しているの?と考えると思います。確かにmelでも簡単なスクリプトを書く分には問題はないと思いますが以下の懸念がある為、Pythonを選択しています。
大きなスクリプトを作ろうとした場合にmelには機能が少なく、pythonなら機能が充実している。
Pythonでのスクリプト作成に便利なエディタがそのまま使える。
オブジェクト指向と呼ばれるものが使える(今回は詳細に触れませんが大きなメリットなので、名前だけでも覚えておいてください)。
melはMayaでしか使えないがPythonならMaya以外でも使えるので、習得すればいろいろな方面で活かすことできる。
業界全体でmelからPythonベースに移行している傾向があり、melの機能や書き方を忘れてしまっている人が多い為、PythonならプログラマーやTAに相談した際にmelか...と悲しい顔をされない。
コマンドにはオプションを設定できる機能があり、設定することで追加の機能を呼び出すことができます。このオプションがmelと一致していることもあり、melには日本語ドキュメントが存在しているので良いとしています。
Mayaを使用中にどんなコマンドが動いているのかを確認したい場合はスクリプトエディタを確認してください。
melやPythonを記載する為のエディタです。以下の画像の丸で囲われた箇所をクリックすると立ち上がります。
以下の画像でよく使うボタンを説明します。
Maya内で作業を行うと対応するコマンドが表示される場所です。
melやpythonを記述する場所です。今回用意したスクリプトをコピペする場所はこちらです。
①の内容を綺麗にするコマンドです。
②に記載された内容を全て実行します。
②に記載されたコマンドをドラッグしていた部分のみ実行します。
新しくコマンドを記載するタブを作成します。
⑥をクリックすると出てくるウィンドウです。今回はPythonを選択してください。
作業をしても上記の画像の④にコマンドが表示されない場合は以下の機能を使ってみてください。
melでしか実行できないコマンドも数は少ないですが存在します。いずれ必要になるかもしれませんが最初は問題ないと思います。
実際に検索する手順として以下のように進めるとわかりやすいかと思います。
maya上で行いたい作業を実際にする。
スクリプトエディタにmelコマンドが逐一出るので要素を抽出していく。
「Autodeskの公式ページ内のPythonコマンドページ」で同じコマンド名を検索し、実際に呼び出して機能を習得する。
cmdsに存在しなかった場合は代わりの機能を調べる。
代わりの機能がどうしても見つからない場合は以下のコマンドを使用し、pythonから直接melコマンドを実行する。
eval
import maya.mel as mel #'実行したいコマンドを文字列で記載する。記載方法はmelの文法で書く' mel.eval()
今回は"JNT"という文字列が名前に含まれるオブジェクトのみをシーン内から抽出して自動で選択するスクリプトを作りました。行の解説は1行目以外、上に記載しています。
コピペして編集する場合はスペースの位置などには充分注意して編集してみてください。
pythonの書き方に沿っていない場合は# Error: invalid syntax # とスクリプトエディタに表示されます。スペースや文字列の書き方、半角で記入など書き方があっているかチェックしましょう。
スクリプトエディタに貼り付けてPython形式で実行してみてください。
nodeNameSelect
# -*- coding: utf-8 -*- # ↑1行目に書くことでスクリプト内に日本語が使えるようになります。日本語を使わない場合は記載しなくても良いです。 # maya.cmdsに含まれる機能を使いたいので読み込む処理。毎回maya.cmdsと記載するのが面倒くさいのでこの行以降はcmdsの文字と記載するだけでOKにする記述を書く。 import maya.cmds as cmds # ネーミングルールに沿った名前のノードを入れる容器みたいなもの。リスト型と呼ばれ、[]で記載する。 select_target_node = [] # シーンに存在する全てのノードを取得する。cmds.ls(selection=True)と記載すると現在選択しているオブジェクトのみ取得してくる。 all_nodes = cmds.ls() # 全てのノードがall_nodesの中に入っているのでループ処理をする。改行されて、半角スペース4つずれている箇所がループ中に行われる内容。以降半角スペース4つずれている事をインデントと書きます。 # for文を書く場合は必ず最後に:をつけましょう。 for node in all_nodes: # nodeの中にはmayaのノード名が含まれています。 # ノードの名前の中に文字列JNTが入っているか調べる。文字列を指定する場合""で囲むか''で囲みましょう。 # if文を記載する場合はfor文と同じく最後に:をつけましょう。 if "JNT" in node: # インデントがずれている箇所が上記のJNTが名前に含まれる場合の処理です。 # ネーミングルールに沿った名前のみ選択してあげたいので、正しいものはこのリストに追加する。追加するリストに.append(入れたい要素)でOK select_target_node.append(node) # else文を記載する場合はfor文と同じく最後に:をつけましょう。 else: # インデントがずれている箇所が上記のJNTが名前に含まれなかった場合の処理です # 今回はとくに何もしないのでpassと記載します。初心者向けなので記載していますがpassの場合はelse文自体を省略できます。 pass # ループが終わったら文字列JNTが含まれているオブジェクトのみを選択してみます。cmds.select(選択したいもの)と記載すればよいです。 cmds.select(select_target_node) # 処理が終わったことをテキストでスクリプトエディタに表示します。表示したい内容をprintの横に書きましょう(この書き方はpython3での書き方です)。 print ("Finish!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
意図した通りの要素が選択できたでしょうか?これでネーミングルールに沿ってスクリプトから自動で選択することができましたね。
今回は選択する処理を最後に記載しましたが、cmds.delete(消したいもの)と記載することでネーミングルールに沿った、シーン内に要らないものを削除することもできます。
if文の中に処理を行いたい内容を記載することで要素ごとに処理もできます。
複数の条件分岐を行いたい場合の例として以下に編集した例を載せます。(コメントを記載していない箇所は上のスクリプトと同じです。)
nodeNameSelect2
# -*- coding: utf-8 -*- import maya.cmds as cmds select_target_node = [] all_nodes = cmds.ls() for node in all_nodes: if "JNT" in node: select_target_node.append(node) # if文以外に条件を記載する場合はelifと記載することで別の条件を指定することができます。 elif "CTL" in node: # cmds.setAttr(設定したいノード名とアトリビュート名)で値をセットすることができます # 今回はノード名だけ変わるのでf-stringsの書き方を採用。文字列の前にfを記載し、{}で囲われた場所が指定した値に変わった状態で文字列を生成することができます # f"{node}.rotate"と書くことで、for文のnodeの値が参照され、事前に設定したいアトリビュート名rotateと付け足して書くことができます。 # デフォルトでは1つしか値を受け取らないので、rotateなど3つ同時に数値を設定したいときはtype="double3"と明記する # CTLがノード名に含まれる場合に回転値を全て0に設定する cmds.setAttr(f"{node}.rotate",0,0,0,type="double3") # 更に条件を設けたい場合は同じelifの記載で問題ないです。 elif "NUL" in node: # NULがノード名に含まれる場合にスケールXの値を1に設定する cmds.setAttr(f"{node}.scaleX",1) else: pass cmds.select(select_target_node) print ("Finish!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
名前で検索した場合、今回はinの機能で検索しているので文字が含まれていたらそのオブジェクトも対象にしてしまいます。
完全に一致した場合のif文を書いて対応することもできますが、毎回毎回その文章を書くのは効率が悪いのと、書き方がスマートでないですね。
これらを解決するために筆者はアンダースコア(_)で区切ることで要素を抽出しやすくしています。
pythonには特定の文字列で文字を切り分ける機能があります。.split()でカッコ内に切り分けたい要素を渡すと切り分けた状態の要素が含まれた配列(リスト)型で返ってきます。
要素が切り分けられなかった場合は、切り分けたい要素が配列内に含まれた状態で返ってきます。返ってきた値を扱う際に注意点があり、pyhtonでは配列の始まりは0番目からスタートしますので順番を指定する際は気を付けてください。
そして、-1で配列の最後の要素を指定することができます。
余談ではありますが、スクリプト言語によっては1番目からスタートする言語もあります。混在しないように気をつけてください。
以下に、実際に選択しているノードの名前を抽出し、最初に選んだノードの名前をアンダースコア(_)で切り分けて表示するスクリプトを用意したので実際に動作確認してみてください。
nodeNameSplit
# -*- coding: utf-8 -*- import maya.cmds as cmds sel_nodes = cmds.ls(selection=True) # sel_nodesはリスト型で返ってくるので最初のノードを指定する。 node = sel_nodes[0] # node_nameに含まれる値を.split(分割したい値)で切り分ける。 node_names = node.split("_") print (node_names) # node_namesに含まれる値の2番目を表示する print (node_names[2])
選択しているノードの名前がアンダースコア(_)で区切られていた場合に、切り分けた状態のリストが返ってきているのを確認できましたか?これでより精度の高い抽出を行うことができます。
この機能を使って要素を区切る場合はきちんとアンダースコア(_)の数を統一しておきましょう。数が違うと取得したい文字列が取得できず、悲劇を招くことになりますので注意しましょう。
今回は、アンダースコア(_)で要素を切り分けためにsplitメソッドを使用しました。このメソッドはファイルパスの文字列を切り分ける際にも使用できたりと、何かと便利なので覚えておきましょう。
下の例題を見て頂くと分かりやすいですが、きちんと細かくするなら、sholderとTwistの間にも入れた方が要素として細かくなりますよね?
もちろんアンダースコア(_)を増やすことで精度は上がりますが、アンダースコア(_)の数を統一しなければならない都合上、何の意味も含んでいないものにも要素名を付けることになってしまいます。
筆者としては作業時の手間を考慮して含まないようにしており、名前も余分に増えてしまうことを避ける意図もあります。
どこまで短くするかは好みではありますが今後ネームスペースが付く可能性もあり、名前は長くなりがちです。アウトライナーの視認性を高めためにも、筆者は要素をできるだけ排除しています。
Left、Rightなどは必ず埋め込む情報になり、長いのでLとRなど1文字に省略してしまいましょう。
名前のパターンが決まっている要素は3文字にしてあげると名前の重複を防ぎつつ、短くできると思います。
数字は2桁運用で十分だと思います。3桁になるような階層構造を持つものは切り分けて階層を変える工夫を取った方が良いと思います。
階層構造以外で競合する要素は名前の要素にA、B、C・・・と英単語を付けることがあります。26個以上要素がある場合はAAなどで管理するか、もう少し要素を切り分けましょう。
ネーミングルールを決める際にこの要素の順番で無ければならないルールはありません。統一したルールがあり、統一化されているのであれば自由にして良い要素です。
筆者が運用する場合は以下のように切り分けています。名前の最初の方に部位名などの重要な要素を配置し、末尾になるにつれ連番など重要度の低い要素がくる構成としています。
数字を最後にしておくとMaya内で複製した際に自動で数字が変わるので運用しやすいかと思います。
以上ですっきりとしたネーミングルールになると思います。ここからは運用しやすい形式に加工してみてください。
名前の要素に含みたくはないがオブジェクトにどうしても情報を埋め込みたい場合に使える手法を3つ紹介します。それぞれにメリット、デメリットがあるのでケースに応じて検討してみてください。
どの手法で運用する場合もスクリプト化し、ヒューマンエラーを防ぐようにしておくことを推奨します。
また、アトリビュートで管理する場合や、接続で管理する際は情報が不用意に変わらないよう値をロックすることを心がけましょう。
以下の内容を意識すると良いでしょう。
文字列型のアトリビュートを追加して、直接情報を管理する。
例)身長や種族、性別など1つのノードに含まれていれば十分な要素。
setsを使ってアトリビュートではない要素で管理。
例)一括で選択したいオブジェクトをまとめる。
ネットワークノードを使って、情報を管理する。
例)リグの分類や関連性があるオブジェクトを紐づけたい場合。
文字列型のアトリビュートで管理するメリット、デメリットを紹介します。
スクリプトで埋め込む場合は以下のようになります。
addAttr
# -*- coding: utf-8 -*- import maya.cmds as cmds sel_nodes = cmds.ls(selection=True) node = sel_nodes[0] # アトリビュートを追加する cmds.addAttr(node,longName="monsterType",dt="string")
アトリビュートが存在しているかどうかの確認。
hasAttr
# -*- coding: utf-8 -*- import maya.cmds as cmds sel_nodes = cmds.ls(selection=True) node = sel_nodes[0] node_attrs = cmds.listAttr(node) #所持しているか調べたいアトリビュート名 attr_name = "monsterType" #ノードが所持しているアトリビュート名に該当のアトリビュート(今回はmonsterType)が存在するか調べる if attr_name in node_attrs: print (f"{attr_name}は存在します") else: print (f"{attr_name}は存在しません")
埋め込こんだ値を取得する場合。
getAttr
# -*- coding: utf-8 -*- import maya.cmds as cmds sel_nodes = cmds.ls(selection=True) node = sel_nodes[0] node_attrs = cmds.listAttr(node) #所持しているか調べたいアトリビュート名 attr_name = "monsterType" #ノードが所持しているアトリビュート名に該当のアトリビュート(今回はmonsterType)が存在するか調べる if attr_name in node_attrs: #アトリビュートが存在していたら値を取得する monster_type_state = cmds.getAttr(f"{node}.{attr_name}") print(monster_type_state)
この要素を使えば名前に対してアトリビュートを設定したり、値を比較して処理したり、アトリビュートを所持しているか確認する処理が行えますね。
ここではsetsを使って情報を管理するメリット、デメリットを紹介しています。
セットのメンバーを取得するコマンド例です。
getSetsMember
# -*- coding: utf-8 -*- import maya.cmds as cmds sel_nodes = cmds.ls(selection=True) node = sel_nodes[0] # setsからメンバーを取得し、名前のリストを返す。 member = cmds.sets(node,q=True) print (member)
この章ではネットワークノードを使って複数の要素を管理するメリット、デメリットを載せています。
コマンドでノードに対してコネクションされているノードを抽出する例です。
listConnections
# -*- coding: utf-8 -*- import maya.cmds as cmds sel_nodes = cmds.ls(selection=True) node = sel_nodes[0] # 選択しているノードにコネクションされているノード全てを抽出する。 inputs = cmds.listConnections(node, source=True, destination=False) or [] print(inputs)
今回紹介した内容は以下の要素になります。これらが守られていれば素敵なネーミングルールを付けることができるようになっていると思います。
また、今回はスクリプトの書き方を解説しました。コマンドを知っていけばどんどん自動化できることが増えていくので反復作業の苦痛から解放されることができます。
自分でもスクリプトが作成できるようになれば、より効率良く物事が進められるので、ぜひ試してみてください。
上記のリンク先ページ内にある テクニカルドキュメント > Pythonコマンド からcmdsコマンドを調べることができます。
執筆者:広森
映像業界を経てモノリスソフトへ入社。 以来、テクニカルアーティストとして主にリギング・セットアップ関連の業務を担当。 好きな飲み物は赤ワイン。