# Move 2d tank with independently controlled treads

I have made a tank object that knows its position, speed, rotation, and a double value ranging from 1.0 to -1.0 for each tread. I want it to move base on those values. For example, if the value for the left tread is 1.0 and the value for the right tread is 0.5 the tank should follow a clockwise circular path. However, the closest I have come is when the left tread turns forward (positive) and/or the right tread turns backward (negative), the tank spins CCW and moves down the screen, while the reverse is true if the left tread turns backward and/or the right tread turns forward.

This is my code for moving the tank, where lts is the left tread speed and rts is the right tread speed:

rotation += speed * (rts / 12);
rotation -= speed * (lts / 12);
location.x += speed * (Math.cos(rts / 12) * 14.5);
location.x -= speed * (Math.cos(lts / 12) * 14.5);
location.y -= speed * (Math.sin(rts / 12) * 14.5);
location.y += speed * (Math.sin(lts / 12) * 14.5);

• Curious how you've hooked up the input to this. How does a player affect one tread vs. another?
– Jon
Apr 3, 2015 at 13:13
• In the prototype, Q and A control the left tread (forward and reverse, respectively) and W and S control the right tread. However, I'm working on having a simple AI control the treads. Apr 5, 2015 at 18:49

The equations of motion for a differential drive machine are as follows, where x, y, θ, and l are the 2D position, orientation, and drive shaft length (distance between wheels) respectively:   While a tank is not quite an ideal differential drive machine, this should be a sufficient approximation.

Translating this into code using a simple Euler integration:

// "speed" must be expressed in units per second
float velocityR = rts * speed;
float velocityL = lts * speed;

// "width" must be expressed in units
rotation += (velocityR - velocityL) / width * dt;
location.x += 0.5 * (velocityR + velocityL) * Math.cos(rotation) * dt;
location.y -= 0.5 * (velocityR + velocityL) * Math.sin(rotation) * dt;'


To make this work without a variable time step, you can keep your current speed value and remove the multiplication by dt (delta time). However, I strongly suggest that you move to a time step that is refresh-rate-agnostic.

• +1 but, since it is arbitrary, 'width' should probably be rotationFactor, turnFactor, etc. Modifying the 'width' of the tank to vary the turn speed sounds funny; i.e. at open-throttle tanks cannot turn as fast, but are just as wide. (Considering rts/lts as a constant "desired" velocity)
– Jon
Apr 3, 2015 at 13:10
• Good call, I wasn't thinking about that. Apr 3, 2015 at 17:31
• I had to change it so that location.y was being incremented rather than decremented, but then it was perfect! Apr 3, 2015 at 21:42