【ArtistでもMELが書きたい】Maya MEL入門(7)【変数】

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

# 環境
Autodesk Maya 2022.2

# 前回のあらすじと今回へのつなぎ
[前回](https://qiita.com/Hum9183/items/71750017c0c1bf46c0ee)は`setAttr`と`getAttr`を使いました。
オブジェクトのアトリビュートを設定・取得できるようになったため、MELでできることとしてはかなりの範囲になります。
ここで一つ`setAttr`と`getAttr`を生かして「二つのオブジェクトの位置を揃えるMEL」を書いてみましょう。

# どうつくる？
仮に、CubeAとCubeBという二つのオブジェクトがあると仮定しましょう。
![MEL_learning01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/c726f7e5-0a78-c236-3438-8b760e6ad3b1.png)
やりたいこととしては、「**CubeBの位置をCubeAの位置に移動させる**」としておきます。

アトリビュートを取得するコマンドと設定するコマンドはすでに知っているわけですから、
CubeAの位置を`getAttr`で取得して、その値をCubeBに`setAttr`で設定すれば実現できそうです。

実際のMELで書いてみましょう。
```
getAttr "CubeA.translateX";
getAttr "CubeA.translateY";
getAttr "CubeA.translateZ";

setAttr "CubeB.translateX" ?;
setAttr "CubeB.translateY" ?;
setAttr "CubeB.translateZ" ?;
```
あれ、`setAttr`の一番後ろには「設定する値」が入るはずですが、
今回のMELを実現するためには、なにを入れてもよいかわかりません。

**`getAttr`で取得した値**を入れたいところですが、どうすればいいのでしょうか？そんなコマンドがあったりするのでしょうか。

# 変数
コマンドではないですが、MELの機能として「**変数**」という機能があります。

変数とは読んで字のごとく「変わる数」です。
数というと数字をイメージするかも知れませんが、ここでは数字というよりは**値**というイメージのほうが近いです。

そしてMELの機能としての変数は、**値を保存する箱**のような機能を持ちます。

文法としては以下になります。
```
型名 $変数名
```
実際の書き方でいうとこのような感じです。
```
float $cubeATranslateX
```
型名は後述しますが、決まったものの中から選びます。コマンドと同様ですね。
変数名は、好きな名前を設定することができます。

「CubeAの移動X」を変数に入れるMELはこうなります。
```
float $cubeATranslateX = `getAttr "CubeA.translateX"`;
```
なにやら新要素がたくさんありますが、ひとまず実行してみましょう。
シーン上には忘れずにCubeAを配置してください。
![MEL_learning02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/6a8318ef-6160-d86f-9265-85f8a4a6ff64.png)
できました、が、
特になんの変哲もありません。
ログには、
```
// 結果: 0 // 
```
と表示されていますが、これは変数を書かないときでも同様です。
ですが、今回の実行では変数`cubeATranslateX`に0が**入りました**。

いくら変数の中に0が入りましたと言われても、目で見ないとわからないですよね。
それでは下記のMELで確認してみましょう。
```
float $cubeATranslateX = `getAttr "CubeA.translateX"`;
setAttr "CubeB.translateX" $cubeATranslateX;
```
シーン上には原点にCubeAを、X座標を適当に移動させたCubeBを置いてください。

![MEL_learning01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/c23a94e2-ae5c-e58a-ea5a-4d5f68c42be5.png)
![MEL_learning03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/e2c4d281-388c-afd5-3995-1c8ffc95cfc2.png)

CubeBが原点に移動しましたね！
色々と分からない文法はたくさんありますが、`変数cubeATranslateX`に**値が入っていた**のは間違いなさそうです。

# 細かい文法を見ていく
文法の説明をすっ飛ばしてしまいましたが、ひとつずつ確認していきましょう。
要素としては三つあり、それぞれ「代入」「式」「型」といいます。

# 代入とはなんぞや？
代入とは**変数に値を入れること**です。
```
float $cubeATranslateX = `getAttr "CubeA.translateX"`;
```
上記のMELは`変数cubeATranslateX`に`CubeAの移動Xの値`を**代入**するという処理です。
空っぽの箱に物が入れられるイメージですね。

変数に代入する際は、数学のように`=(イコール)`を使いますが、右辺と左辺が逆になります。
代入したいものを右辺に置き、代入されるものを左辺に置きます。
間違えやすいので要注意です。

# 式とはなんぞや？
それ自体が**値を返すもの**です。
別の言い方をすると「変数に代入できるもの」ですね。
```
float $cubeATranslateX = `getAttr "CubeA.translateX"`;
```
上記のMELでは「\`getAttr "CubeA.translateX"\`」が式になります。
**返す**というとピンとこないかも知れませんが、イメージとしては`getAttr`コマンドがCubeAの移動Xを値を取ってきてくれて、その値を**渡してくれる**、というような感じです。プログラミングでは、このようなことを**値を返す**といいます。
渡してくれるので変数で受け取りが可能というわけです。

よく見ると見慣れない「\`」がコマンドの両端についていますね。これもMELの文法の一つです。
コマンドを式として使用する際には「`(バッククォーテーション)」で両端を囲う必要があります。
これはルールだとしか言いようがないため、忘れないように注意しましょう。

ちなみにコマンドを「\`」で囲ったものだけが式ではありません。返すことができればなんでも式です。例えば、
```
1 + 2
```
これは式です。

え、数学とそっくりじゃんと思いましたか？そうです。そっくりなのです。
`+`を普通に使えることにも驚きかと思いますが、それは今回は置いておきます(これらは演算子と呼ばれるものです。また別の機会に解説します)

`1 + 2`では3を返すことができますよね？ですので`1 + 2`は式なのです。
```
float $result = 1 + 2;
```
上記では`変数result`に**3が代入される**ということになります。

なんなら、`+`等を使わなくても式としては成立します。
```
float $result = 10;
```



# 間話 変数に代入できるもの
冒頭で、変数はイメージとしては数字というよりも値に近いという話をしました。
数字以外の値とは具体的にはなにがあるでしょうか？

実はみなさんはすでに使ったことがあります。それは**文字**です。
(プログラミングではもっぱら**文字列**と呼ぶことが多いため、以下文字列と呼称します)
**文字列**をMELで表現する際には`"(ダブルクォーテーション)`で両端を囲う必要があります。
```
float $text = "Hello";
```
こんな感じですね。

しかし、もう一点ルールがあるのです。
ためしに現状のスクリプトを実行してみましょう。
![MEL_learning04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/c4554366-8d8a-872d-b131-9b3db0c98bfc.png)
![MEL_learning05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/7e65ab7d-21a7-4437-2c11-7d44cc3f7c30.png)
エラーは出ませんでしたが、警告されてしましました。
```
// 警告: Line 1.22 : 文字列 "Hello" を 0 の float 値に変換します。 // 
```
```
// 結果: 0 // 
```

なぜかはわかりませんが`変数text`の中身が0に変換されてしまいました。
実はこれは変数の**型**が合っていないからなのです。

# 型とはなんぞや？
変数の文法をもう一度おさらいしましょう。
```
型名 $変数名
```
```
float $cubeATranslateX
```
上記でいう「float」というのがまさに**型**になります。
この型には種類があり、変数に入る**値の種類を決定する**ものになります。
今回は重要なものを三つだけご紹介します。

# float型
**float型**の変数に入る値は、**浮動小数点**です。
ここでは単に小数という認識で問題ありません。
```
float &pi = 3.14;
```

# int型
**int型**の変数に入る値は、**整数**です。
```
int &cubeCount = 15;
```
小数を使用しない場合は、こちらを使うことになります。

floatでも代用可能ですが、整数であるべきときは必ずintを使いましょう。

たとえば、シーン上にあるCubeの数を変数に代入するときのことを考えます。
float型を使っている場合は、誤って**1.5**と入れてしまうこともできてしまいます。
さすがに問題ですよね。Cubeが1.5個あるといわれても、その後の処理を正しく実行できません。

# string型
**string型**の変数に入る値は、**文字列**です。
```
string $cubeName = "CubeA";
```
先程も少し説明したように、文字列をMELで表現する際には`"`で両端を囲う必要があります。忘れやすいので気をつけましょう。

string型を学んだところで、
```
float $text = "Hello";
```
先程、警告を出されたMELはどこを直せば良いでしょうか？

```
string $text = "Hello";
```
`変数text`の型がfloatになってしまっていたので、**string型に直せば**`文字列"Hello"`を代入することができますね。

# 二つのオブジェクトの位置を揃えるMEL リベンジ
さて変数について学んだところで、「二つのオブジェクトの位置を揃えるMEL」を書く準備が整いました。
実現したいこととしては「**CubeBの位置をCubeAの位置に移動させる**」です。
早速書き進めていきましょう。

まずはCubeAの位置を`getAttr`で取得しましょう。取得した値は変数にとっておきたいので、以下のようになります(コマンドを「`」で囲うのを忘れずに)
```
float $cubeATranslateX = `getAttr "CubeA.translateX"`;
float $cubeATranslateY = `getAttr "CubeA.translateY"`;
float $cubeATranslateZ = `getAttr "CubeA.translateZ"`;
```
これでそれぞれの変数に、3軸の座標が代入できました。

続いて、その値をCubeBに`setAttr`で設定していきましょう。
```
setAttr "CubeB.translateX" $cubeATranslateX;
setAttr "CubeB.translateY" $cubeATranslateY;
setAttr "CubeB.translateZ" $cubeATranslateZ;
```
あとは上記の二つを合体させるだけです。

```
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_learning08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/727acbb4-7044-29e4-40fc-b1401c513d3e.png)
![MEL_learning06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/f207004b-43ff-39d7-ba5b-d94eeffcb773.png)
![MEL_learning07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/fab1a28d-95ed-4f99-3458-d6dde730b6e0.png)
やりました！大成功です！

# 型の記述
もしかしたら`setAttr`で「$cubeATranslateX」と書いてあるところで疑問に思った方がいるかもしれません。「型は書かなくてもいいの？」と。
実は型を記述するのは最初の一回のみというルールがあります。

最初に使う際に「float」と記述していれば、二回目以降は「float」と書かなくても良いのです(というより書いてはいけません)

# 【もっと知りたい】変数の宣言と初期化
今回は、
```
string $text = "Hello";
```
というようなかたちで変数を記述しましたが、この記述は**初期化**といいます。
初期化？言葉からは意味が想像し辛いですよね。

初期化の意味を知るには、まず変数の**宣言**を知る必要があります。
まず変数というのは、下記のような記述をすることができます。
```
string $text;
$text = "Hello";
```
上の行が**宣言**で、下の行が**代入**です。

宣言は文字通り、変数を使いますよという**宣言**です。変数の型と変数の名前を宣言し、以降の行でその変数が使用可能になります。
代入をしない限りは変数の中は**空っぽのまま**です。

初期化というのは、変数の宣言と代入と**同時に行うこと**をいいます。
```
string $text = "Hello";
```
宣言だけだと変数の中身は空っぽのままですが、同時に代入まで済ませてしまう...つまり**初期値を設定している**わけです。
なので初期化と言うんですね。

基本的にシンプルなMELを書く分には、初期化で十分に書くことができます。
ですが、場合によっては宣言のみを先に行い、あとで代入することもありますので、頭の片隅に入れておくと良いかも知れません。

# おわりに
いかがでしたか？
変数はほとんどのプログラミング言語に存在する、非常にスタンダードな機能です。
最初はとっつきにくかったり、どれがどれだか分からなくなるかも知れませんが、次第に慣れます。
最初のうちは一つ一つ確認しながらスクリプトを書いていきましょう。

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

# 参考
https://knowledge.autodesk.com/ja/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2020/JPN/Maya-Scripting/files/GUID-D2BE103B-A136-43B6-8142-BF5E68F246F8-htm.html