【ArtistでもMELが書きたい】Maya MEL入門(11)【if文】

# はじめに
この記事は、3DCG業界で働く**Artist向けのMEL入門記事**です。
普段の業務を効率化したいけど、MELの書き方が分からない、調べたけどイマイチ分からなかった、そんな方向けに書きました。
本記事は「if文を使う」をゴールとします。

# 環境
Autodesk Maya 2022.2

# 前回のあらすじと今回へのつなぎ
[前回](https://qiita.com/Hum9183/items/76fd522ebb84f2f3bca3)はfor文を学びました。繰り返し処理を扱えるようになったおかげで、一括系の処理が行いやすくなりましたね。
さて今回は前回作った「**シーン内のすべてのジョイントの名前を、prefixが"jnt_"の名前にリネームするMEL**」を少し改良していきます。
もう改良するところは無いよ、と思うかもしれませんが、実は結構大事なところが抜けていたりします。

# 大事なところって？
ずばり**複数回の使用を想定していない**ところです。
具体的に見ていきましょう。
まずは前回のスクリプトの確認からです。
```
string $allJoints[] = `ls -type "joint"`;
string $jnt = "jnt_";
for ($joint in $allJoints) {
    rename $joint ($jnt + $joint);
}
```
試しに動作確認をしましょう。
![MEL_learning01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/a47df1ff-df03-722e-4fac-19369f3e23c4.png)
実行すると、
![MEL_learning02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/eee5d696-2498-abae-985a-87bf4771b141.png)
正常に動きましたね。

さて、ここで一つ実際の仕事を想像してみましょう。

>骨を設定し終わって上長に提出したあなたは、翌日にフィードバックを受けます。
>「ここに骨を追加してほしい」

想像に難くないのではないでしょうか？

この際、どういう修正を行うかは重要ではありません。
重要なのは、骨に変更が入るため再度MELを実行する必要があるということです。

このような修正例のパターンを想定してみます。
![MEL_learning03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/5eb32365-89a2-3eb8-9639-0b57143a35f3.png)
新しく追加した骨にも「"jnt_"」を追加したいので再度スクリプトを実行します。
![MEL_learning04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/62acbc1a-6cd9-f93f-e959-6385d582c051.png)
新しく追加した骨は正常にリネームされているようですが、
...あれ？今までの骨にも「jnt_」が追加されています。
「jnt_jnt_joint1」等の名前になっていますね。

...少々困ってしまいました。
現状のMELは**すべての骨**に「jnt_」を付ける処理になっているので、すでに「jnt_」が付いている骨もリネームしてしまうんですね。
どうも今知っている文法やコマンドだけでは修正が難しそうです。

実はこのような場合に対応するために、MELには**if文**というものが存在します。

# if文とは
簡単に言うと条件で処理を分岐することができる文です。

文法は下記です。
```
if (真偽値) {
    処理;
    ...(つづく)
}
```
for文と比べるとシンプルですね。
`{}`内はインデントを付けてください。

さて、まずは真偽値とはなにかを見ていきます。

# 真偽値とは
真偽値とは、「真」か「偽」かを表現する値です。
MEL上では真を**true**、偽を**false**で表現します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/82c0f6d4-33db-a4b6-7ad4-a490d323f9c8.png)
スクリプトエディタでもハイライトが効くことからMELに内蔵されていることがわかります。
ちなみに英語では**boolean**と呼びます。もしかしたらMELコマンドリファレンス内で見たことがある人もいるかも知れません。あれはtrueもしくはfalseという意味です。
(ちなみに内部的には、trueは1、falseは0として扱われます。printすると1と0がそれぞれ出力されます)

# 真偽値とif文を使ってみる
早速使ってみましょう。
```
if (true) {
    print("Hello!!");
}
```
上記を実行すると、
![MEL_learning06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/e973f709-e4d4-e86d-d9bf-7895cbf35bf6.png)
「Hello!!」が出力されました。

真偽値の部分をfalseにするとどうなるでしょうか？
```
if (false) {
    print("Hello!!");
}
```
![MEL_learning07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/6b3933ec-ebe7-77f0-e988-3fc9c42d93e5.png)
「Hello!!」は出力されませんでしたね。

つまりif文は**真偽値がtrueなら`{}`内の処理を実行し、falseなら実行しない**ということになります。

# 真偽値を実践的に
突然ですが[第7回](https://qiita.com/Hum9183/items/acd66f3275f87f7295d9#%E5%BC%8F%E3%81%A8%E3%81%AF%E3%81%AA%E3%82%93%E3%81%9E%E3%82%84)で**式**というものを学んだことを覚えていますか？
簡単に振り返ると

>それ自体が値を返すもの

です。
例としては「\`getAttr "CubeA.translateX"\`」「1 + 2」「10」なんかが挙げられていましたね。

さて、実はif文は真偽値部分に「真偽値を返す**式**」を書いても正常に動作します。
「true」はtrueを返す式なので、当然と言えば当然ですね。

ここで一つ真偽値を返す式を紹介します。
```
値 == 値
値 != 値
```
実際のMELだと、
```
if (3 == 3) {
    print("3は3です");
}
if (3 != 4) {
    print("3は4ではありありません");
}
```
上記のようなかたちになります。
`==`は等しければtrueを返します。数学と違って`=`が2つ必要な点に注意してください。
`!=`は等しくなければtrueを返します。ちょっと分かりづらいですが単純に**条件を変転している**と考えてください。

ちなみに文字列でも使用する事ができます。
```
if ("hum" == "hum") {
    print("humとhumは同じ文字列です");
}
```
少し実践的に書いてみましょう。
試しに、**選択したものの名前が「"jnt_head"」かどうか**を確認してみます。
「"jnt_head"」ならprintを行い、そうでないならなにもしません。
```
string $sels[] = `ls -sl`;
if ($sels[0] == "jnt_head") {
    print("jnt_headを選択しています");
}
```
![MEL_learning08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/80b0f476-ad5f-f945-90a6-ab6b0bf2476c.png)
![MEL_learning09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/3bf276ac-b2b9-37cc-d2a0-f6159e601b9a.png)
jnt_headを選択した場合は正常にprintされます。

jnt_neckを選択した場合はどうでしょうか？
![MEL_learning10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/b713ee4c-9f82-6f74-7f3d-ca1a5c94d881.png)
![MEL_learning11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/045cf331-775d-4931-e36e-86568723d418.png)
なにもprintされませんね。if文が正常に機能しているようです。

# 本題に戻り
さて、if文の使い方がわかったところで、本題の解決法を探っていきましょう。

問題点としては、**すでにリネームされている骨も再度リネームしてしまう**ことにあります。
ということは**すでにリネームされているかどうか**を真偽値として取得し、if文と組み合わせればうまく行きそうです。
骨の名前に「"jnt_"」が含まれているかどうかで判断できそうな気がしますが一体どう行えばよいでしょうか？

# 文字列操作
MELには文字列を操作するコマンドがたくさん存在します。
今回は`tokenize`コマンドが有効そうなので使い方を見ていきましょう。
使用例は下記です。
```
string $longWords = "apple_banana_orange";
string $fruits[];
tokenize $longWords "_" $fruits;
print($fruits);
```
![MEL_learning18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/d7291cb8-52d9-4643-dbf6-e3f43ddff71e.png)
実行している内容としては、「"apple_banana_orange"」という文字列を「"_"」で区切り、
バラバラになった「apple」「banana」「orange」を$fruitsに入れているという感じになります。
コマンドが変数に値を代入してくれるのは初ですね。

文法は下記です。
```
tokenize "分割したい文字列" "区切り文字列" 分割結果を入れる変数;
```
3つ目の「分割結果を入れる変数」というのが少し特殊で、事前に変数の宣言をしておく必要があります。
コマンドが実行されたら変数の中に結果を入れてくれます。
(ちなみに`tokenize`コマンドは値を返すこともできます。返す値は**分割できた文字列の数**です(今回であれば3))

# リネーム処理のリベンジ
さて、文字列の操作を学んだので、**骨の名前に「"jnt_"」が含まれているかどうか**を真偽値で取得することができそうです。
本命のMELに取り掛かる前に、肩慣らしとして**選択した骨の名前のprefixが「jnt_」かどうかを判別する処理**を書いてみます。
```
string $sels[] = `ls -sl`;
string $splitNames[];
tokenize $sels[0] "_" $splitNames;
string $prefix = $splitNames[0];

if ($prefix == "jnt") {
    print("選択したオブジェクトのprefixはjntです");
}
```
実行してみます。
![MEL_learning12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/573dc7b8-6647-b99d-e290-0f9df0c54fd9.png)
![MEL_learning13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/ad5e4626-59e0-6d81-3123-95c291f33276.png)
問題なさそうですね。

さて次は本題です。前回のMELにこのリネーム処理を組み込んでみましょう。
for文とif文を組み合わせることになるので少し複雑ですが、頑張ってついてきてください。
```
string $allJoints[] = `ls -type "joint"`;
string $jnt = "jnt_";

for ($joint in $allJoints) {
    string $splitNames[];
    tokenize $joint "_" $splitNames;
    string $prefix = $splitNames[0];
    
    if ($prefix != "jnt") {
        rename $joint ($jnt + $joint);
    }
}
```
合体するとこんな感じになります。
結構複雑になってきましたね。

まずは動作を見ていきましょう。
![MEL_learning14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/d4f2c2fe-e8f2-df63-599c-8471b234d15a.png)
![MEL_learning15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/b30428d9-e96f-5077-7600-f3e361fdcf12.png)
![MEL_learning16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/68427fa3-c249-7f23-e940-eefe128e64f7.png)
うまくいきましたね！

処理が複雑なので、コメントで挙動の補足を行いたいと思います。
```
// シーン内の全てのjointを取得する
string $allJoints[] = `ls -type "joint"`;

// リネームする際の文字列を用意
string $jnt = "jnt_";

// $allJointsでforループを回す
for ($joint in $allJoints) {
    // tokenize結果の受け取り用変数を定義
    string $splitNames[];
    // $jointを「_」で分割する
    tokenize $joint "_" $splitNames;
    
    // $splitNamesの1つ目の要素はprefix
    string $prefix = $splitNames[0];
    
    // prefixが「jnt」ではないなら
    if ($prefix != "jnt") {
        // リネームする
        rename $joint ($jnt + $joint);
    }
}
```
![MEL_learning17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/b801072f-e3e2-fe55-1238-2074380240aa.png)
一つずつ処理を読み、理解を深めていきましょう。
処理の途中で変数の中身をprintしてみるとわかりやすいかもしれません。

# おわりに
いかがでしたか？
今回はfor文とif文、さらに文字列操作までを一度に行いました。
もしかしたら、今までの記事と比べるとちょっと難しかったかもしれません。ですがこれ以上難しいものはもうほとんど出てきません。
あるにはあるのですが、正直MELは**for文**、**if文**、**コマンド**だけで大体の処理を書くことができます。言わば三種の神器です。

私はMELを書き始めのころはイマイチ理解しておらず、ただ文法に当てはめてを書いているだけでした。
しかし、書き続けていると不思議なもので、手になじむ瞬間があります。
みなさんもぜびMELを書き続けてもらえたらうれしいです。

次回→準備中