変数には「スコープ」と呼ばれるアトリビュートがあり、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) |
前述のように、ローカル変数はスコープが有効な限り値を保持します。ネストされているスコープでは、通常、これは非常に短時間です。関数、ループ式、またはブロック式の実行開始から実行終了までです。
スコープが実行範囲に入るたびに、ローカルの新規セットが作成され、スコープの終了時に削除されます。
変数を常に明示的に宣言することは推奨される方法です。その理由については、このページの以降の部分を参照してください。GUID-382E7583-DD2A-4DF5-B568-71502DF95ED9.htm#WS3ED54CBA79FF2E3D1E593B6612B78359E8F-7631
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 つのスコープ コンテキストのレベルになり、その下のレベルのスコープ コンテキストすべてに拡張されます。
次に、スクリプトの例を示します。
行 # |
例 |
1 2 3 4 5 6 7 8 9 10 11 |
a=10 -- scope context: global ( b=20 -- new scope context: level 1 for i = 1 to 5 do -- new scope context: level 2 ( j=random i a k=random i b print (j*a+k*b) ) -- end of scope context: level 2 a=a+b ) -- end of scope context: level 1 print a -- back to global scope context print k |
結果 #1 |
コメント |
10 170 120 290 320 360 30 30 30 undefined undefined |
result of a=10 print from i=1 print from i=2 print from i=3 print from i=4 print from i=5 return value of end of scope level 1 result of print a return value of print a result of print k return value of k |
結果 #2 |
コメント |
10 170 420 410 180 160 30 30 30 5 5 |
result of a=10 result of print from loop i=1 result of print from loop i=2 result of print from loop i=3 result of print from loop i=4 result of print from loop i=5 return value at end of scope level 1 result of print a return value of print a result of print k return value of k |
上のスクリプトで、変数 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 行目で計算された最後の値が出力されます。
ローカル変数を明示的に宣言する場合、上のスコープ コンテキスト レベルで使用した変数と同じ名前を再使用できます。
この場合、新しく宣言したローカル変数は、同じ名前の外部変数を不可視にします。
ローカル変数のスコープ内でのこの変数名への参照は、以後この新しいローカル変数が使われます。
ローカル変数のスコープの最後に到達すると、次の外部変数が再び可視になります。
この可視手順は「構文スコープ」と呼ばれます。次のスクリプトに構文スコープの例を示します。
# |
例 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
global foo = 23, x = 20 -- explicit declaration of foo and x, -- scope is global y = 10 -- implicit declaration of y, -- scope is global format "context level global: foo= %\n" foo if x > y then ( -- top-level open parentheses -- new scope is created local baz = foo + 1 -- uses the global foo local foo = y - 1 -- declares 1st local foo, hides global foo format "context level 1: foo= %\n" foo b=box() -- b implicitly declared as local b.pos.x = foo -- uses 1st local foo if (foo > 0) then ( local a local foo = y - x -- a nested 2nd local foo, hides 1st local format " context level 2: foo= %\n" foo a = sin foo -- uses 2nd local foo format "a= %\n" a ) -- leave scope b.pos.y= foo - 1.5 -- back to 1st local foo format "context level 1: foo= %\n" foo ) -- leave scope -- b, baz and foo no longer in scope format "context level global: foo= %\n" foo -- back to global foo |
|
出力 |
|
20 -- result of line 1 10 -- result of line 3 context level global - foo= 23 -- output from line 5 OK -- result of line 5 context level 1: foo= 9 -- output from line 11 context level 2: foo= -10 -- output from line 18 a= -0.173648 -- output from line 20 context level 1: foo= -10 -- output from line 23 OK -- result of if expression lines 6 to 24 context level global: foo= 23 -- output from line 26 OK -- result of line 26 |
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 |