記事の目次

    前回はイントロダクションということで、なぜスクリプトを書くのか、スクリプトを書くことによってどんなメリットが得られるのかについて紹介しました。今回からは、本格的に MAXScript の勉強をしていきましょう。デザイナーの方にはプログラミングに対して苦手意識を持たれる方が多いですが、できるだけ分かりやすい解説を心掛けていきますので、ぜひ御一読ください。

    MAXScriptリスナーとMAXScriptエディタ

    標準でインストールされる MAXScript のリファレンスは英語版しか用意されていません。「英語なんかヘッチャラ!」という方なら英語環境で頑張って頂ければと思いますが、そうでない方はオートデスクの「Autodesk 3ds Max サービス&サポート」から日本語版のリファレンスを入手しましょう。
    上記サイトの「3ds Max 2010、3ds Max Design 2010 MAXScript 日本語リファレンス」からダウンロードします。

    3ds Max には、ユーザーが Max を操作した履歴を見ることができる「MAXScriptリスナー」とMax 上でスクリプトを書くための「MAXScriptエディタ」があります。
    本格的なスクリプトを書く場合は MAXScriptエディタでは機能が十分ではなく不便な所もありますが、書いたスクリプトをその場ですぐ試すことができるのでとても重宝します。

    まずは試しに、MAXScriptリスナーで履歴を見てみましょう。メニューの[ MAXScript(M)/MAXScriptリスナー(L)... ]からMAXScriptリスナーを開き、マクロレコーダを使用可能にします。

    MAXScriptリスナーのUI

    使用可能にしたら、GUIからボックスをいくつか作ってみます。すると、MAXScriptリスナーの上段に何やら文字列が出てきます。これが、今あなたが Max を操作した履歴です。

    MAXScriptリスナーの履歴を表示

    では、この内容をMAXScriptエディタにコピー&ペーストして実行してみます。
    まずは新しいシーンを作成して、MAXScriptエディタの[ツール(T)/全てを評価(A)]を実行します。
    どうでしょう? 先ほど手で作成したものと同じオブジェクトが作成されましたね? ようこそ、MAXScript の世界へ!

    MAXScriptリスナーのコマンドを実行

    と思いきや、表示されている色が違いますね。この色は、オブジェクトを作る時に Max が自動的にしていしているためです。もちろん、色を統一する必要があるときはスクリプトで指定することもできます。MAXScriptリスナーについての詳しい情報は、先ほどダウンロードしたマニュアルの[ [MAXScript リスナー]ウィンドウ]の項目を参照してみてください。

    この MAXScript リスナーを上手に使うと、プログラマーとのコミュニケーションがスムーズに行えるようになります。データの変換など、単純だけれども大量に処理をしなければいけない仕事が来た時にひと通り作業を見てもらいながら、同時にリスナーーでログを取ってプログラマーに渡します。そうすれば CG ソフトのオペレーションに詳しくないプログラマーでも、何をしなければいけないのか簡単に理解することができます。

    この方法の一番の利点は、MAXScriptリスナーが翻訳者となって、プログラミングが不得意なデザイナーとCGソフトの扱いが不得意なプログラマーの会話をすることができることです。実際に、筆者も仕事で同じような方法で作ったスクリプトをデザイナーに使ってもらったことが多々あります。
    そのようなやりとりをしていると、作った私自身そのプログラムをどうやって使うのか知らないということもありました(笑)。同様に、デザイナーもプログラムが中で何をやっているのか理解していません。それなのにきちんと成果が上がっていたというのがとても面白い現象です。

    [[SplitPage]]

    プログラム基礎の基礎

    いくらMAXScriptリスナーを駆使しても、どうしてもプログラムを書かなければ実現できないことは多々あります。本職のプログラマーほど流暢にプログラミング言語を扱うことができなくても、ほんのちょっと概念と文法を覚えるだけでできることが一気に広がるのがプログラミングの楽しいところです。

    ここからはちょっと退屈かもしれませんが、プログラムを書く上で最低限知っておかないといけない言葉と概念を解説します。

    変数

    「変数」は、数値や文字列などの値を入れておく場所です。変数の名前は、アルファベットまたは「_」(アンダースコア)で始まり、任意の英数文字または「_」を含むことができます。また、名前の大文字小文字は区別されません。

    
    
    [例の引用 : MAXScriptリファレンス/名前、リテラル定数、および式/名前/名前]
    
    

    変数に値を入れる(代入すると言います)ときは '=' を使います。a という名前の変数に 1 を代入するときは;

    
    
    a = 1
    
    

    ......というように書きます。

    変数と配列の概念図

    変数と配列の概念図。配列(コレクション)については後述

    制御構造

    同じ処理を何回も繰り返したいときや、条件によって処理を変えたいときがあります。そのような時には「制御構造」を使います。
    例えば、処理を繰り返す for 文、処理を変える if 文などがあります。

    ブロック

    処理のかたまりを「ブロック」と言います。MAXScript では、ざっくりと '(' と ')' で囲まれた部分をブロックと思っておきましょう。場合によってはブロックの中にブロックを入れる事もできます。
    細かく言えばもっとたくさんありますが、退屈な上に理解するには結局は手を動かしてみるのが一番です。これらのことを頭の片隅に置きながら、実際のプログラムを見てみましょう。

    前回の内容を詳しく読み解いてみる

    環境が整ったところで、まずは肩慣らしに前回ご紹介したコードを解説していきますね。
    こちらがそのコードです;

    
    
    for s in selection do (
    		print s.Name
    )
    
    

    ......このコードは以下のような構造になっています。

    print s.Name

    後述しますが、「s 」にはオブジェクトが入っています。オブジェクトにはいくつか決まった変数(プロパティ)があって、「Name 」というプロパティはそのものズバリ、オブジェクトの名前を指します。

    selection

    MAXScriptで予め用意されている変数です。今選択しているオブジェクトの配列が格納されています。配列とは、変数が順番に並んだひとまとめのことと言えますが、正確には「selection 」は、MAXScriptにおいて「コレクション」と呼んでいます。詳しくは MAXScript リファレンスの[コレクション→コレクションのタイプ→ObjectSet値]を参照してください。ここを見ると、selection 以外にも様々な変数があることが分かりますよ。

    ただ、今はあまり難しいことは考えないで、「selection っていうのを使うと選択しているオブジェクトの一覧が取れるんだな」ぐらいに思って頂ければ十分です。

    for 文

    「for」は、指定した条件が整うまで "()" で囲まれた処理を繰り返すための命令です。

    MAXScript の for 文には、2種類の使い方があります。ひとつは今回の例で、配列の中身をひとつひとつ変数に代入しながら繰り返し処理を行うものです。
    今回の例の場合、s という変数に selection から一つづつオブジェクトを取り出して代入して、その度にブロックの中の処理をしています。

    もうひとつは、数値を変化させながら(大抵は1づつ加算しながら)繰り返し処理を行うものです。
    では、同じ処理を加算しながら行うバージョンに書き変えてみます;

    
    
    for i=1 to selection.count do (
    		print selection[i].Name
    )
    
    

    ......最初の例に比べて、ちょっとゴチャゴチャしてますね。最初に i に 1 が代入されて、1,2,3,...と selection.count(選択されているオブジェクトの数)まで加算しながら繰り返し処理を行います。

    具体的な例を見てみましょう。今、Box001, Box002, Box003 が選択されているとします。そこで、サンプルコードを走らせるとどうなるでしょう;

    
    
    /* 1度目のループ*/
    s = selection[1] --s に $Box001 が入る
    print s.Name     --"Box001"
    
    /* 2度目のループ */
    s = selection[2] --s に $Box002 が入る
    print s.Name     --"Box002"
    
    /* 3度目のループ */
    s = selection[3] --s に $Box003 が入る
    print s.Name     --"Box003"
    
    
    /* おわり */
    
    

    ......for の部分を全て展開するとこのようになります。もちろん、このように書いても選択されているものの数が3つと決まっていれば同じことができますが、数が増えたり、名前以外のものを表示したいとなると途端に破綻してしまいます。せっかく苦労してスクリプトを書くのだから、どうせなら何度でも使い回せるようにした方が良いですよね。

    [[SplitPage]]

    特殊な表記

    解説中で、$Box001 というように '$' をジオメトリ名の頭に付けています。これは、Box001 というジオメトリを表す変数のことで、MAXScript では通常の変数と区別するため、このような表記方法を取っています。ちょっと分かり辛いのでまとめてみます;

    
    
    print $Box001.Name -- "Box001" と表示される
    $Box001.Name = "newName"
    print $newName.Name -- "newName" と表示される
    print $Box001.Name -- エラーになる
    print newName -- undefined
    
    

    また、ジオメトリは '*' を使って配列として取得することもできます。試しに、

    
    
    for box in $Box* do (
    		print box.Name
    )
    
    

    ......と実行してみてください。シーンの中にあるジオメトリのうち、頭に "Box" と名前の付くものだけを選び出すことができました。これも最初の例の応用です。

    for をさらに使いこなしてみる

    名前を表示するだけなのも飽きてきました。もうちょっと別の使い方も考えてみます。
    せっかくCGソフトなので、オブジェクトを操作してみましょう。
    100 個のオブジェクトを配置してみます。

    
    
    for i=0 to 9 do (
    		for j=0 to 9 do (
    				x = i * 10
    				y = j * 10
    				box = Box pos:[x, y, 0] length:1 width:1 height:1
    				box.Name = "Box_" + (i as String) + "_" + (j as String)
    		)
    )
    
    

    ......何だかいきなり複雑になってしまいました。ですが、ひとつひとつの構成要素をじっくり見ていけば理解できますよ。
    一番大きな違いは、for 文の中に for 文が入っていることです。さらに、内側の for 文の中身もよく分からない記号が沢山入っています。
    こういう時には、MAXScript エディタの機能を使います。

    MAXScript構文の折りたたみ例1

    内側の for の左にある四角の部分をクリックしてみてください。すると!! for が折り畳まれて、 シンプルな見た目になりました。

    MAXScript構文の折りたたみ例1

    さらに外側の for の中身も折り畳んでみてください。シンプルな for だけのブロックになりました。

    MAXScript構文の折りたたみ例1

    こうして見ると、外側の for によって i が 0 から 9 まで変化しながら毎回内側の for が 呼ばれていることが判ります。
    これを理解できてしまえば、後は一番内側のブロックを解析するだけです。

    
    
    x = i * 10
    y = j * 10
    
    

    「x」と「y」という名前の変数にループで加算される値を 10 倍して代入しています。

    
    
    box = Box pos:[x, y, 0] length:1 width:1 height:1
    
    

    ......Box ジオメトリを生成しています。 pos や length, width, height は Box を作る際のオプションです。先ほどシーンを再構築するために MAXScript リスナーの値をコピー&ペーストした時にもありましたね。
    GUI から作るとこれ以外にも様々なオプションがつきますが、必須ではないので省略しています。

    
    
    box.Name = "Box_" + (i as String) + "_" + (j as String)
    
    

    作成した「Box」の名前を付けています。 文字列は '+' を使って繋げることができます。
    「 (i as String) 」という部分は、i という数値を文字列に変換しています。
    数値と文字列は、画面で見たときにはほとんど違いがありませんが、コンピュータからみたら大きな違いがあります。

    
    
    a = 1 -- a には数値の 1 が入る
    b = a + a -- 数値を足しているので b は 2
    a = "1" -- a には文字列の 1 が入る
    b = a + a -- 文字列を足しているので b は "11"
    
    

    人間が計算をする時はその場に応じてその値が数字なのか文字列なのか、それらをどのように扱ったらいいのか判断をしながら処理ができますが、コンピュータに処理をさせる時には厳密に指示をしなければいけません。そのため、わざわざ明示的に文字列に変換しているのです。
    これは数値や文字列に限らず、プログラミングを行う上でとても大事なことになってきます。プログラムがなぜかエラーになるのかとか、マニュアルで調べた機能をどのように使うのか分からない時は、プログラム上で今どのような形式のデータを扱っているのか注意すると問題が解決することが多々あります。

    因みに、ループで selection から 取り出した s の中には、数値でも文字列でもない、ジオメトリという形式のデータが入っていました。そして、今解説中の Box にも同じ形式のデータが入っています。
    マニュアルで「Box」を調べてみてください。検索すると、"Box: GeometryClass" という項目が見つかります。このページを見ると、Box に指定できるプロパティの一覧を知ることができます。そこを見てみると、Box ジオメトリを生成するときに使用した「length」、「width」、「height」などのオプションもあります。

    それ以外の、「pos」や「Name」などはどこにあるのでしょうか? これは[ノードの共通プロパティ、演算子、メソッド]にあります。
    pos や Name は Box に特有のものではなく、Sphere や Plane にもあります。Max ではこれらを総称して「ノード」と呼び、ノードは共通のプロパティを持っています。ノードの一覧は[GeometryClass: Node]の項目で見ることができます。

    今、自分がどのような種類のデータを扱っているか知りたいときに便利なのが classOf メソッドです。
    classOf <変数、オブジェクトなど> と実行すると、データの種類を表示してくれます。

    MAXScript構文の折りたたみ例1

    データの種類が判れば、それを元にマニュアルを参照して何ができるのかを調べたり、スクリプト内でどのような処理がされているのかを理解することができます。
    そのジオメトリにどのようなプロパティがあるかなど、より詳細な内容を知りたければ「showProperties」のようなメソッドを使います。これらの使い方は次回以降、必要に応じて解説します。興味のある方はリファレンスの[クラスとオブジェクトの調査関数]を参照してみてください。

    さて、解説が長くなってしまいましたが、これで全ての内容が理解できるはずです。いかがでしょうか?
    今回解説したループと、MAXScript リスナーを上手に使えば今まで手で何百回と繰り返してきた単純作業をスクリプトに任せることができるようになります。ぜひ、活用してみてください。次回以降は、より具体的な応用例をご紹介していきます。

    TEXT_痴山紘史(JCGS)

    映像制作のためのパイプライン構築をはじめ、技術提供を行なっていくエンジニア集団、「JCGS(日本CGサービス)」代表取締役......というのは表の顔で、実態は飲み会とCG関連の技術が好きなただのCGオタク。
    http://philosy.com/blog