Tank Track Scripting

This tutorial was initially developed from one of the first tutorials for XSI on the net. It did not cover the exact scripting side, more of the idea, so I must pay homage to its origonality, but I hope this version helps to rectify some the issues missing.
You can download the basic scene used in this tutorial here:
In this tutorial, we shall be using XSI to complement our modelling and animation tasks. Open the basic.scn in XSI. The tank geometry hierachy is located inside the tank model. The position_control object, when branch selected can control movement of the entire tank. Beneath it, are the tank geometry ( stored under body) , the wheel geometry (stored under wheelsl & wheelsr) , the tracks (stored under ltracks and rtracks) , the track controls ( left_track_rot & right_track_rot) and the two paths for the tracks to follow ( track_path_l and track_path_r).
Thirty nine tracks are already created for each side of the tank – seventy eight tracks in total - making it an extremely tedious task to setup and animate the entire tracks on an individual basis. We shall be using scripting to attach and set expressions on each track, so that the wheels rotate, along with the tracks when the tank is translated as a whole. The basic technique in use is to setup one individual track and use scripting to complete the remaining 38 tracks on each side.
Firstly, select l_track1 under the l_tracks null in the explorer. Use Constrain->Path(Curve) and select track_path_l. This constrains the track to move along the path, and hence the effect of moving along the wheels of the tank. In the tangency tab, active tangency and set the alignment to the z axis, i.e. enter 0 in the x and y alignment box and 1 in the z alignment box. This has the effect of aligning the track so that its tangency is always perpendicular to the track path, allowing the realistic animation of the track along the curve.
As we progress through this tutorial, we shall be collecting the history of our actions so that we can replicate the tasks performed on 1 track to the remaining 77. Open the script editor and copy the 4 new lines of history that we generated:
ApplyCns "Path", "tank_model.l_track1", "tank_model.track_path_l"
SetValue "tank_model.l_track1.kine.pathcns.tangent", True
SetValue "tank_model.l_track1.kine.pathcns.dirx", 0.000
SetValue "tank_model.l_track1.kine.pathcns.dirz", 1.000
Go back to the first tab in the constraint ppg and scroll the path percentage back and forth. This is the percentage of the tracks position along the path. Notice how 0% and 100% are in fact the same positions. We’re going to make the tracks easier to animate by linking them to a large visual indicator – its already in the scene called left_track_rot.
What we would like is one revolution of left_track_rot to represent a journey of 100% of the track through the curve. Lets look at this relationship: we want 360 degrees of motion of the left_track_rot local rotation in x to equal 100% of travel in the path percentage of the track. We need to normalise the rotation down to a value between 0 and 100 – the range of the percentage. If we take the value of the rotation of left_track_rot and divide it by 3.6 ( there are 3.6 times 100 degrees in 360 degrees) we can directly relate the 2 values.
Right click on the path percentage keyframe icon and set expression. Delete anything that is in the editing pane and hit F11. Navigate through the scene and find left_track_rot. Click on it. Hit F12 and select kine. Repeat hitting F12 and select local. and rotx. Now simply add “ /3.6” to the end of the line. Your expression should look something like this:
tank_model.left_track_rot.kine.local.rotx / 3.6
Validate to check for errors and apply this expression. Copy the line from the history that performs this action and paste it into the scripting editor editing pane, after the text that you copied earlier.
The line should look something like(in one single line though):
SetExpr "tank_model.l_track1.kine.pathcns.perc", "tank_model.left_track_rot.kine.local.rotx / 3.6"
We have now successfully fitted out tank with one working track but it needs 38 more on the left side and 39 more on the right side. It should be fairly simple to automate this procedure.
We need something to repeat through the set of commands for each track. We know the amount of tracks that we have to fix, and we know their naming convention so using a simple FOR…NEXT loop seems ideal. Lets place a loop around the entire text – we know we have l_track1 to l_track39 so we want our loop index to travel from 1 to 39 inclusively. Lets also name the index variable of the loop “index” to make things straight forward. We should have something like…
for index = 1 to 39
ApplyCns "Path", "tank_model.l_track1", "tank_model.track_path_l"
SetValue "tank_model.l_track1.kine.pathcns.tangent", True
SetValue "tank_model.l_track1.kine.pathcns.dirx", 0.000
SetValue "tank_model.l_track1.kine.pathcns.dirz", 1.000
SetExpr "tank_model.l_track1.kine.pathcns.perc","tank_model.left_track_rot.kine.local.rotx /3.6"
next
At the moment, you can see that the script will loop from 1 to 39 and place the iteration of the loop into the index variable. As it stands, the script will process the commands on to l_track1 39 times – we want the script to process the command l_track1 to l_track39. We can remove the number “1” reference and concatenate onto the object basic name of “l_track” the index variable. Now as the script progresses we shall be referencing the correct each track individually…
for index = 1 to 39
ApplyCns "Path", "tank_model.l_track"&index, "tank_model.track_path_l"
SetValue "tank_model.l_track"&index&".kine.pathcns.tangent", True
SetValue "tank_model.l_track"&index&".kine.pathcns.dirx", 0.000
SetValue "tank_model.l_track"&index&".kine.pathcns.dirz", 1.000
setexpr "tank_model.l_track"&index&".kine.pathcns.perc","tank_model.left_track_rot.kine.local.rotx / 3.6”
next
Don’t run the script yet – its not finished as there is still a problem to overcome. At the moment the same set of commands is happening to every track in the loop, i.e. they will all be generated in the same place. What we need to do is spread the tracks out over the length of the path – the path percentage value can allow this to happen. We need to offset each track by a set amount so that they are equally spaced around the track. Lets consider this:
We have 100% that we need to spread out over 39 tracks, i.e. an offset of 100 / 39 – quite simple really. This is our offset, but we need to increment this offset for every track. Once again we can use the index variable to help us – if we take the offset and multiply it by the index of the loop, we shall be able to spread the tracks out over the entire length of the curve. How do we implement this? It’s in the line that sets the expression in our script – we can adjust its effect in the script editor:
setexpr "tank_model.l_track"&index&".kine.pathcns.perc","tank_model.left_track_rot.kine.local.rotx / 3.6”
Lets implement our offset feature of “( 100 / 39 ) * index” by concatenating it on to the end of our expression…
setexpr "tank_model.l_track"&index&".kine.pathcns.perc","tank_model.left_track_rot.kine.local.rotx / 3.6 + ” & ((100/39)*index)
You can see that we maintain the old expression but tack an addition operator inside the quotes of the string, and then concatenate the offset expression onto the end. That’s it for the script:
for index = 1 to 39
ApplyCns "Path", "tank_model.l_track"&index, "tank_model.track_path_l"
SetValue "tank_model.l_track"&index&".kine.pathcns.tangent", True
SetValue "tank_model.l_track"&index&".kine.pathcns.dirx", 0.000
SetValue "tank_model.l_track"&index&".kine.pathcns.dirz", 1.000
setexpr "tank_model.l_track"&index&".kine.pathcns.perc","tank_model.left_track_rot.kine.local.rotx / 3.6 + " & (( 100 / 39) * index)
next
Before you run the script however, select the first track that you made the initial commands on and hit constrain->remove all constraints.This prevents any double constraints that could cause errors.
Now run the script.The tracks on the left side should be attached to the curve in a seamless manner.
Save the script and lets slightly alter it to generate the right tracks. Change all the track names from l_track to r_track, track_path_l to track_path_r and left_track_rot to right_track_rot.
It should look like this – we are duplicating the script to repeat the process on the other side – the right side – of the tank with the geometry available. How simple things are when they are named correctly…
for index = 1 to 39
ApplyCns "Path", "tank_model.r_track"&index, "tank_model.track_path_r"
SetValue "tank_model.r_track"&index&".kine.pathcns.tangent", True
SetValue "tank_model.r_track"&index&".kine.pathcns.dirx", 0.000
SetValue "tank_model.r_track"&index&".kine.pathcns.dirz", 1.000
setexpr "tank_model.r_track"&index&".kine.pathcns.perc","tank_model.right_track_rot.kine.local.rotx / 3.6 + " & (( 100 / 39) * index)
next
The final task to do now in this tutorial is to link the position of the tank in the z axis to the rotation of the track control objects. Once again we have to normalise some values – it is always easier to write the relationships down in order to figure out the correct expression. What we need to consider is…
1 revolution of the left/right_track_rot is equal to 1 complete track revolution along the curve…
The curve has a set length - the distance that the tracks traverse in 1 motion along the curve.
We know the distance that the tank can travel in the z distance – we can get its z position value.
Therfore when the tank has moved 1 unit, the track_rot objects need to rotate 1 full circle divided by the length of the curve to avoid the tracks sliding.
How do we get the length of the curves?
Look in the scripting help file – hit F1. Now scroll down through the list of commands and you will soon find a command called GetCurveLength. Perfect!
Click on this command and its help page will appear. Copy the example on the page and paste it into the script editor. We can often use the examples on the help pages to build simple tools.
You can see how this script works – it builds a spiral curve and then reads its curve length. We want our script to print the length of the curves used to control the tracks.
Lets change…
dim length
CreatePrim "Spiral", "NurbsCurve"
GetCurveLength( "Spiral", length )
To…
dim length
GetCurveLength selection(0), length
Logmessage “The length is “ & length
You can see that firstly we removed the line to generate the spiral – we already have the objects to measure in the scene. Secondly we removed the brackets around the command – XSI does not like parenthesis when calling a subroutine such as GetCurveLength – watch out for this glitch in the help files. We also replaced the “Spiral” reference in the subroutine to selection(0). This means our script will now tell us the length of the currently selected object – lets hope the user has a curve selected otherwise we’ll generate an error. We could fix this to check for curves but there seems little point at this moment in time. If we were going to create a script that a lot of people would have to use then such considerations are wise, but this one is very simple and there probably is no need to even keep it after we need it!
Select track_path_l and run the script. We should get something like this in our history pane of the script editor…
'INFO : "The length is 26.1838111903466"
This value is important – write it down ( or read it on this tutorial).
Now that we know the length of the track path curves we can generate a simple expression to obey the rules we discussed earlier.
Select the left_track_rot and hit CTRL and K. This brings up the local kinematics ppg. Right click on the rotx keyframe icon and hit “set expression”. Delete anything in the expression editing pane.
The large blue arrow object in the scene can be used to animate the tank. Its called position_control. We are going to link our expression to this so lets bring it into the expression editor in the same manner as before – hit F11 to select the object and hit F12 to bring in its global.kine.posz.
Remember that 360 degrees of rotation of the left_track_rot is equal to the position_control travelling 26.183… units.
Therefore we need the rotation of the circle to represent the global position in z of position_control multiplied by ( 360 degrees of the circle divided by the distance of the length of the curves controlling the track). I.e.
(Length of curve)26.183… units in z of position_control
Equals…
360 degrees of l_track_rot
Therefore l_track_rot = 26.183… * position_control z position / 360
And hence the actual expression is :
tank_model.position_control.kine.global.posz * ( 360 / 26.183 )
Validate and apply this expression.
Select and copy it into memory ( CTRL C ) and then set an expression on the other roation control – r_track_rot. Paste this expression into editing pane and validate and apply this expression too - they both use the same values and the same object to drive the animation – position_control.
Now branch select position_control and move it in the z axis. Notice how the tracks stick to the floor in a realistic manner.
Animating the tank is now a lot easier. We could enhance this scene by calculating how much each side moves as the tank turns, etc. and have the tank almost driven entirely by expressions. There are more areas that could be driven by the tank – e.g. the inner wheels that directly drive the tracks.This is another tutorial. J