【ArtistでもMELが書きたい】Maya MEL入門(8)【ls】

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

# 環境
Autodesk Maya 2022.2

# 前回のあらすじと今回へのつなぎ
[前回](https://qiita.com/Hum9183/items/acd66f3275f87f7295d9)は変数を使いました。変数を使えるようになった今では、MELで実行できる幅が格段に広がったのではないでしょうか？
さて、今回は`ls`コマンドを学んでいきます。変数をふんだんに活用する`ls`コマンドはMELのなかでもかなり重要なコマンドになります。一つずつしっかりと確認していきましょう。

# lsコマンドの概要
`ls`というのは**List**の略です。
リストアップとよく言いますよね。
`ls`コマンドの主な挙動としては、「フラグで指定したものを**配列型**で返す」です。

# 配列型とはなんぞや？
int, float, stringに続く新しい型になります。
しかし今までの型とは少し毛色が違います。
まずは初期化する場合の文法を見ていきましょう。
```
型名 $変数名[要素数] = {要素0, 要素1, 要素2、...}
```
実際のMELだと下記です。
```
string $cubeNames[3] = {"CubeA", "CubeB", "CubeC"};
```

配列型は、実直に言ってしまえば、**ある型の値を複数個保持できる型**です。
上記の`変数cubeNames`はstring型の値を三つ保持しています。
保持できる値のことを**要素**といい、その数を**要素数**といいます。

# 配列型を使ってみる
実際に使ってみるのが一番理解がはかどります。
配列型を活用して、CubeA、CubeB、CubeCを生成するMELを書いてみます。
```
string $cubeNames[3] = {"CubeA", "CubeB", "CubeC"};
polyCube -name $cubeNames[0];
polyCube -name $cubeNames[1];
polyCube -name $cubeNames[2];
```
まずは実行して確認しましょう。
![MEL_learning01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/1386a2cd-f08e-bf3d-3ef9-36daf11638b5.png)
![MEL_learning02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/49cd36b2-c00b-0439-c055-2112c6afe723.png)
うまくいきました。
より詳細な文法を見ていきましょう。

# 配列型の文法
まず、初期化時の文法ですが、
```
string $cubeNames[3] = {"CubeA", "CubeB", "CubeC"};
```
上記は当然OKですが、実は、
```
string $cubeNames[] = {"CubeA", "CubeB", "CubeC"};
```
こちらの記法でもOKです。変数宣言の要素数を省略できるんですね。

初期化が終わったら、次は使用法です。

配列は一つの変数の中に**複数個の値を保持**しています。なので実際に`-name`フラグ用の値を適用する際には、`変数cubeNames`の**どの値を使うのか**を指定する必要があります。
```
polyCube -name $cubeNames[0];
```
指定自体はシンプルで、変数名の後ろに`[]`をつけ、`[]`内に使いたい要素の順番を書きます。
この要素の順番のことを**添字**といいます。
上記だと`変数names`中の**添字が0の要素**を使う、ということになります。

一つ注意なのはこの順番は、1からのスタートではなく、**0からのスタート**だということです。
要素数が3の配列は、添字は、**0, 1, 2**となります。
1, 2, 3ではないので、十分に気をつけてください。

# 間話 printコマンド
突然ですが、「変数の中身は今はこれがこうで...」と考えるのって少し大変じゃありませんか？
変数の中身を確認することができれば多少は楽ができそうです。
そして、便利なことにそんなコマンドが存在します。
```
print()
```
`print`コマンドといいます。
使い方としては、`print`コマンドの後ろの`()`に変数を入れるだけです。
```
float $pi = 3.14;
print($pi);
```
早速実行してみましょう。
![MEL_learning03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/6f70c458-c0d3-a446-4670-eceb2aeaea51.png)
`print`の結果はログで確認ができます。
![MEL_learning04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/95e0c76e-8095-c46b-8213-4b907a66c970.png)
**3.14**と表示されています。
このように変数の中身を見たりして、プログラムが正常かを確認することを**デバッグ**といいます。
とりわけ、`print`でデバッグすることを**printデバッグ**と呼んだりします。

「この書き方で合ってるはずだけど、何故か上手く動かない」というときは、変数の中身をprintで確認してみましょう。
出力されたログをもとに、ミスが見つかるかも知れません。

https://knowledge.autodesk.com/ja/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2020/JPN/Maya-Scripting/files/GUID-3EA92249-0247-4538-82D3-AC6F37B76AB7-htm.html

# lsコマンドを使ってみる
便利な`print`を学んだところで、本題の`ls`コマンドを実際に使ってみましょう。
今回は選択したオブジェクトたちを取得するMELを書いてみます。
```
string $selectionObjs[] = `ls -selection`;
print($selectionObjs);
```
`ls`コマンドは「フラグで指定したものを配列型で返す」コマンドでしたね。
[MELコマンドリファレンス](https://help.autodesk.com/view/MAYAUL/2022/JPN/index.html?contextId=COMMANDS-INDEX)の`ls`コマンドのフラグから、-selectionフラグの効果を見てみましょう。
![MEL_learning05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/1993acbb-cbe7-1807-7f7a-6778fb1a46ff.png)
選択したオブジェクトがリストアップできそうですね。

実行前に、適当なオブジェクトをシーンに並べましょう。
あとは、それらを選択した状態にしておきます
![MEL_learning06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/ac13b5ba-c4dd-49bd-7fcd-d21c554226e0.png)
![MEL_learning07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/7a47b5cf-0099-25d6-3ef4-286375008adb.png)
それでは実行していきましょう。
![MEL_learning08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/08180b5f-0e3a-0e8f-af01-f1eda1d95167.png)
![MEL_learning09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/5da5edb5-47cc-526c-9e2d-176adbdc596c.png)
無事に四つのオブジェクト名がログに表示されています。

# MEL実践
せっかく`ls`コマンドを使用できるようになったので、前回作った、
```
float $cubeATranslateX = `getAttr "CubeA.translateX"`;
float $cubeATranslateY = `getAttr "CubeA.translateY"`;
float $cubeATranslateZ = `getAttr "CubeA.translateZ"`;

setAttr "CubeB.translateX" $cubeATranslateX;
setAttr "CubeB.translateY" $cubeATranslateY;
setAttr "CubeB.translateZ" $cubeATranslateZ;
```
このMELをパワーアップしてみましょう。

「パワーアップもなにも、このままでもいいじゃん」を思う方がいるかもしれません。ですがよく考えてみてください。このMELはオブジェクト名が「CubeA」と「CubeB」でしか正常に動作しないのです。
このままでは汎用性がありませんよね。

しかし`ls`を使えば**現在選択しているオブジェクト**を取得できるので、少なくとも「CubeA」と「CubeB」でしか動かないなんてことは回避できそうです。

それではまず、MEL作りの計画から始めましょう。
動作要件としては、「**一番目に選択したオブジェクト**の位置を**二番目に選択したオブジェクト**の位置に合わせる」でやってみます。
今回はわかりやすさ重視のため、シーン上にはCubeとConeを用意します。
![MEL_learning10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/38780958-a839-2b12-f13e-9510fc8b49af.png)
一番目にCube、二番目にConeを選択し、MELを実行すると、Cubeの位置がConeの位置と同じになればOKということにします。

まずは、移動するオブジェクト(一番目に選択したオブジェクト)と、移動先のオブジェクト(二番目に選択したオブジェクト)を取得してみます。
`ls`コマンドにおいて、選択した順番と配列に格納される順番の関係性はわかりませんが、一旦**選択した順番でオブジェクトが格納されている**想定でMELを書いてみましょう。
`print`も活用して、配列の中身を見てみます。
```
string $selectionObjs[] = `ls -selection`;
string $firstSelectionObj = $selectionObjs[0];
string $secondSelectionObj = $selectionObjs[1];
print($firstSelectionObj);
print($secondSelectionObj);
```
![MEL_learning11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/41a11b89-efab-9cb6-787f-26e38105e647.png)
![MEL_learning12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/0175c5a6-27ac-3b3d-9d3f-87627e76c341.png)
```
pCube1pCone1
```
ログを見ると、CubeとConeの名前は取れているようですが、改行されていません。
実は、`print`では意図的に改行しない限り改行はされません。
改行を表す`\n`を間に挟んで、再度実行してみましょう。
![MEL_learning13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/511165de-f9d3-03c4-059c-35a729e82ec7.png)
![MEL_learning14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/bec63537-8ac1-6d60-cefd-fa330ed38102.png)
うまくいきましたね。

改行に気を取られていましたが、**選択した順番で配列に格納されている**予想は当たっていましたね。このまま作業を続けていきましょう。

オブジェクトが取得できているので、後は**位置を取得・設定する処理**と合体するだけで完成になりますね。
しかしここで一つ問題があります。`getAttr`コマンドに「"オブジェクト名.アトリビュート名"」という形式の文字列を**渡す**必要がありますが、オブジェクト名は現在変数で用意しています。どうすればいいのでしょうか？
```
float $cubeATranslateX = `getAttr $secondSelectionObj".translateX"`;
```
無理あり当て込んでみてもエラーが出るようです。
![MEL_learning15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/093b7272-c198-c850-4f5e-c80336afdeca.png)

# 間話 文字列の結合
実は文字列は`+`を使うことで結合することができます。
```
string $firstName = "my";
string $secondName = "Cube";
string $combinedName = $firstName + $secondName;
polyCube -name $combinedName;
```
![MEL_learning16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/62670697-8b62-91ea-e986-4a59f2aedd81.png)
![MEL_learning17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/7b9f1518-ac14-130a-fbc0-a96b0c847e09.png)
ちなみに`()`で囲いさえすれば、コマンド内でも結合することができます。
```
string $firstName = "my";
string $secondName = "Cube";
polyCube -name ($firstName + $secondName);
```

# MEL実践(2)
文字列の結合法がわかれば後は書くのみです。
```
string $selectionObjs[] = `ls -selection`;
string $firstSelectionObj = $selectionObjs[0];
string $secondSelectionObj = $selectionObjs[1];

float $secondTranslateX = `getAttr ($secondSelectionObj + ".translateX")`;
float $secondTranslateY = `getAttr ($secondSelectionObj + ".translateY")`;
float $secondTranslateZ = `getAttr ($secondSelectionObj + ".translateZ")`;

setAttr ($firstSelectionObj + ".translateX") $secondTranslateX;
setAttr ($firstSelectionObj + ".translateY") $secondTranslateY;
setAttr ($firstSelectionObj + ".translateZ") $secondTranslateZ;
```
一番目にCubeを、二番目にConeを選択して、
![MEL_learning10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/506803dc-17bc-e2ff-a4be-8403a04d4119.png)
実行してみましょう。
![MEL_learning19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/74f90157-1384-0616-21c9-7ce138f4c2d9.png)
![MEL_learning18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/3a33bc22-dbea-55d8-c5cc-08dfd84f70d3.png)
やりました！大成功です！
これはかなり実践的なMELが書けたのではないでしょうか？

# おわりに
いかがでしたか？配列型にlsコマンドにと大ボリュームでしたね。
新しい要素は読むだけ理解するだけ、ではなく、実際にスクリプトを書いて、体験して覚えるのが上達の近道です。
実際にスクリプトを書くと、本記事では説明していない文法やエラーに当たってしまうこともあるかと思います。そんなときでも一つずつ落ち着いて原因を探し出し、時にはインターネットの情報も駆使して、解決を目指してみてください。
そしてうまくMELが動いたときには存分に達成感に浸ってください！

次回はこちら→[【ArtistでもMELが書きたい】Maya MEL入門(9)](https://qiita.com/Hum9183/items/be7d648eb760f3aab2ca)

# 参考
https://knowledge.autodesk.com/ja/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2023/JPN/Maya-Scripting/files/GUID-09B231DC-4D86-4E0D-BFCB-9E7E6F3F404C-htm.html