MaxScriptを書こう ~その14

2021年9月3日

桁数指定をUIから行えるようにする

前回round関数を作成し、数値を丸める際に桁数を指定できるようになりました。これを私たちのadjustPosツールからも指定できるようにしましょう。

ユーザインタフェース上から桁数にあたる数値を渡したいのですが、これを実現する方法はけっこうたくさんあります。今回の用途で最も使いやすいのはスピナーでしょう。

スピナーは3dsmaxのインタフェース上でも数値を操作する部分に良く使われています。スピナーの良いところは、数値以外のものは入力できない、という点です。このため、スピナーから送られてくるデータは必ず数値だ、という前提で処理してしまって構いません。これがテキスト入力などの場合、入力されたものが数値かどうか(厳密には数値に変換できるかどうか)を確認して処理を分岐する必要が生じます。

スピナーを追加する

ではスピナーを搭載してみましょう。

rollout ro_adjust_pos "位置の値を丸めるツール" (
    include "$userScripts\\commonLib.ms"
    spinner spn_digits "桁数" type:#integer
    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

スピナーもボタンと似たような方法で定義します。違うのはtypeの指定です。typeは:を使って指定しているので、デフォルト値を指定された引数だということになります。(デフォルトは#floatで、浮動小数点数という意味です)

今回、桁数は整数なのでtypeを#integer(整数の意)に指定します。

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

桁数を指定するスピナーができました。もちろんまだこの値をどうこうする処理を何も書いていないのでこのスピナーは何も意味を持っていません。ただ、動作は確認できるので触ってみましょう。

ちょっと触ってみるとわかりますが、このスピナーは0より下に行きません。0の状態から下向きの矢印を押しても変化しませんし、直接負数を入力しても0に戻されてしまいます。

負数を指定したら10の位、100の位などで丸めてほしいのでこのスピナーの状態はあまりよろしくありません。そこで値の範囲も指定することにします。

値の範囲はrangeというキーワード引数で指定します。スピナーのところだけ抜き出してみます。

spinner spn_digits "桁数" type:#integer range:[-10, 10, 0]

範囲はこのように書き、[最小値、最大値、初期値]という形で指定します。つまりrange:[-10. 10. 0]というのは「スピナーの範囲はー10から10までで、初期値は0ですよ」という意味になります。

-10から10までにしたのは、桁数の丸めについてだいたい10桁ぐらいあれば用が足りるだろう、という目算によりますが、余裕を見て-100~100ぐらいに設定しても良いでしょう。

スピナーから値を受け取って処理に使用する

あとはこのスピナーから値を得て処理に使えば良いだけです。

が。やろうと思うと問題があることに気づきます。ツールから使用している関数はadjustPos()ですが、この関数には引数は1つしかありません。対象のオブジェクトを受け取るだけです。adjustPosの中でroundを呼んでいて、そこに桁指定があります。

困ったことになりました。スピナーから取得した桁数をroundの実行に使いたいのですが、窓口のadjustPosは桁数を受け取らないのです。

これをどのようにするかは難儀な問題なのですが、簡単に解決するならadjustPosにも引数を追加して、受け取った引数をそのままroundに渡せば良い、ということになります。

function round num digits:0 = (
    local target_num = num * (pow 10 digits) + 0.5
    local ret_val = (floor target_num) / (pow 10 digits)
    return ret_val
)

function adjustPos obj digits = (
    obj.pos.x = round obj.pos.x digits:digits
    obj.pos.y = round obj.pos.y digits:digits
    obj.pos.z = round obj.pos.z digits:digits
)

こんな感じでしょうか。adjustPos関数に2番目の引数を追加しました。これにデフォルト引数を設定していないのは、二重にデフォルト引数を指定するとわかりにくくなってしまうからです。

ためしにテストしてみるとこんな感じです。

※関数型パラダイムなどではこのような「受け取った引数をそのまま別の関数に渡す」という処理はやらないほうが良いとされますが、ここではこれで用が足りるので良いことにしてしまいます。

これで受け取る準備ができたので、スピナーの値を受け取って実行するようにしましょう。

adjustPosを呼び出している部分を次のように書き換えます。

adjustPos sel spn_digits.value

スピナーオブジェクトを格納している変数spn_digitsを使用し、そのvalueプロパティを取得してadjustPosの2番目の引数に渡しています。

実行してみると以下のようになり、ちゃんとスピナーで指定した桁数に丸められていることが確認できます。

これでほぼ動作が意図通りになりました。これでスクリプトツールとしては完成でも良いのですが、このままだとこのツールを使うには毎回「スクリプト→スクリプトを起動」というメニューを辿ってこのmsファイルを指定する、という作業が発生します。

次回はこれをマクロ化し、ショートカットキーを割り当てたり、ツールバーから実行したりできるようにしましょう。

今回のまとめ

  • 数値を受け取るインタフェースとしてスピナーが便利
  • スピナーの値の型(タイプ)はtypeキーワードで指定する
  • スピナーの値の範囲はrangeキーワードで指定する

前:MaxScriptを書こう ~その13

次:MaxScriptを書こう ~その15