This tutorial covers the following topics in MAXScript:
PreScript.max
- This is a simple scene with some animation. You don't need to use this...it
is just so you can follow along exactly with my tutorial and compare your
results
LinktoVert.ms
- This is the completed version of the script you are going to write.
LinktoVert-Helper.ms
- This is the same script but with some modifications so you can understand
the different parts of the script. You aren't writing this one but if you
run into problems you should run it. Hit the define buttons to see what
the different parts of the script does what.
Download
all three files here: linko2v.zip
The very first thing you need to know about scripts is they are NOT hard to write. Scripting is very useful and can greatly reduce the amount of time needed to create a complex scene. You don't have to be a programmer. You don't have to be a math wizard. All you need to do is spend a little time looking through the on-line help and at examples.
Now, open up the PreScript.max file and hit play. It shows a simple box moving around and a teapot that is just sitting there. When we are finished with the script you will be able to pick a vertex on the box for the teapot to be linked to. Now to access MAXScript open up the utility panel and click MAXScript. If you don't see it, hit More.
Here is a brief run down on the MAXScript Rollout.
"Open
Listener" opens up the MAXScript listener,
basically it is the command prompt of MAX. You can type in stuff like "The_Cube
= box length:10 width:10 height:10" and it will make a box at (0,0,0) with
those sizes.
"New Script" just opens up a Notepad.exe like thing for writing scripts. Useful hotkeys are "Ctrl + S" to save and "Ctrl + E" to evaluate the script.
"Open Script" opens up a script for editing.
"Run Script" this just runs the script without opening up the editor. When you run a script it either opens up a floater or adds it to the "Utilities" depending on how you wrote the script.
The "Utilities"
selection lists the scripts that you have run or are in your MAXScript
start-up folder. To open that utility's rollout, click on the arrow and
the list opens up. Highlight the script you want and release the mouse
button. It should open up a Rollout below.
Lets think ahead of what we will need to write in our script. We will need to select the vertex, and the object that will be linked to the vertex. Once we select the objects we should link them. No problem, that is only three buttons. Now to actually move the teapot to the vertex we will use a Scriptable Controller. A Scriptable Controller is just like a float controller or a bezier controller but the main difference is you can have access to all of MAXScript’s functionality. It is MUCH more powerful than the expression controller. To keep this script pretty simple it will just get a vertex numberthat the user will select ahead of time and allow the user to pick a child object. To select the vertex the user can apply a meshselect modifier or similar and select the vertex.
Click on New Script and lets begin to write. (There is a list of all the functions I used at the bottom of the tutorial)
First thing we need to write is:
Now that we created the Floater we need to add a rollout to it.
Now is a good time to see your progress. Hit Ctrl + E. This evaluates your script so far. You should see this pop up.

All you see is your floater. But you defined a rollout too, but it isn't there. That is because one useful feature of the floater is you can add new rollouts to it when needed. Add the following line to the VERY END of your script. It should even be past ") -- Close Rollout":

Now that you have the rollout defined it is time to put some stuff in it. I like to keep my Rollout's neat and semi-organized, that way people besides yourself will feel more comfortable with your script. One way to keep it clean is with the "group". Group is a Rollout Control, it controls the rollout's layout. The first thing we want in our floater is a way to select the vertex we want to be the parent so we will title this group "Pick Vert". Type the following in inside the Vert_Linker Rollout parenthesis.
Lets add some labels before we make the button in this group. Type:
Since we would like to know what vertex we selected we will add the following line in.
Lets to a quick catch up. You now should have the following:
Rollout Vert_Linker "Vertex Linker" (
group "Pick Vert" (
Label
getvert1L "Select a vertex to be parent"
Label
getvert2L "then hit 'Get Selected Vert' "
Button
GetVert_Button "Get Selected Vert"
Label
vertnumL "Vertex Number: (none selected)"
) -- Close Group
) -- Close Rollout
addRollout Vert_Linker MainFloater

Lets add the next part, the selection of the child object (or the object you are linking to the vertex). Type the following after the " ) -- Close Group" since this is a new group:
Now it is time to add the link button and respective group.
Time for another Recap. You should now have the following script.
Rollout Vert_Linker "Vertex Linker" (
group "Pick Vert" (
Label
getvert1L "Select a vertex to be parent"
Label
getvert2L "then hit 'Get Selected Vert' "
Button
GetVert_Button "Get Selected Vert"
Label
vertnumL "Vertex Number: (none selected)"
) -- Close Group
group "Pick Child" (
Label
childL "Select Child Object"
PickButton
GetChild_PB "Get Child"
Label
childnameL "Child Object: (none)"
) -- Close group
group "Link ME Plz!" (
Button LinkME_Button "Link ME!" enabled:off
) -- CLose group
) -- Close Rollout
addRollout Vert_Linker MainFloater

Now it is time to add some actions to those buttons. First lets do the getvert_button. Type
parentOBJ
= $
vertnum
= (getvertselection parentOBJ)[1]
vertnumL.text
= "Vertex Number: " + (vertnum as string)
VertPicked
= true
if
ChildPicked == true do LinkME_Button.enabled = on
) -- Close on GetVert
getvertselection takes one parameter (a mesh), in this case it is our parent object or the box. The getvertselection function gives an array (fancy name for a list) back of all the selected vertices. Since we are only going to select one vertex the array will only have one number in it. This is where the "[1]" comes in. An array in MAXScript looks like this #(1,3,69,13). To access the individual numbers in this array we use the following syntax. Myarray[1] This will output 1, the first number in the array. Or Myarray[2] will output 3 or the second number in the array. Since the getvertselection gives us the vertex in an array we have to extract that vertex number from the array. By extracting the integer we can deal with a number (or integer) instead of a list of numbers. This is why the [1] is at the end of it. It outputs the first and only number in the array (our vertex) as an integer and saves it as the variable vertnum.

The
next line changes the label below the Get Vert button. In order to change
the what the text of the label says we change the value of "vertnumL.text"
If you remember, vertnumL was our label we made that said "Vertex
Number: (none selected)" Well we want to change
this to "Vertex Number: (our vertex number)"
So this is how we do it. vertnumL.text = "Vertex
Number: " + (vertnum as string). The vertnumL.text
stores the string (what is says) well we want to keep the "Vertex
Number: " part. But we want to add the number
of our vertex. So we add it to the string by using the "+"
sign. The only problem is the variable vertnum (our vertex number) is stored
as a interger but the value of vertnumL.text has to be a string. This is
why the "as string"
is there. It converts the integer to a string so it can be appended onto
"Vertex Number: "
Back to the block of script we wrote. The next two lines...
VertPicked
= true
if
ChildPicked == true do LinkME_Button.enabled = on
are needed to activate our LinkME_Button
we made. We had made the LinkME_Button inactive when we created it.
if is a conditional statement. If this then that. The "==" doesn't assign anything, it is used to test. We are testing to see if the Child has already been picked. If it has then the LinkME_Button will become enabled. If it hasn't then it remains locked.
The button should now work when you use it. Remember, you HAVE to select a vertex before you push the get vert button. Because the Button gets what ever is currently selected. If a vertex is not selected and you push the button the script will crash and burn. Onto the next button.
To get the "Get Child" button to work, type the following,
Child_Obj
= obj
childnameL.text
= "Child Object: " + Child_Obj.name
ChildPicked
= true
if
VertPicked == true do LinkME_button.enabled = on
) --close on getchild
Now for the final button. Type:
Child_Obj.position.controller
= position_script()
Child_Obj.position.controller.script
= "getvert parentOBJ vertnum"
) --close on LinkMe
The last line of text assigns the "mini-script" to the child_obj's position script controller. The function getvert takes two parameters. The first is the object with the vertex. The second is the vertex number. The function returns the position of that vertex. This position is what the scriptable controller uses.
SO....this is what you should have now.
Rollout Vert_Linker "Vertex Linker" (
group "Pick Vert" (
Label
getvert1L "Select a vertex to be parent"
Label
getvert2L "then hit 'Get Selected Vert'"
Button
GetVert_Button "Get Selected Vert"
Label
vertnumL "Vertex Number: (none selected)"
) -- close group
group "Pick Child" (
Label
childL "Select Child Object"
PickButton
GetChild_PB "Get Child"
Label
childnameL "Child Object: (none)"
) -- close group
group "Link ME Plz!" (
Button LinkME_Button "Link ME!" enabled:off
) --close group
-----------------------------------------------------------------------------
on
GetVert_Button pressed do (
parentOBJ
= $
vertnum
= (getvertselection parentOBJ)[1]
vertnumL.text
= "Vertex Number: " + (vertnum as string)
VertPicked
= true
if
ChildPicked == true do LinkME_Button.enabled = on
)
-- close on getvert
-----------------------------------------------------------------------------
on
GetChild_PB picked obj do (
Child_Obj
= obj
childnameL.text
= "Child Object: " + Child_Obj.name
ChildPicked
= true
if
VertPicked == true do LinkME_button.enabled = on
)
--close on getchild
-----------------------------------------------------------------------------
on
LinkME_button pressed do (
Child_Obj.position.controller
= position_script()
Child_Obj.position.controller.script
= "getvert parentOBJ vertnum"
) --close on linkme
) -- Close Rollout
addRollout Vert_Linker MainFloater -- Add Rollout to the main floater
Download the script from here: LinktoVert.ms (shift+click to save).
==========================================================================
Used
Functions list:
These
are all built in functions that I used.
NewRolloutFloater
"Name of Floater" width height | Creates a
floater
Rollout
Name_of_Rollout "Title of Rollout" ( ........... )
| Creates a rollout
addRollout
Name_of_Rollout Name_of_floater | adds a rollout
to a floater
group
Group_Name "title of group" | makes a group
with that title
label
Label_Name "text" | Puts text onto the rollout
if
This do That | tests something and if it works
do that
getvertselection
object | gets the vertex number of the currently
selected object/vertex as an array
getvert
object vertex_number | returns the position
of that vertex number of that object (x,y,z)
Further Reading and Explanations:
Although
the MAXScript help file is very resourceful it is lackinging in useful
examples.
So
to see why I did certain things search for the following things and read
up.
For
information on the proper placement and structure of you script:
For
example, why I defined the utility rollout first, then the buttons, then
the "on this do that" section read this in the MAXScript Reference:
Working
with MAX > Utility Rollouts > Visibility of Locals, Functions, Structs
and UI Items in Rollout Code
For
information about controllers and access to them:
Working
with MAX > Controllers and Keys > Controller Creation and Assignment.