Unity3D Script to Govern Tank Track Animated Motion

A few days ago, I wrote a post about some of the problems I was having with getting my tank’s track animation within Unity for the Super Space Trooper video game I’m developing.  The problem was that when modeling with Blender I had used the curve modifier to govern the motion of the tank treads.  Unity, it seems, doesn’t support that on import, so I have written source code to take care of it.

Instructions For Using the Tank Tread Scripts:

  1. Create a GameObject in Unity and add the TankTracks.js script to it
  2. Add your individual tank treads as children to the GameObject.  Be sure that each tank tread has the same local rotation.  In other words, if a tank tread appears to be rotated by 20 degrees then the mesh actually should be rotatet at 20 degrees in the Instpector.  See the image below for a visual example
  3. Rename all your tank treads with the same name and append a number sequence afterwards, starting at 1 (not 0).  I used “TankTread1″, “TankTread2″ etc.  Do this in the correct order
  4. In the Inspector of the parent GameObject, update the variable field titled “Child Name”.  For example “TankTread”.
  5. You can change the speed in which they move using the Duration variable in the Inspector for the parent GameObject.  You’ll probably want to control that by the parent of the GameObject.  Duration is in seconds.

Example of Step #2:

Unity3D Tank Tread Rotation Script Same Rotation Example

You can see here that not only do the two selected objects appear to have different rotation, they actually do have different rotations within Unity. If their rotations were both set to 0,0,0 then they would appear the same. This will likely need to be done in your 3D modeling program

My source code may not be the prettiest, and I’m fairly sure that the code could be reduced a fair bit.  But at this point I’m satisfied with it for my purposes.

/* Written by Cassius Adams for the Super Space Trooper video game. http://www.SuperSpaceTrooper.com */
var childName : String = "Enter Child Name"; // The name of the children without the #
var duration : float = 1; // variable to control time taken to travel between points
private var nameOfChildScript : String = "TankTreads"; // If you change this, you'll need to change all references to TankTreads in this source code
private var totalTreads : int = 0; // Hold the total number of items in the array
 
private var startTime : float;
private var runOnce : boolean = false;
private var wp : Transform; // Holds the current parent object
 
private var holderArray : Array = new Array(); // Used to create 2D array (1st dimension)
private var startPoint : Vector3;
private var startPoints : int = 0; // 2nd dimension of array, index #0
private var endPoint : Vector3;
private var endPoints : int = 1; // 2nd dimension of array, index #1
private var childObjects : int = 2; // 2nd dimension of array, index #2
private var cachedLastPosition : Vector3;
private var cachedEndPoints : int = 3; // 2nd dimension of array, index #3
private var cachedStartPoints : int = 4; // 2nd dimension of array, index #4
private var startRotation : Quaternion;
private var startRotations : int = 5; // 2nd dimension of array, index #4
private var endRotation : Quaternion;
private var endRotations : int = 6; // 2nd dimension of array, index #6
private var cachedLastRotation : Quaternion;
private var cachedEndRotations : int = 7; // 2nd dimension of array, index #7
private var cachedStartRotations : int = 8; // 2nd dimension of array, index #8
 
function Start() {
	wp = gameObject.transform; // So we can get only details about this parent and its children
	totalTreads = transform.childCount; // Get the length of the array
    startTime = Time.time; // Keep track of time so we can change the duration
 
	/* Creation of the second dimension of the holderArray array
	The names being given (startPoints, endPoints, etc) are to allow
	for easier understanding of	what's being done in the script.  They
	simply contain integers for the array indexes.  The 'totalTreads'
	just tells the array how long it needs to be based on how many child
	objects there are*/
	holderArray[startPoints] = new Array(totalTreads);
	holderArray[endPoints] = new Array(totalTreads);
	holderArray[childObjects] = new Array(totalTreads);
	holderArray[cachedEndPoints] = new Array(totalTreads);
	holderArray[cachedStartPoints] = new Array(totalTreads);
 
	holderArray[startRotations] = new Array(totalTreads);
	holderArray[endRotations] = new Array(totalTreads);
	holderArray[cachedEndRotations] = new Array(totalTreads);
	holderArray[cachedStartRotations] = new Array(totalTreads);
 
	// Assign the movement script to the children
	for (var child : Component in wp) {
		child.gameObject.AddComponent(nameOfChildScript);
	}
 
	for(i=1;i<=totalTreads;i++) {
		var b = i-1; //
	    for (var child : Transform in wp) {
        	if(child.transform.name == childName+i) {
        		// add all the values into the 2nd dimension arrays
        		holderArray[startPoints][b] = child.transform.localPosition;
        		holderArray[startRotations][b] = child.transform.localRotation;
        		holderArray[childObjects][b] = child;
        		holderArray[endPoints][b] = holderArray[childObjects][b].localPosition;
        		holderArray[endRotations][b] = holderArray[childObjects][b].localRotation;
    			holderArray[cachedEndPoints][b] = holderArray[endPoints][b];
    			holderArray[cachedEndRotations][b] = holderArray[endRotations][b];
    			holderArray[cachedStartPoints][b] = holderArray[startPoints][b];
    			holderArray[cachedStartRotations][b] = holderArray[startRotations][b];
        	}
        }
    }
 
    /* Store the value of the current (first) item in the array to the
	temporary 'currentPosition' variable, then remove it from the array */
    currentPosition = holderArray[cachedEndPoints].Shift();
    // Now add that variable the previously removed item to the end of the array
    holderArray[cachedEndPoints].Add(currentPosition);
    // Same for rotations
    currentPosition = holderArray[cachedEndRotations].Shift();
    holderArray[cachedEndRotations].Add(currentPosition);
 
}
 
function Update () {
    var timer = (Time.time - startTime) / duration;
 
    // We have reached the end position (endPoint) so reset the time and update the arrays
    if(timer >= 1){
    	startTime = Time.time;
        UpdateArrayList();
    }
}
 
function UpdateArrayList() {
	for (var child : Transform in wp) {
		for(i=1;i<=totalTreads;i++) {
			var b = i-1;
			if(child.transform.name == childName+i) { // Make sure it's only for the correct child, one at a time
				var other : TankTreads = child.gameObject.GetComponent(nameOfChildScript);
				other.Enable(); // Enable the lerping (movement)
				other.startPoint = holderArray[startPoints][b]; // Assign the starting position to the child
				other.endPoint = holderArray[endPoints][b]; // Assign the ending position to the child
				other.startRotation = holderArray[startRotations][b]; // Assign the starting rotation to the child
				other.endRotation = holderArray[endRotations][b]; // Assign the ending rotation to the child
				other.startTime = startTime; // Update the child with the new startTime
			}
		}
	}
 
	/* Store the value of the current (first) item in the array to the
	temporary 'currentPosition' variable, then remove it from the array */
    currentPosition = holderArray[startPoints].Shift();
    // Now add that variable the previously removed item to the end of the array
    holderArray[startPoints].Add(currentPosition);
    // And again for the rotations
    currentPosition = holderArray[startRotations].Shift();
    holderArray[startRotations].Add(currentPosition);
 
    for(i=0;i<=totalTreads-1;i++) {
    	holderArray[startPoints][i] = holderArray[endPoints][i];
    	holderArray[startRotations][i] = holderArray[endRotations][i];
    	// Check to see if we've reached the position of the object we're moving.  If so, update the start position
		if(holderArray[endPoints][i] == holderArray[cachedEndPoints][i] && runOnce == true) {
			holderArray[startPoints][i] = holderArray[endPoints][i];
			holderArray[startRotations][i] = holderArray[endRotations][i];
	    } else {
	    	runOnce = true;
	    	// set the initial end point to the first item in the array (change to 2nd item - is currently same as transform.position)
	    	holderArray[endPoints][i] = holderArray[cachedEndPoints][i];
	    	holderArray[endRotations][i] = holderArray[cachedEndRotations][i];
		}
	}
 
	/* Store the value of the current (first) item in the array to the
	temporary 'currentPosition' variable, then remove it from the array */
    currentPosition = holderArray[cachedEndPoints].Shift();
    // Now add that variable the previously removed item to the end of the array
    holderArray[cachedEndPoints].Add(currentPosition);
    // And again for the rotations
    currentPosition = holderArray[cachedEndRotations].Shift();
    holderArray[cachedEndRotations].Add(currentPosition);
}

 

/* Written by Cassius Adams for the Super Space Trooper video game. http://www.SuperSpaceTrooper.com */
private var duration : TankTracks;
private var speed : float = 0;
var lerping : boolean = false;
var startPoint : Vector3;
var endPoint : Vector3;
var startRotation : Quaternion;
var endRotation : Quaternion;
var startTime : float = 0;
 
function Start() {
	speed = transform.parent.GetComponent(TankTracks).duration;
}
 
function Update() {
	var i = (Time.time - startTime) / speed;
	if(lerping == true){
		transform.localPosition = Vector3.Lerp(startPoint, endPoint, i); // Move between the starting position and the end position
		transform.localRotation = Quaternion.Lerp(startRotation, endRotation, i); // Move between the starting position and the end position
	}
	if(i >= 1){
		lerping = false;
	}
}
 
function Enable() {
	lerping = true;
}

Potential Problems:

There may be complications when adding a rigidbody to the tank treads.  Same for colliders.  I’m not actually sure as I haven’t tried either.

If you get some use out of this script, or revise it to work better or more efficiently, please post a comment.

Posted in Javascript JS, Super Space Trooper, Unity 3D, Video Game Development
0 comments on “Unity3D Script to Govern Tank Track Animated Motion
2 Pings/Trackbacks for "Unity3D Script to Govern Tank Track Animated Motion"
  1. […] there it is.  At this point I moved onto the scripting aspects of the model which included controlling each track tread individually, adding the box colliders (no mesh colliders here), and then the animations.  Scripts were added […]

  2. […] The second complication was that I had a terrible time with writing the aforementioned tank track script.  The idea seemed so simple, but having very little experience with multi-dimensional arrays I was going a little crazy.  Ok very crazy.  Nonetheless, the script and animated tank tracks are working now.  You can get the source code to do that for your tank from my blog post titled “Unity3D Script to Govern Tank Track Animated Motion“. […]

Leave a Reply

Your email address will not be published. Required fields are marked *

*


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>