Script controllers function in similar way to expression controllers, providing a properties dialog in which a script can be entered that is used to compute the controller value. The primary advantages of script controllers are:
They can use all the features of the MAXScript language including loops, scripted functions, pathnames, etc.
Almost any property of any object in the scene can be used to help compute controller values, including things like mesh vertices, values of properties at arbitrary frame times, and other non-animatable properties that are not accessible in Expression controllers.
Mesh values, long strings and large bitArrays are not recommended for use in variables inside a script controller because they would use system memory and not MAXScript Heap memory and could cause Out Of Memory situations without ever getting garbage collected..
They can use MAXScript global variables to communicate and coordinate with other controllers and scripts in 3ds Max.
When you assign a Script controller to some parameter, a Properties dialog becomes available in Track View through the right-mouse-button menu or the Properties button on the Track View toolbar.
The script controller dialog has been changed to look a lot like the expression controllers' dialog. At the same time, the expression controller has been completely rewritten in 3ds Max 8 to use Parameter Block 2 and both expression and script controllers now use the same code base and provide very similar features. The main difference is the language used to write the expressions.
The new script controller dialog provides the following controls:
Create Variable group of controls:
Name text field - lets you enter and edit the name of user variables.
Create button - used to create a variable. Enter the variable name in the Name field and press the button to add to the list of variables.
Delete button - used to delete a variable. Select an existing variable from the Variables list to get its name into the Name field OR type the name of the variable manually into the Name field, then press the Delete button.
Rename button - used to rename an existing variable. Select an existing variable from the Variables list to get its name into the Name field. Type the new name of the variable into the Name field, then press the Rename button.
Variable Parameters group of controls:
Tick Offset value spinner - this value spinner can be used to specify a time offset in ticks for the current variable. Select a variable from the list and change the Tick Offset value - if the variable is assigned a Track, Controller or Node, (in other words it is not a Constant), when the script expression is evaluated, the variable's value will be taken from the current time plus the Tick Offset.
Variables list - lists all available variables in the controller. You can create any number of user variables as described above. The following pre-defined constant variables are available in every script controller and cannot be deleted or renamed:
F - the current time in frames
NT - the normalized time
S - the current time in seconds
T - the current time in ticks
- lets you assign a
constant value to the selected variable.
The constant value can
be ANY MAXScript value, including integer, float, array, node etc.
You can enter any valid MAXScript expression in the
expression field. Then press
button to evaluate the
expression at the current time. Press OK to assign the
expression result to the variable, or Cancel
to not assign a value. In the following screenshot, the constant
value is an array containing the integer 1, the global constant
value Pi and the position of the scene node Sphere01. The evaluated
expression result contains only constant
values - the integer 1
does not change, the float value of Pi is taken as the second array
element, and the position of the Sphere01 at the current frame (at
the moment of the evaluation) is taken as the third array
Assign Controller - lets you assign a controller to the selected variable. The value of the controller will be taken at the current time plus the variable's Tick Offset.
Assign Track - lets you assign a track to the selected variable.
Assign Node - lets you assign a node to the selected variable. See also Weak References To Nodes in the Expression/Script Controller
Expression text field - used to enter the script expression to calculate the controller value. See the section below on writing controller scripts for details.
Description text field - can be used to provide comments regarding the functionality of the controller.
Load/Save buttons - Load and save scripts to text files.
Evaluate button - Evaluates the script expression. The current time is used unless explicitly stated differently in the code using the at time context or when using controller variables with Tick Offset different than 0.
Close button - Compiles and checks the controller script for errors. If no errors are found, it closes the dialog. If an error is detected, a MAXScript error message is shown first with the description of the problem. After closing the error message, a prompt appears asking whether to revert the expression to the original value of the current track and close the dialog (OK) or to return to editing the expression (Cancel):
The main difference is that there is an "Assign Node" button that allows you to pick a node. What is special about this is that the node is not held as a direct reference of the script controller, rather it is held as in indirect reference. This is done through an intermediate class called NodeTransformMonitor. The only messages that propogate from the referenced node to the scripted controller are node transform messages and node deletion messages. This allows you to place a scripted controller on a sphere's radius track, and then in the scripted controller have a variable pointing at the node holding the sphere without creating a circular dependency. Note that the expression controller also uses the NodeTransformMonitor when you have a variable pointing at a node.
If you click Assign Constant, you can type in any valid MAXScript expression. If the result is a MAXWrapper (like a node, material, modifier, controller etc.), the result is stored as an Object. If the result is a subAnim, it is stored as a Target.
You can assign either a constant value from a MAXScript expression, or a dynamic value if the expression evaluates to a controller.
If you create the variable myVar and use the Assign Constant button to assign the expression
in the Assign Constant dialog, the value of the controller on the current frame will be taken as a constant value and assigned to the variable myVar.
But if you say
this will create an Object value that points at the controller. In the controller's expression code, you can use 'myVar.value' to get the current value. The value will not be stored as a constant, but will be dynamic and change as the current time changes.
Let's say that you have two scripted controllers X and Y, and a node N that has a sphere base object. A variable in X points at N as an Object. A variable in Y points at N as a Node. If you change the sphere's radius, only X is re-evaluated. If you move N, both X and Y are re-evaluated.
If stored as a Node value, the controller is invalidated only if the node's transform changes or the node is deleted. But it does allow you to specify nodes that would create a circular reference if specified as an Object value.
To specify whether to store an Object or a Node, you should use the Assign Track / Assign Node buttons, or the MAXScript methods. When using the Assign Constant expression, a MAXWrapper will always be stored as Object.
If you want, you can just create variables that hold nodes as Object values, and then reference the parameter you want as you would normally in the expression. For example, myNode would point at a sphere node, and your expression would be "myNode.radius". But this is not going to have as good performance as pointing to the sphere's radius track as a Target. In the former, any change to the node (for example, moving the node) will result in a controller re-evaluation. In the latter, only changes to the sphere base object will result in a controller re-evaluation.
Also, if you just specify a Node variable and then access base object parameters on it (for example, its radius), you will get an error if you bring in the node as an XRef. An XRef object doesn't expose the properties of the object it wraps, so there is no 'radius' parameter. Thus an error will be generated when the expression is evaluated. If you point a variable at the sphere's radius track and use that variable instead, the expression will still work when XRef-ed in.
There are multiple important differences.
Let's say you have an object $Sphere01 using a script controller pointing at an object called $Box01.
If you would say
every time the expression is evaluated MXS needs to resolve the node name 'Box01' to a node value, resolve the position property to the position controller, resolve the x_position property to the x_position subAnim, get the controller from the subAnim, and get the MAXScript MAXControl value wrapping the controller.
If you had a Target variable pointing at the controller, all that needs to happen is to get the MAXScript MAXControl value wrapping the controller.
The second difference is that if you rename 'Box01', your scripted controller would stop working. When using a variable, there is no node name dependency!
The third difference is that if you select and clone 'Box01' and the 'Sphere01', your newly cloned scripted controller will continue to point at 'Box01', not the clone of 'Box01'. As a variable, it will point at the cloned controller.
The fourth difference is that if you XRef Scene with 'Box01' and the 'Sphere01', your scripted controller will fail because it cannot resolve 'Box01' since the node is in an xref scene node tree, not the main 3ds Max scene node tree. As a variable, the scipted controller holds a reference to the node, so it doesn't matter where that node is.
If you do Save Selected on 'Sphere01', then 'Box01' will also be saved because it is dependent on the controller. Likewise, if you merge 'Sphere01', 'Box01' should also be merged.
You do not need 'dependsOn' in the scripted controller's expression since 3ds Max 8.
'dependsOn' should not be used in new scripts, and should actually be removed from old ones.
When you have 'dependsOn' in a script controller expression, a new variable will be automatically created (Depends_#) that points at the node as an object. This will cause all changes to the node to result in a script controller re-evaluation.
The automatic creation of the dependson_# variable is a special case. It is created based on the 'dependsOn' command being executed and by definition the arguments to dependsOn are MAXWrapper objects.
This is also needed to ensure that existing script controllers using 'dependsOn' would continue to work correctly without tweaking the controller.