Tank Age: Movement

28 May 2010. comments

I’ve been developing a top-down tank game and I needed a way to accurately represent the movement of a vehicle with independently moving treads. Controls such as left/right/up/down need to be polled from the keyboard, but their specific interaction with eachother and the rate of movement corresponding to a key being continuously held down needs to be representative of throttle/brake inputs. When a tank turns its speed needs to be negatively affected by the nature of treads; To turn a tank slows or reverses one treads which causes the tank to rotate in the direction of the slower tread.

Here’s some of what I came up with:

private void handleThrottleInput() {
  Tank tank = state.getTank();  

  if(input.left()) {
    tank.rotateLeft();
  }
  if(input.right()) {
    tank.rotateRight();
  }
  if(!input.left() && !input.right()) {
    if(input.up()) {
      tank.increaseDirectionalForce();
    }
  }
  if(input.down()) {
    tank.decreaseDirectionalForce();
  }
  if(!input.up() && !input.down()) {
    tank.decayDirectionalForce();
  }
}

You’ll notice in the code above I don’t have ‘else’ blocks. This is so that pressing one key doesn’t exclude other keypresses from counteracting other behaviors. The other thing you see here is a request to decay the directional force of the tank. This is a cheap way to simulate momentum after having lifted off the throttle.

Here is some code for the rotation of the sprite based on the above logic:

public void rotateLeft() {
  if(velocity > 80) {
    angle = clampAngle(angle - Constants.ROTATEANGLEFAST);
  } else {
    angle = clampAngle(angle - Constants.ROTATEANGLESLOW);
  }
}  

public void rotateRight() {
  if(velocity > 80) {
    angle = clampAngle(angle + Constants.ROTATEANGLEFAST);
  } else {
    angle = clampAngle(angle + Constants.ROTATEANGLESLOW);
  }
}  

private double clampAngle(double angle) {
  double result = angle % 360;
  if (result < 0) {
    result = result + 360;
  }
  return result;
}

The clampAngle code makes it easier to reason about calculations instead of having to figure out what something like 9142 degrees ends up being in a 0-360 degree spread.

Here is what the throttle methods look like:

public void increaseDirectionalForce() {
  long millis = System.currentTimeMillis();  
  if((millis - Constants.SPEEDDELAY) < lastDirectionalForceChangeMillis) {
    return;
  }
  if(directionalForce < 10) {
    directionalForce++;  
    lastDirectionalForceChangeMillis = System.currentTimeMillis();  
  }  
}  

public void decreaseDirectionalForce() {
  long millis = System.currentTimeMillis();  
  if((millis - Constants.SPEEDDELAY) < lastDirectionalForceChangeMillis) {
    return;
  }
  if(directionalForce > -10) {
    directionalForce--;  
    lastDirectionalForceChangeMillis = System.currentTimeMillis();  
  }  
}  

public void decayDirectionalForce() {
  long millis = System.currentTimeMillis();  
  if(millis - Constants.SPEEDDELAY < lastDirectionalForceChangeMillis) {
    return;
  }
  if(directionalForce > 0) {
    directionalForce--;  
    lastDirectionalForceChangeMillis = System.currentTimeMillis();  
  } else if(directionalForce < 0) {
    directionalForce++;  
    lastDirectionalForceChangeMillis = System.currentTimeMillis();  
  }  
}  

public void rotateLeft() {
  sprite.rotateLeft();  
  long millis = System.currentTimeMillis();  
  if(millis - Constants.SPEEDDELAY < lastDirectionalForceChangeMillis) {
    return;
  }
  if(directionalForce > 5) {
    directionalForce--;
  }
  lastDirectionalForceChangeMillis = System.currentTimeMillis();  
}  

public void rotateRight() {
  sprite.rotateRight();  

  long millis = System.currentTimeMillis();  
  if(millis - Constants.SPEEDDELAY < lastDirectionalForceChangeMillis) {
    return;
  }

  if(directionalForce > 5) {
    directionalForce--;
  }

  lastDirectionalForceChangeMillis = System.currentTimeMillis();  
}

I had to play with the decay/force values in order to make the tank behavior feel somewhat realistic and ‘heavy’ when responding to input. This is probably a hacky way to simulate the physics involved but its good enough for the simple 2D game.

comments

Tagged: java algorithms game development

2017 Ben Lakey

The words here do not reflect those of my employer.