Basic Syntax

Spelling

Maxscript is not case sensitive.

x and X would be considered the same.

MaxScript Statements

MaxScript statements terminate at the end of line. If the expression at the end of the line is not a complete expression it will attempt to continue reading till the end of the next line.

These two statements are the same

3 * 2 + 3
3 * 2 +
3

Try typing above into listener to see what happens.

Explicit statement continuation

You can also explicitly continue a line use backslash \

For example (this is same as above):

3*2 \
+3

Multiple statements on the same line

If you want to have mulitple expressions on the same line, you can separate them with semi-colons ;

3*2+3;4+4

Comments

comments in MaxScript start at double hyphens and lasts till end of line:

3+2 --this part is a comment after the 3+2 expression

C style comments using/* and */ are also supported

Datatypes

MaxScript variables do not have explictly declared types. The way to think about them is that they are like generic references. When you say:

a = 5

you are make a refer to an integer value so a will behave like an integer.

If later you do the following:

a = 5.0

then a will start behaving like a floating point value (think of it as throwing away the reference to the original integer and refering to a new floating point value instead)

Numeric Data types

The numerical rules for maxScript is similar to C/C++.

numbers entered without a decimal are integer and follows integer mathematical rules.

7/2 is 3 (not 3.5)

8/3 is 2 (not 2.667, note the truncation, not rounded up)

if an expression contains both integers and floating point, result is a floating point value

8.0/3 is 2.667

expressions are evaluated according to order of expression rules and propagated in the same manner. In otherwords, follow order of operator rules, results are evaluated one operator at a time.

2.0 + 8/3 is 4.0 (8/3 is 2, add 2.0 to it gives 4.0)

Numeric Operators:

String literals:

string literals are enclosed in double quotes

"this is a string literal"

strings can be concatenated with + operator

"this is " + "added together with + operator"

results in

"this is added together with + operator"

Boolean literals:

true
false
on  -- true
off  -- false

Vector literals

as max deals in 3D, you will see a lot of vectors used in the code. a vector is declared as 3 comma separated values within []. For example, suppose you wanted to place an object at position x=0, y=0, z = 10, you would set the pos parameter to [0,0,10]

[1,2,3]

Reserved Words

if
then
else
about
and
animate
as
at
by
case
catch
collect
continue
coordsys
do
exit
fn
for
from
function
global
if
in
local
macroscript
mapped
max
not
on
or
parameters
persistent
plugin
rcmenu
return rollout
set
struct
throw
to
tool
try
undo
utility
when
where
while
with

Predefined constants/literals:

true/false
on/off
pi
e
red
green
blue
black
yellow
orange
white
grey
brown
x_axis
y_axis
z_axis
ok
undefined
dontcollect
unsupplied

Control Structures

Control structures are used to control the flow of your program. MaxScript has the usual expected control structures like any other language but some of them do not always work as expected.

Furthermore testing out control structures in the listener can be a limited. It is probably better to write a small script file and run it each time instead.

Selection

Selection statments allow you to choose between one or more paths of script execution. In Max, the control statements return a result which can be used for further evaluation (like the ?: operator of C/C++)

if-do


if <evaluated-expression> do <expressions>

The if-do form is more useful in the listener where expressions are evaluated as they are typed. Everything after the do is performed if the evaluated-expression is true

a=0
x=10
if x > 3 do ( a= a+1)

As there is no possibilty of an else with the if-do form of selection, the code typed into the listener which is processed as soon as the statement is complete will immediately evaluate the statement.

if-then

if <evaluated-expression> then <do if true> [else <do if not true>]

if the evaluated expression is true, evaluate statements after then. else is optional, but if included, do the code after else when evaluated expression is false.

In the case where you are typing code into the listener, an if-then statement that does not include the else will not evaluate until a statement follows it.

For example if you type in:

if x > 3 then a=a+1

nothing will happen until you add another statement or an else clause.

case

Case is similar to a switch statement:

case [<evaluated-expression>] of (
<factor>:<expression>
[<factor>:<expression>]
[...]
[default:<expression>]
)

can be an a literal of some sort or it can be an expression also

NOTE: unlike C/C++ switch statments, once a match is found, the expression of the matching factor is evaluated and no other. There no break that is needed to get past the other cases like there is in C/C++

Also notice that the <evaluated-expression> is optional. It is possible to write something like:

case of(
<factor>:<expression>
[<factor>:<expression>]
[...]
[default:<expression>]
)

If this is done, the first factor expression that is true will be evaluated and the rest will be ignored

a=1
b=2
c=3
case of(
(a == 0):"here"
(b == 2): "there"
(c == 3):  "everywhere"
default: "nowhere"
)

Iteration

Iterations allow you to do a set of expressions over and over again. Similar to selection statements, iteration statements also evaluate to a value. In this case it evaluates to the value of the expression of the last iteration.

while


while <expression> do (
<expressions>
)

do-while

do (
   <expressions>
) while <expression>

for

The for loop is a counting loop. In max, other than standard counting loop, it also allows you to work with thing arrays or a set of objects as part of the for loop syntax. In this section we will only talk about for as a counting loop. We will look at the usage of for loops that iterate through collections in the array section of the notes


for <variable> = <expr> to <expr> [by expr>] [while<expr>] [where <expr>] do
(
  <expressions>
)

So, here are some examples of the equivalent C/C++ for loop and max for loop Example 1: simple counting loop:

/* C/C++*/
for(i=1;i<=5;i++){
...
}

/* max*/
for i = 1 to 5 do (
...
)

Example 2: counting loop that counts by 2's

for(i=1;i<=5;i+=2){
...
}

for i = 1 to 5 by 2 (
...
)

Example 3: counting loop with additional termination condition.

for(i=1;i<=5 && x>3;i++){
  ...
}
for i = 1 to 5 while x>3 (
...
)

Note that while it is possible to break out of loops early using exit, doing so has significant performance hits.

Example 4: counting loop where statements are only done when a condition is met (but loop continues regardless)

for(i=1;i<5;i++){
    if(x>3){
        ...
    }
}
for i=1 to 5 where x>3(
...
)

Functions

You can write functions in MaxScript. Like C/C++ functions, MaxScript functions have a name and parameter list. However, it does not have a return type. The function returns whatever the last expression evaluates to in your function body.

You can also explicitly return a value using the return statement. Like C/C++, once a return statement is encountered, statements that follow it in the function are ignored.

Basic syntax:

[mapped] function <functionName> {<parameter list>} = (
  <expressions for function body>
)

[mapped] fn <functionName> <parameter list> = (
  <expressions for function body>
)

The word mapped in front of the function is optional. If it exists, the function can be applied over a collection such as an array or a set of objects. More details on this will be provided in the array section of the notes

Notice first that you can spell function either as "function" or "fn". There is no difference...

functions can be recursive in nature.

The consists of the names of the parameters for the function. There are two ways to define parameter lists.

Parameter passing by Position

This is the method that you are used to for C/C++ functions. The order of the parameters in your declaration determines how values supplied in the function call are assigned. A parameter of this nature is declared by simply declaring the name of every parameter separated by space.

For example, this function divides 2 numbers

fn doDiv first second = (
  first/second
)

/*to call above function, returns 10/2 which is 5 and prints that value*/
a=doDiv 10 2

print a

Note the lack of return statement in the above function. The return is implicitly whatever the expressions in the () evaluate to.

Parameter passing by Keyword

fn doDiv first:  second: = (
  first/second
)

/*note that in keword style, order has no meaning
  so you can put values in whatever order you want*/

a=doDiv second:5 first:10
print a

keyword style parameter lists also allows you to set default values for your parameters

/*default for first is 10, second is 5.  If no values are supplied for any of these keywords the default is used*/
fn doDiv first:10  second:5 = (
  first/second
)

/*note that in keword style, order has no meaning
  so you can put values in whatever order you want*/

a=doDiv
print a
a=doDiv second:3
print a

Mixing position and keyword parameter passing

It is possible to have part of parameter list as positional and part of it as keywords. However, the positional parameters must come first before any of the keyword parameters.

Pass by Reference

As with C, parameter passing is normally pass by value. That is any changes to your parameters do not modify the a variable used by the calling function.

Furthermore, you will also need to indicate you are making a call by reference when you make the function call by prefixing the by reference variable with an &. This is different from how references work in C++ (though sort of similar to C pointers...)

fn passing byValue &byRef = (
    byValue = byValue+1
    byRef = byRef+1
)

a = 1
b = 1
passing a &b
print  a
print  b

Arrays

Arrays is a type of collection in max. One important difference between arrays in C/C++ and arrays in max is that max arrays can have elements of different datatypes within the same array. This is very diffrent from C/C++ in that all elements of an array must be exactly the same type.

Think of arrays as simply an indexed list, where each list item can be anything.

Furthermore arrays are indexed starting 1 and not 0 like C/C++.

Array declaration:

To declare an array use the following notation:

emptyArray = #()

anotherArray = #(2,4,6,8)

thirdArray=#(2,4,6, "hello", "goodbye", red)

Array access

To access any element of an array, use arrayName[index]. For example, the following prints the first element of the array anotherArray:

anotherArray = #(2,4,6,8)
print anotherArray[1]

Function calls and arrays

Like C/C++, arrays passed to functions are pass by reference. That is if you pass an array to a function and the function changes the array, your original array will be modified.

fn checkArrayRef array=(
   for i=1 to array.count do(
       array[i]=array[i]+10
   )
)

anotherArray = #(2,4,6,8)

for i=1 to 4 do(
    print anotherArray[i]
)

checkArrayRef anotherArray

for i=1 to 4 do(
    print anotherArray[i]
)

Mapped functions

A function may be preceded by the keyword mapped. If this keyword is present, it will allow you to apply the function through every element of a collection.

For example consider the following function:

mapped function mprint p = (
   print p
)
mapped function changeToRed co =(
   co.red=255
   co.blue=0
   co.green=0
)

a = #(red,green,blue,yellow)
mprint a
changeToRed a
mprint a

In the above example, the functions are both mapped. Thus, when I call it using an array, it will automatically call the function using every item in the array.

For loops and arrays

When working with a collection like an array, the for loop can go through the entire collection of objects.

For example:

myArray=#("alpha","beta","gamma")
for theString in myArray do(
  print theString
)

You can also use a for loop to collect things into an array using the collect statement. The result of the expression in the loop is added to the array:

myArray=#()
for i = 1 to 10 collect (
    i*2
)

Structs

MaxScript allows you to create structs with functions. Essentially this means you can write object oriented code if you wish to do so. Structs in many ways are similar to how they would work in C++.

Syntax

struct <struct name> (
   <data member1>,
   <data member2>,
   ...
   <last data member>,
   fn <memberfunction1> <memberfunction parameters> = (
        <member function definition>
   ),
   fn <memberfunction2> <memberfunction parameters> = (
        <member function definition>
   ),
   ...
   fn <lastmemberfunction> <memberfunction parameters> = (
        <member function definition>
   )

)

A few syntactic quirks. All members are comma separated. So once you start a struct, everything member you create in that struct is separated by every other member with a comma.

All data members can be initialized using = operator to specify default values. Data member values can be supplied using either their order or their names. When using names, you can use any ordering.

members can be accessed using (.) notation as with C++

struct myObject (
   a = 8,
   b = 3,
   fn doAdd = ( return a+b),
   fn doDiv = (return  a/b)
)

fn myMain = (
  one = myObject()
  two = myObject 6 5
  three= myObject  b:5 a:20
  print (one.doDiv())
  print (two.doDiv())
  print(three.doDiv())

)
myMain()

Starting with max 2010, you are allowed to create public vs private members and their function is similar to C++. in earlier versions of max, all members are public.

Last updated