Midterm Project: Tanks

In-game display

This game is inspired by Tanks, a game which user(s) can control tanks to destroy enemy tanks. To play this game, each player needs to apply basic geometry and trigonometry to target and hit enemy tanks.

My version of Tanks has similar mechanics with some differences. The game will rotate turns between players, limiting each turns to take no more than 20 seconds. During their turns, players can adjust launch angle, launch power, position, and launch shells to attack opponent(s) using a keyboard. Launched shells will follow a simple physics law and eventually land on either the landscape or an opponent tank, permanently changing the landscape or damaging the opponent tank. While the game is running, there will be a dashboard which indicate which player is playing, how much HP (life) is remaining, what angle the tank is aiming, how strong the launching power will be, and how much time left before the turn ends. The match/game ends when there is one or zero (there can be a tie!) players on the map with some health.


On browser that blocks canvas-based fingerprinting, the game may not work as intended.
 

When I started working on this project, I tried to implement a lot of features. And later, I realized that I again underestimated the work I need to do to write this game. The below is the list of canceled/partially implemented features of this game due to limitations of reality.

    1. Adjustable number of players
    2. Customizable tank colors
    3. Map generator (users can draw their own map)
    4. More sound effects

However, there are also many features that are implemented successfully.

    1. Interactable instructions page
    2. In-game menu for pause, restart, and exit the game.
    3. Multiplayer (hardcoded 2)
    4. Image-based maps (drawn on mspaint.exe)
    5. Interactable modifications on the map
    6. Tanks and their controllable features
    7. Turns, player indicator (the yellow arrow) and time limits
    8. Functioning dashboard
    9. Physics of launched shells
    10. Some sound effects
    11. Etc.

Among those, I am especially proud of 4 and 5. Below, I will spend some time explaining how they are done.

function preload() {
  for (let i=1;i<=5;i++) {
    append(gameMaps, loadImage("maps/map_"+i+".png"));
  }
  ...
}

First, images of maps needs to be loaded before the game starts. To be used as maps, they need to at least satisfy two things. First, the pixel color for air must be similar to [190, 251, 254, 255]. Second, there must be enough non-air pixels for tanks to land on when the game starts. If these two are satisfied, pretty much any image can be used as a playable map. Once the maps are loaded, Tank objects and Shell objects can interact with air and non-air pixels.

function compareColors(a, b) {
  // used to compare colors
  if (!a || !b) {
    return false;
  } // if one of them is null, false
  if (a.length != b.length) {
    return false;
  } // if length differs, false
  for (let i = 0; i < a.length; i++) {
    if (abs(a[i] - b[i]) > 7) {
      return false;
    } // difference may be negligible
  }
  return true;
}

// Partial code from class Shell
...
if (this.y>0&&!compareColors(get(this.x, this.y), c.ac)) {
  this.explode();
}
if (this.y > height) {
  this.explode();
}
...

The above code uses a get() function to check whether a launched shell is in the air or overlapped with non-air pixels (ground, map boundaries, and tanks) and decide if it will explode or not. If it chooses to explode, it will trigger the below code.

append(c.explosions, [this.x, this.y, this.explosionRadius]);

It records where the explosion happened and the size of the explosion. The recorded explosions are later used in the below code so the pixels affected by the explosions are “erased” or filled with air color.

modifyMap() {
  push();
  noStroke();
  fill(this.ac);
  for (let i = 0; i < this.explosions.length; i++) {
    let tmp = this.explosions[i];
    circle(tmp[0], tmp[1], tmp[2]);
  }
  pop();
}

Of course, there were(and still are) a lot of difficulties when writing this game, and below are the major ones.

    • Object overlapping
      • Because the map is modifiable, positions of all objects are relative to their surroundings, and in some cases, tanks can overlap each other and show unintended behavior. There seems no easy fix for this.
    • Continuous sound effect
      • When tried to use sound effects for continuous events (such as tanks moving, aim adjusting, etc.), it results in undesirable sound. This may be solvable by finding a good sound file that has matching start and end tone, but I could not find those. So I removed those sound effects.
    • Uphill/Downhill movement
      • Similar to object overlapping, tanks moving uphill and downhill causes them to stutter.
    • Interaction failure with air and non-air pixels
      • In the early stage of coding this game, tanks could stop in the air or indefinitely fall down into the ground. This is mainly due to pixels being meshed together when pixel density is not 1, but it is fixed by having threshold on color differences.
    • Simultaneous keyboard control
      • There were times when multiple keys were pressed, only one (the latest) one will be recognized, and this key will remain recognized unless all keys are released or a new key is pressed. Fixed this issue by using keyIsDown() in draw() instead of keyPressed().
    • There may be other major issues that I have encountered, but I cannot remember them at this point.

From this project, I learned that even a simple-looking feature can be long and complex to implement. For the later projects,  I will try not to focus on implementing multiple features; I will instead focus on making small features more reliable and smooth.

Leave a Reply