Block-expressions are the basic constructors for structured code. They allow you to group a sequence of expressions and evaluate them in sequence as though they were a single expression.
The syntax for <block_expr>
is:
( <expr> { (; | <eol>) <expr> } )
It is a parenthesized sequence of expressions with each <expr>
separated by either a line break or a ; semicolon.
MAXScript also allows empty block-expressions, ()
. This is useful when incrementally building code to place an empty expression in a path to be filled in later. When evaluated, empty expressions yield the value undefined
.
EXAMPLES:
x = 2 y = 3 z = 4 p = [10,10,10] centre = [20,20,20] ( d = centre.x^2 - (p.x^2 + p.y^2) newz =if d > 0 then sqrt d else p.z print newz ) if x < y then (print x; print y; u += 10; print u)
You can use a mixture of line breaks or semicolons:
EXAMPLE:
( print x; print y p.x += 10 print p.x; print p.y )
Because a block-expression is an <operand>
, you can insert a block-expression anywhere you can put an <operand>
. This is unlike most languages that limit where you can put blocks of code to only a few places.
Further, because all constructs are expressions in MAXScript, a block-expression yields a value defined to be the last <expr>
in the block. This allows you to use a nested sequence of expressions to compute an operand for some operation:
EXAMPLE:
b = box() b.pos.z = ( d = x^2 - y^2 if d > 0 then sqrt d else z )
Here, the result of the block-expression is the result of the final
if
expression, which is then assigned tob.pos.z
.
Because the item in parentheses can be any<expr>
, you can turn any construct in MAXScript into an operand by putting it in parentheses. This allows you to write the following example, in which the operand to be indexed is the conditional result of an if
expression:
(if a > b then c else d)[n + 1]
As described in Scope of Variables, top-level open parentheses in a script file or in the Listener create a new variable scope. Any block-expressions with the top-level block-expression do not create a new variable scope, even if the variable is explicitly declared as local in the inner block-expression. A variable’s scope extends into any block-expressions within the variable’s scope. If you declare a variable as local in a scope, the newly declared local variable hides any outer variables with the same name. Any reference to that variable name later in the local variable's scope refers to the new local variable. At the end of the local variable's scope, the next outer variable becomes visible again. This is shown in the following script:
SCRIPT:
a=1 ( local a=2 ( local a=3 ) print a ) a
OUTPUT:
1 -- result of line 1 3 -- output of line 7 3 -- result of block-expression lines 2 to 8 1 -- result of line 9
Any variable names used in a block-expression which have not been created at a higher scope level are implicitly declared as local in the present scope. This can result in unexpected execution results for scripts that are not set up correctly.
FOR EXAMPLE, CONSIDER THE FOLLOWING SCRIPT:
(b=bend();addmodifier Objs b) -- create bend modifier and apply to objects b.angle=10
The first time you execute this script, the following error message is generated:
-- Unknown property: "angle" in undefined
If you execute the script again, it works properly. The reason is there are actually two variables with the name
b
- one is local to the block-expression and contains the bend modifier value, and the other is global and has the valueundefined
. During the second execution, globalb
exists, so the block-expression does not implicitly declare a new variableb
, rather it uses the global variableb
.
When writing scripts, it is also a good programming practice to explicitly declare as local all variables unless you really want them to be global variables. There are several reasons for this practice:
All the variable names used in the block or function will be together, which makes it easier to find the variable names and helps prevent using incorrect names in the script.
If more than one script is executing at the same time (for example, you are running a scripted utility and have a scripted controller in the scene) and both scripts use the same global variable name, the two scripts can interfere with one another. This can cause apparently random failures of one or both scripts.
You are sure you are using the correct variable in the event that a variable with the same name has already been declared at a higher scope, and you will not inadvertently change the higher-level variable's value.
Values of variables explicitly defined as local are displayed in error call stack trace-backs.
In 3ds Max R2.5, if a 3ds Max class, a MAXScript method, or a MAXScript read-only global variable has the same name as an implicitly declared local variable, a MAXScript runtime error will be generated if an assignment to that variable name occurs. Since the variable name already existed with a global scope, an implicitly declared local variable was not created. Since one or more new 3ds Max classes will be created by any third party plug-ins the user has loaded, you were not be able to tell ahead of time if a variable name was a global variable name. In 3ds Max 4 and higher, if the MAXScript compiler detects that the first reference to such a variable is an assignment, it will implicitly declare a local variable, rather than leaving it as a reference to a read-only system global.
FOR EXAMPLE,
executing the following expression in 3ds Max R2.5 would result in a value of
Box
, sinceBox
is a 3ds Max object class.In 3ds Max 4 and higher, MAXScript detects that the first use of variable
box
is an assignment and creates an implicitly declared local variablebox
.The result of this expression in 3ds Max 4 and higher is the value
undefined
.(if false do box=10;box)