変数には「スコープ」と呼ばれるアトリビュートがあり、MAXScript コード内で変数がアクセス可能な場所を決定します。
MAXScript には、グローバルとローカルの 2 種類の変数スコープがあります。
「グローバル変数」は実行中のすべての MAXScript コードで見ることができ、3ds Max を終了するまで値を保持します。
「ローカル変数」は、現在有効な構文スコープ内のコードに対してのみ直接可視となり、現在のスコープが有効な場合に限り、値が保持されます。スコープが有効でなくなると、そのスコープにローカルの変数にアクセスできなくなります。これは、多くのプログラミング言語に実装されている変数の概念と同様です。次に、MAXScript が新しいスコープを作成する条件について説明します。
MAXScript では、明示的に宣言したり、グローバルとローカル変数の両方を初期化するために使用する言語構成が提供されています。
次に変数宣言(<variable_decls>
)の構文を示します。
( local | global ) <decl> { , <decl> }
ここで、 <decl>
は以下のように定義されます。
<var_name> [ = <expr> ] -- name and optional initial value
例:
global baz -- initialized to 'undefined'
global foo=10, initialized=false -- initialized globals
local x = 1, -- continued on several lines
y = 2,
z = sqrt(123.7)
前述のように、ローカル変数はスコープが有効な限り値を保持します。ネストされているスコープでは、通常、これは非常に短時間です。関数、ループ式、またはブロック式の実行開始から実行終了までです。
スコープが実行範囲に入るたびに、ローカルの新規セットが作成され、スコープの終了時に削除されます。
変数を常に明示的に宣言することは推奨される方法です。その理由については、このページの以降の部分を参照してください。
3ds Max 8 以降、MAXScript は変数名をグローバルとして明示的にアクセスする方法を提供しています。変数のグローバルのプール内に存在している場合に使用されます。変数がグローバル スコープ内に存在していない場合、明示的にグローバルなものとして作成されます。
構文はダブルコロンの'::'で、このトピックで記述されます。
状況によってはトップ レベルのスコープの有効期間を延長でき、トップ レベルで宣言されたローカルは値をその延長期間にわたって保持できます。
特に、スクリプト化されたロールアウト、ユーティリティ、プラグイン、スクリプト コントローラ、マクロ スクリプトで宣言されたトップ レベルのローカルは、ロールアウト、ユーティリティ、プラグイン、マクロ スクリプトが存在する限り値を保持します。
通常、これらのローカルは再定義されるまで存在し続けます。たとえば、マクロ スクリプトを定義して初めて実行するときトップ レベルのスコープが作成され、マクロ スクリプトを再定義しない限り、継続実行数を超えるまで有効になります。この時点で、既存のトップレベル スコープが破棄され、新しいスコープ(およびそのトップレベル ローカル)が作成されます。
これにより、これらの構文のトップ レベル ローカルをプライベート グローバルとして使用できます。保持される値は実行中はそのまま残りますが、構文内のコードのみが変数を参照するため、他のグローバルと競合することはありません。
スクリプト化されたプラグインでは、プラグインの代わりにトップレベル ローカル スコープの別のコピーが「個々に」作成されます。このスコープは、そのインスタンスが残っている間、現在の3ds Max セッションが終了するまで有効です。
履歴:
3ds Max R2.x では、宣言が新しい変数を作成する場合に限り、オプションの初期化値がグローバル変数に適用されます。たとえば、以下のスクリプトを 2 回実行すると、2 行目の x の値は、最初の実行では 10 になり、2 回目の実行では 20 になります。3ds Max 3 以降では、オプションの初期値は、常にグローバル変数に適用されます。
global x = 10
print x
x=20
3ds Max 3 以降でグローバル変数の初期化条件文を作成したい場合は、次のような構文を使用します。
global foo; if foo = = undefined do foo = 23
変数を明示的に宣言せず、その変数名が上のレベルのスコープに存在しない場合は、その変数を初めて使用するとき MAXScript によって変数が作成され、特別な値 undefined
を保持するために変数が初期化されます。変数を使用する前に、変数を明示的に宣言したり値を初期化する必要はありません。前のステートメントで明示的に宣言されていない変数は、暗示宣言変数と呼ばれます。暗示的に宣言された変数のスコープは、変数が最初に使用されたときの MAXScript のスコープ コンテキストになります。MAXScript スコープ コンテキストの初期値はグローバルで、次の場合に新しいスコープ コンテキストが開かれます。
スクリプト ファイルまたはリスナーにおけるトップ レベルの始まりのカッコ
関数本体の始まり
for
ループ本体の始まり
ユーティリティ、ロールアウト、右クリック メニュー、マクロ スクリプト、またはツール定義の始まり
ユーティリティ、ロールアウト、右クリック メニュー、マクロ スクリプト、またはツール イベント ハンドラの始まり
when
本体の始まり
これらの新規スコープ内では、新しく参照された変数は、ローカル変数として明示的に宣言されます。
スコープ コンテキストはネストされています。明示的または暗示的に宣言された変数のスコープが 1 つのスコープ コンテキストのレベルになり、その下のレベルのスコープ コンテキストすべてに拡張されます。
次に、スクリプトの例を示します。
上のスクリプトで、変数 a
はグローバル スコープ コンテキストで最初に使用され、スコープにはスコープ コンテキスト グローバル、レベル 1、およびレベル 2 が含まれます。
変数 b
は、スコープ コンテキスト レベル 1 で最初に使用されるのでローカル変数として明示的に宣言され、スコープにはレベル 1 とレベル 2 のスコープ コンテキストが含まれます。
変数 i
および j
はスコープ コンテキスト レベル 2 で最初に使用され、スコープはスコープ コンテキスト レベル 2 のみです。
変数 k
のスコープは、このスクリプトが既に実行されたかどうかにより異なります。
初めてスクリプトを実行した場合、変数 k
は最初に 5 行目で定義され、スコープはスコープ コンテキスト レベル 2 になります。
また、変数 k
は 11 行目で再度使用されています。5 行目で定義された変数 k
は、ここではスコープ内にないため、新しい変数 k
が定義され、スコープはグローバルになります。
2 回目にスクリプトを実行する場合、5 行目で MAXScript により変数 k
が既に存在することが検出され、既存の変数 k が使用されます。
このため、最初にスクリプトを実行すると、11 行目で undefined
が出力されますが、2 回目にスクリプトを実行すると、11 行目では 5 行目で計算された最後の値が出力されます。
for
ループ カウンタで使用される変数(上のスクリプトでは変数 i
)は、特殊なケースです。for
ループ カウンタ変数のスコープは、常に for
ループ用に作成されたスコープ コンテキストです。これは、for
ループ カウンタ変数の名前が、上位のスコープ コンテキストで既に暗黙的または明示的に宣言されている場合にも当てはまります。ローカル変数を明示的に宣言する場合、上のスコープ コンテキスト レベルで使用した変数と同じ名前を再使用できます。
この場合、新しく宣言したローカル変数は、同じ名前の外部変数を不可視にします。
ローカル変数のスコープ内でのこの変数名への参照は、以後この新しいローカル変数が使われます。
ローカル変数のスコープの最後に到達すると、次の外部変数が再び可視になります。
この可視手順は「構文スコープ」と呼ばれます。次のスクリプトに構文スコープの例を示します。
1 つの変数名を何度も使うことに違和感があるかもしれませんが、大型のプログラムの場合これらのスコーピング規則が便利な場合もあります。
たとえば、新しいユーザ インタフェース アイテムとそのハンドラをユーティリティ スクリプトに追加する場合、ハンドラに使用する変数をローカルとして明示的に宣言すると、これらの変数がスクリプト内のほかの場所で使用されている同名の変数に依存しないことが保証されます。
スクリプトを作成するときは、ローカル変数およびグローバル変数を明示的に宣言することをお勧めします。
暗示的な宣言は簡易的に提供されるものであり、通常、リスナーでインタラクティブに作業する場合や短いスクリプトを作成する場合に使用します。
長いスクリプトを作成する場合は、変数を明示的に宣言するとエラーが軽減し、コードが読みやすくなります。
また、特にグローバル変数が必要な場合を除き、すべての変数をローカルとして宣言することをお勧めします。これは、次のような理由によります。
ブロックまたは関数で使用するすべての変数名をひとまとめにできるため、変数名を見つけるのが簡単で、スクリプト内で誤った名前の使用を防ぐことができます。
同時に複数のスクリプトを実行する場合(たとえば、スクリプト ユーティリティを実行し、シーン内にスクリプト コントローラがある場合)、両方のスクリプトで同じグローバル変数名が使用されているとスクリプト同士が干渉し、両方のスクリプトで同じグローバル変数名が使用されているとスクリプト同士が干渉し、一方または両方のスクリプトで予期できないエラーが発生する場合があります。
上のレベルのスコープで同じ名前の変数が既に宣言されている場合でも、確実に正しい変数を使用でき、上のレベルの変数の値を無意識に変更することはありません。
ローカルとして明示的に定義された変数の値は、エラー コールスタック トレースバックに表示されます。
3ds Max R2.5 では、3ds Max クラス、MAXScript メソッド、または MAXScript 読み込み専用グローバル変数が暗示宣言ローカル変数と同じ名前を持ち、その変数名への代入が発生した場合には、MAXScript ランタイム エラーが生成されます。変数名が既にグローバル スコープに存在していたため、暗示宣言ローカル変数は作成されません。ユーザがロードしたサード パーティのプラグインによって 1 つ以上の新規 3ds Max クラスが作成されるため、変数名がグローバル変数名の場合は前もってこれを知ることができません。3ds Max R3 では、これらの変数への最初の参照が代入であることが MAXScript コンパイラにより検出されると、読み込み専用のシステム グローバルへの参照としてそのままにするのではなく、暗示的にローカル変数が宣言されます。たとえば、3ds Max R2.5 で次の式を実行すると、値は Box
になります。これは、Box
が 3ds Max オブジェクト クラスだからです。3ds Max R3 では、変数 box
の最初の使用が代入であることが MAXScript により検出され、暗示宣言ローカル変数 box
が作成されます。3ds Max R3 でのこの式の結果は、undefined
です。
(if false do box=10; box)
次のスクリプトでは、定義されていない変数 k
を 7 行目で使用することにより、エラーが挿入されています。出力では、エラー コール スタックのトレースバックにより、関数 afunc
内と、afunc
を呼び出しているブロック式での変数 b
の値が表示されます。
スクリプト:
fn afunc =
(
local b
b = box()
for i in 1 to 10 do
for j in 1 to 10 do
instance b pos:[i*20,j*30,30*sin(k*36)]
)
--
(
global c=10
local b
b="hello"
afunc()
)
出力:
afunc()
-- Error occurred in j loop
-- Frame:
-- k: undefined
-- j: 1
-- called in i loop
-- Frame:
-- i: 1
-- called in afunc()
-- Frame:
-- b: $Box:Box102 @ [0.000000,0.000000,0.000000]
-- called in <block>()
-- Frame:
-- b: "hello"
-- No ""*"" function for undefined
OK
3 行目と 12 行目を削除してこのスクリプトを実行すると、出力結果は次のようになります。関数 afunc
とブロック式は別の MAXScript スコープ コンテキストに存在するため、変数 b
はそれぞれローカルとして暗示的に宣言され、異なる値を持ちます。 ただし、これらは暗示的に定義されるため、エラー コールスタック トレースバックには表示されません。
出力:
afunc()
-- Error occurred in j loop
-- Frame:
-- k: undefined
-- j: 1
-- called in i loop
-- Frame:
-- i: 1
-- called in afunc()
-- Frame:
-- No ""*"" function for undefined
OK