Python + MaxScriptのツール開発入門(Max2020版) その2

2021年9月3日

関数の中身を実装する

オブジェクトのコピーを実行する

 グリッドコピーということでグリッド状にオブジェクトのコピーを配置することを考えますが、ひとまずグリッド状というのは置いておいて、選択したオブジェクトをコピーする動作を実装してみましょう。

 Max上で、手動でShift+コピーしたときに行われるのはノードのクローンという動作です。クローンにはコピー、参照、インスタンスの三種があり、UIから手動で実行する場合にはダイアログが出て選択できるようになっています。

 この動作を実行するMaxScriptはCloneNodesというメソッドで、maxOpsというコア・インターフェースに実装されています。

 3dsmaxではAPIのインターフェースをFunction Publishingという仕組みを使ってMaxScriptに公開しています。コア・インターフェースにはさまざまな種類があり、maxOpsはMaxのシステム操作に関わる部分を持っているインターフェースです。

リファレンスの読み方

 maxOps.CloneNodesはMaxScriptリファレンスにで調べると以下のように書いてあります。

 今回は少々冗長にはなりますが、このリファレンスに書いてある情報をくどいほど詳しく説明してみます。(ここからのCloneNodesに関する説明はMaxScriptリファレンスに書いてある内容に捕捉を加えただけのものです。)

 左端のは、このメソッドがbool値(trueまたはfalse)を返すことを意味しています。

 右側は引数です。<>で挟まれているのが引数の型です。コロン(:)がついているのは名前付き引数で、オプション(必須ではないもの)です。

 1つ目の引数は次のようになっています。

<&node array>nodes

 型のところにある&は、この引数が「参照」であることを示しています。MaxScriptでは引数の参照渡しは&を明示的に記す必要があります。

 ここではnode array、つまりノードの配列を参照で渡せ、という意味になります。

 2つ目以降は名前付き引数です。

offset:

 これはoffsetという名前でpoint3のデータを渡せという意味です。Point3というのは[x, y, z]のようになった3つの数値の組です。ここで渡すデータはoffset(オフセット)なので、ノードのクローンを生成した際の移動値になります。手動で実行した場合の、Shiftを押しながら移動させた移動ベクトルになります。(具体的には、コピー先座標からコピー元座標を引いた差になります。)

expandHierarchy:

 これはexpandHierarchyという名前でbooleanの値(trueまたはfalse)を渡せ、という意味です。ここにtrueを渡すと生成されたクローンは元のオブジェクトの子になります。デフォルト値はfalseです。

※デフォルト値というのは、名前付き引数を渡さなかった場合に使用される値のことです。MaxScriptの名前付き引数はオプション(必須ではない)という扱いなので、渡さなかった場合はデフォルト値が使用されます。デフォルト値は引数ごとに設定されており、リファレンスに記述があります。

cloneType:

 これも同様に、cloneTypeという名前でenum値を渡せ、という意味です。enumというのは列挙値と呼ばれるもので、予め値を列挙して宣言しておくものです。

 cloneType列挙値は{#copy | #instance | #reference}という形で設定されており、使用する際はこの3つの値(コピー、インスタンス、参照)のいずれかを選択して使用します。デフォルト値は#copyです。

actualNodeList:<&node array>

 これは少々わかりにくいですが、クローン作成時に元のノードを格納する配列です。最初に渡すnodes引数(名前のない引数)と基本的には同じものですが、例えばターゲットカメラのクローンを作成する場合などに、nodesに渡したもののほかに、そのノードと従属関係のあるノードも必要になることがあります。そういったものを含めてクローン元になったもののすべてがこの変数に格納されます。

※この変数は関数に値を渡すためのものではなく、関数から値を受け取るためのものです。そのため空の配列を宣言し、その参照を渡すのが一般的です。

newNodes:<&node array>

 これもactualNodeListと同じような動作をします。こちらには新たに生成されたノードが格納されます。

このmaxOps.CloneNodesは元になるノード配列nodesを受け取ってクローン生成を実行し、生成したものをnewNodesに格納する、という動作をします。

 基本的にはnodesのクローンがnewNodesに格納されますが、nodesに渡したノードの状態(主に従属関係)によっては、nodesに無いもののクローンがnewNodesに含まれることもあります。(つまりnodesの要素数とnewNodesの要素数にズレが生じる)

 しかし、actualNodeListにはクローン元のすべてが含まれているため、actualNodeListとnewNodesの間では要素の1対1関係があります。

※このCloneNodesの説明はすべてMaxScriptリファレンスに書いてある内容に補足説明を加えただけのものです。

クローンを1つ作る動作を実装してみる

では、このmaxOps.CloneNodesを使ってクローンを1つ作る機能を実装してみましょう。

function gridCopy src_node x_count y_count z_count x_interval y_interval z_interval = (
    --------------------------------------
    -- 選択したノードをグリッド状にコピーする
    --  param
    --          node: 元のノード
    --          int: x個数
    --          int: y個数
    --          int: z個数
    --          int: x間隔距離
    --          int: y間隔距離
    --          int: z間隔距離
    --------------------------------------
    print src_node
    print x_count
    print y_count
    print z_count
    print x_interval
    print y_interval
    print z_interval
    local src_nodes = #(src_node)
    local src_list = #()
    local new_list = #()
    local offset = [x_interval, 0, 0]
    maxOps.CloneNodes &src_nodes offset:offset expandHierarchy:false cloneType:#copy actualNodeList:&src_list newNodes:&new_list
)

選択したオブジェクト1つを受け取り、それを配列に格納してその参照をCloneNodesに渡します。actualNodeListとnewNodes用の配列も宣言し、その参照を渡します。

offsetはX方向のみ、受け取った間隔の値を使用しています。

実行すると以下のようになります。

選択したティーポットがX方向へオフセットクローンされました。

次回はこれを元にして、X方向の指定した個数分のクローン動作を実装しましょう。

まとめ

  • 目的の動作を実現する機能をリファレンスから探す
  • リファレンスの読み方を学ぶ(値の型、引数の渡し方、引数の内容等)
  • 複雑な動作はごくシンプルなところから実装し、動作を確認しながら進める