So here’s a quick problem from CGTalk which is a classic example of how just a simple bit of MaxScript knowledge can help:
Hey whats up guys,
Working on an interesting project where we’re modeling a parking lot environment, but the vehicles are aligned along a curve (not nice straight rows.)
So far we’re using the Spacing Tool to clone and align a car model along a path that follows the contour of the spaces.
But here’s the issue, ultimately we’d like to use a small array of cars (5 cars with 3 colors each) and distribute them “randomly” within the parking spaces.
So imagine the spacing tool solution, except instead of using just one object, its distributing random cars along the path.
Any know of a method, work-around, or perhaps another script that can make this happen?
My worst case solution is to just duplicate a cube (with the approximate scale of the cars) along the paths, and then just click and align the car models manually. However, with hundreds of cars, this could be quite tedious.
Any time and assistance is much appreciated,
So lets break this down into the issues.
- We can get the spacing and alignment using the ‘Spacing Tool’
- Spacing tool only distributes one object
We could write some complicated tool which mimics the spacing tool but lets be efficient (lazy) and use the spacing tool to distribute some dummy objects.
Now we have a list of object helpers we can use to distribute a selection of cars too. In this example I’ve just made some boxes, but you can use any object, it’s important to note for this example it needs to be one whole object, and you’ll need to make sure the pivot is in a logical and consistant place on all the cars.
So once we’ve got a group of cars, we want to define that as a group, we then want to go through every Point helper, and for each Point helper we need to choose a random car, create an instance of that random car, then set it’s position and orientation (Transform, another tutorial…) Lets look at the script… funny enough looks very similar to what I just wrote, not too far off english really.
thecars = selection as array for o in $Point* do ( r = random 1 thecars.count thecar = instance thecars[r] thecar.transform = o.transform )
Quick, simple and it works… not a complex script and might require some tidy up later and maunal tweaking but sometimes writing a maxscript isn’t about making a complete tool, it’s just about writing something to help you with your day and get a repetative task done quicker. Lets break this down then…..
thecars = selection as array
We’re creating a new variable here which is an array (think list) of objects that we currently have selected. We can then refer to this list later on using the index system… thecars will return the first item in the list. We can get the total number of items in this list by asking it’s count. thecars.count which will return 6 if you had 6 objects selected when you run the script.
for o in $Point* do ( )
For loops are a way of asking the code to go through a sequence of task…. you can say
for i = 1 to 5 do print i
and you’ll get
..printed in your Maxscript listener.
T’ve tried to explain for o in blah loops before and it’s complicated to a non-codey person. It confused me for quite a while before I understood it. So here goes…
$Point* – This is a quick and dirty way of getting all the objects that are called $Point01, $Point02, into an array (think list). The reason we have to put a $ symbol before it is to tell max that we’re talking about a node… and by node we mean something in the max viewport we can see, not necessarily an object, but any class of thing in the viewport.
We have therefore a list we can process, we could say….
for i = 1 to $Point*.count do print $Point*[i] which will print the node entry into the maxscript listener, but it’s a bit messy, what is better is if we can just refer to each item by a temporary value.
for o in $Point* do print o
It’s saying for ‘every item x’ in the list do print x, we can call this ‘o whatever we like, it’s a variable we’re declaring just for this loop…. for example…
for cabbage in $Point* do print cabbage
Works exactly the same.
I real-life world example… imagine you have a desk full of objects, you need to clean all the items on your desk, they’re al numbered. Your boss comes over to give you your orders… he could say…
“You have 5 things to clean on your desk,
clean deskitem 1
clean deskitem 2
clean deskitem 3
clean deskitem 4”
Ok that might seem a slightly alien way of talking, but stay with me… or he could say.
“For every item on your desk,
The difference might seem subtle but sometimes it can make the difference between writing loads of code and not a lot of code, similarly, sometimes when you need to know the index of the item you’re working on you need to use the for i = 1 to 10 version. These should become clearer with more tutorials.
Back to the script
r = random 1 thecars.count
This line defines a new variable I’m calling r, that we’ll use in a moment, we’re using the Random() function to get a number between 1 and the number of items in our thecars list.
thecar = instance thecars[r]
Now we’ve got a random number we can create a new variable which refers to the node that the instance function returns, Instance() does much as you expect, instances an object just like you do in max normally. It’s a function that needs an argument passed to it, in this case it wants to know what it needs to instance, so we pass it one item from our list. thecars[r] r is defined as a random number between 1 and thecars.count, at this point r will be defined, it won’t be random again, but as we’re in a loop every time the loop runs r will change value. thecar now returns a node in the scene, so we can reference and control this new node.
thecar.transform = o.transform
We now need a new car to assume the position and orientation of the Point helper. Orientation.. direction…rotation… translation and transform is worth of a massive blog post so won’t got too heavily into this now. Transform is a vector direction, and contains the position and scale, so by assigning the transform we’re setting the position and orientation in one line.