MaxScriptを書こう ~その11

2021年9月3日

ツール内で使う関数はどこで定義すべきか

またまた大上段に振りかぶりました。○○すべき、という言葉を使うとどうもたいそうなことを言っている感じになってしまいますが、これも厳格な話ではなく、いくつかの方法を紹介して、その都度目的に合った場所に書きましょう、というような方向で説明してみたいと思います。

同じスクリプトファイルに書く

同じファイル内のロールアウト定義より前に書くことができます。

function adjustPos obj = (
    --関数の中身(省略)
)

rollout ro_adjust_pos "位置の値を丸めるツール" (
    ロールアウトの中身(省略)
)

createDialog ro_adjust_pos

このような感じです。これを評価すると上から順に評価されるため、関数の評価→ロールアウトの評価→createDialogという順番で評価され、問題なく実行できます。

ここで注目すべきところは、function adjustPosと書いてあるところのadjustPosは変数の一種である、ということです。これは関数の名前なのですが、関数というのはある一定の処理に対して名前をつけたものです。名前をつけたものというのはつまり、関数自体が変数の中に入っている、と考えることができます。

実際にはadjustPosという変数に格納されているのは関数の本体へのポインタのようなものになりますが、ここでは関数そのものを格納していると考えて差し支えありません。(本当はこの発想は相当強引ですが、MaxScriptの範囲でスクリプトを書いている限りにおいてはほとんど問題にならないので大丈夫です)

※ポインタについてはC++を扱う記事のところで紹介する機会があると思います。

関数宣言のスコープ

だいぶ脱線しましたが、adjustPosも変数だという話でした。これが変数だということは、これにもスコープがあるということになります。ここが重要です。

重要なのでもう一度書きます。function キーワードで定義した関数の名前は変数と考えることができるので、これにはスコープ、つまりアクセスできる範囲がある、ということです。

functionで定義する関数名にはグローバルとローカルの区別はありません。常に、その定義を書いた場所でのブロックスコープが適用されます。どのブロックにも属していない場所に書くとグローバルスコープが適用されます。

※厳密には関数を「宣言」した場所によってスコープが決まります。MaxScriptでは特殊なことをしない限り、関数の宣言と定義が同じ場所にあるため、まずは定義している場所で同時に宣言もされている、と考えてください。

※関数の定義と宣言を分離して使用する例は別の機会に説明します。

ではやってみましょう。

rollout ro_test "test" (
    function testfunc = (
        print "testfunc"
    )

    button btn_test "test"

    on btn_test pressed do (
        if true do (
            local testfunc2 = (
                print "testfunc2"
            )
        )
        testfunc() --実行できる
        testfunc2() --エラーになる(アクセスできない)
    )
)
createDialog ro_test

testfunc()はロールアウトブロックで宣言&定義されているので、このロールアウト内のどこからでもアクセスできます。全てのイベントハンドラから共通で使うことができる、ということになります。

testfunc2はifブロックの中で宣言&定義されたので、ifブロック内からしかアクセスできません。

そのため、ifブロックを抜けた後に書いてある呼び出し部分(15行目)でエラーが発生します。

だいぶ遠回りしましたが元のツールに戻りましょう。

今、同じスクリプトファイルのロールアウト定義よりも前に関数定義を書くと、この場所はどのブロックにも属していない、ということになり、グローバルスコープで関数が宣言されます。

つまり、このようなスクリプトファイルを評価すると、以降その関数はリスナー上から直接実行することができます。

ロールアウトブロックに書く

ロールアウトブロックに書くと以下のようになります。

rollout ro_adjust_pos "位置の値を丸めるツール" (
    function adjustPos obj = (
        --関数定義(省略)
    )

    button btn_exec "実行!"

    on btn_exec pressed do (
        --ボタンハンドラ(省略)
    )
)

この場合、adjustPos関数はロールアウトブロックのローカル関数となり、このツール内では自由に使うことができますが、ツールの外からはアクセスできません。

※今これを評価してリスナーからadjustPosを実行してもエラーにならないかもしれません。それは以前グローバルスコープで評価したものが残っているからです。一度3dsmax本体を再起動し、改めて評価して試してみてください。

次回は、この関数を汎用関数として別のファイルに書いておき、ツールのファイルからそのファイルを読み込んで使用する、という例をやってみましょう。

今回のまとめ

  • 関数は書く場所によってスコープ(有効な範囲)が決まる
  • あるブロック内に書いた関数はそのブロックより下の階層からしかアクセスできない
  • MaxScriptでは特別な書き方をしない限り、関数は宣言と同時に定義されていることになる

前:MaxScriptを書こう ~その10

次:MaxScriptを書こう ~その12