MaxScriptを書こう ~その10

2021年9月3日

メッセージを改良する

前回、条件によってメッセージを表示するという機能を追加しました。正常に終了したときには「完了しました」というメッセージを出すようになっていましたが、これを、オブジェクトをいくつ処理したのかを表示するメッセージに変更しましょう。

具体的には、処理終了時に「○個のオブジェクトを処理しました。」というようなメッセージを表示させる、ということです。

実は今回の目的だけを実現するのであれば、このツールは「選択されているオブジェクトに対して処理を行う」ということがわかっているので、処理されるオブジェクトの数は最初からselection.countであることがわかっています。

ただ、それでは面白くないので、処理を実行するたびに数を数えて、最終的にいくつ処理したのかを表示させるようなものを作ってみたいと思います。

変数を知ろう

まず、処理したものの数を数えたいので、その数を格納する変数を作ります。

実はこれまで、変数はいろいろなところに登場していたのですが、それと意識して宣言したことはまだありませんでした。たとえばrollout ro_adjust_posのro_adjust_posは変数ですし、for sel in selection のselも変数です。(厳密にはselectionも特別な変数です)

変数のは何らかのデータに名前をつけて保持するためのもので、よく入れ物にたとえられます。例えば、

a = 10

とやれば、aという変数に10を代入したことになります。以降、aは10として振舞います。a + 2 とやれば12が返ってきます。

基本はこれだけです。変数にはなんでも入れることができます。例えば1つオブジェクトを選択して次を実行してみてください。

a = $

これで変数aに現在選択されているオブジェクトが格納されます。そのため、以下のようなことが可能です。

ここで、$はあくまでも「現在選択されているオブジェクト」ですが、aの方は代入された時点で$が指していたオブジェクトを格納しているため、代入を実行したあとで選択を外してしまっても、代入したオブジェクトはそのまま保持されています。上記を実行したあと選択を解除していろいろ試してみてください。何も選択していない状態だと$.posはエラーになりますが、a.posは問題なく実行できます。

変数のスコープを意識する

変数にはその変数が有効な範囲があります。有効な範囲というのは、その変数にアクセスできる範囲、という意味です。その有効範囲のことを「スコープ」と呼びます。「変数のスコープ」という呼称はよく出てくるので覚えておきましょう。

変数には大きく分けてグローバル変数とローカル変数の2種類があります。グローバル変数はどこからでもアクセス可能なもので、ローカル変数は限られた範囲でだけアクセスできるものです。

グローバル変数はどこからでもアクセスできるから便利のような気がしますが、どこからでも改変されてしまう可能性があるという危険をはらんでいます。

そのため、ツールを作成する際には、変数は可能な限りローカル変数として宣言するようにしましょう。どうしてもグローバル変数にする必要があるときだけグローバル変数を使い、その場合は簡単にはバッティングしないような変数名をつけるようにしましょう。

今回は全てローカル変数で問題ないため、全部ローカル変数として宣言していきます。

では、処理したオブジェクトの数を数える変数を宣言してみましょう。

rollout ro_adjust_pos "位置の値を丸めるツール" (
    button btn_exec "実行!"

    on btn_exec pressed do (
        if selection.count == 0 do (
            messageBox "操作するオブジェクトを選択してください"
            return false
        )
        local cnt = 0
        for sel in selection do (
            adjustPos sel
            cnt += 1
        )
        messageBox (cnt as string + "個のオブジェクトを処理しました")
    )
)

createDialog ro_adjust_pos

これを実行すると次のようになります。

9行目でlocalというキーワードを使い、ローカル変数を宣言しています。何もつけずに変数を宣言するとMaxScriptではグローバル変数になります。そのため常にlocalというキーワードをつけて変数を宣言する習慣をつけましょう。

ローカル変数のスコープ

変数は大きく分けてグローバルとローカルという説明をしましたが、実はローカル変数にもスコープ(有効な範囲)があります。

MaxScriptのローカル変数はブロックスコープです。ブロックスコープというのは、宣言したブロックでのみ有効、という意味です。

ブロックというのは、ものすごくザツに言うと()の中、というようなイメージです。

例えばfor文は次のようになります。

for sel in selection do (
    --ここがブロックの中
)
--ここはブロックの外

私たちのツールはブロックがさらに階層になっています。

rollout ro_adjust_pos "位置の値を丸めるツール" (
    --rollout のブロック
    button btn_exec "実行!"

    on btn_exec pressed do (
        --ボタンハンドラのブロック

        if selection.count == 0 do (
            --ifのブロック
        )

        for sel in selection do (
            --forのブロック
        )
    )
)

外側のブロックをスコープとする変数はその内側からも見えますが、内側のブロックの変数は外からは見えません。

例えばロールアウトのブロックにlocal宣言した変数はボタンハンドラの中からもアクセスできますし、さらにその中のifやforのブロックからもアクセスできます。

ですが、ifブロックの中でlocal宣言した変数はボタンハンドラブロックからはアクセスできませんし、ボタンハンドラブロックでlocal宣言した変数はロールアウトブロックからはアクセスできません。

元のコードに戻ってみると、local宣言したcnt(countの略のつもり)という変数はボタンハンドラのブロックに宣言されています。そのためそれより内側にあるforブロックの中からアクセスできるわけです。

ここでcnt += 1という表記が出てきますが、これはcntに1を足したものを改めてcntに代入する、という意味になります。つまりcnt = cnt + 1と同じことを意味しています。

最後にそのカウントした値をメッセージに表示するわけですが、表示するためには文字列でなければなりません。cntは数値のデータですが、これを数「字」にする必要があります。

MaxScriptでは変数のタイプ(型と呼びます)を変更するにはas○○という表現を使います。

※変数の型については別のところで詳しく説明する予定です。今回は「数値を文字列と連結するにはas stringする必要がある、ということだけ覚えておいてください。

次回はあらかじめ評価しておく必要のあるadjustPos()の関数定義をどこに書くべきか、ということを考えてみます。

今回のまとめ

  • 変数にはグローバル変数とローカル変数があり、それぞれスコープ(有効な範囲)が異なる
  • ローカル変数は宣言する場所によってスコープが決まり、そのスコープの外からはアクセスできない
  • 数値と文字列を連結するには数値を文字列に変換する必要があり、その変換にはas stringを使う

前:MaxScriptを書こう ~その9

次:MaxScriptを書こう ~その11