Back in late 2006 I wrote my most ambitious JavaScript/game project so far: TankWar Online, which as you may guess from the name was about tanks, shooting stuff, and it had a real-time online game mode – as far as I know, the first such ever in a JS based game.
Originally the game was made for an older version of Opera 9, and only worked in it. However, I have now modified the game so it should work in most browsers (tested Firefox, Opera 9, 10).
Click here to play cross-browser version of the game. The game should work for other parts than the online game mode, as the server is no longer functional.
Interested in hearing how the game works, such as terrain, physics and the computer AI? Keep reading!
A disclaimer regarding the code
If you want to go look into the code, feel free, but keep in mind I wrote this in late 2006 and the code is not really very good. It probably makes sense after you’re totally familiar with it, but even I who wrote the thing had problems finding the places I needed to modify to make it work once again.
Main features of the game
Let’s look at how some of the main features of the game work.
Terrain
The terrain in TankWar is generated randomly, based on a bunch of parameters. If you go into the map settings from the main menu, you can see and adjust these parameters.
The input fields in the map settings screen are actually Web Forms 2 (now a part of HTML5 forms) elements, which still don’t seem to be supported by any other browser than Opera, even after many years. If you don’t have Opera handy, the main difference between the fields in other browsers and Opera is that in Opera, the fields get little arrows for increasing and decreasing the value.
The terrain generation algorithm itself is pretty simple. It takes the base height value, and then adds a number of points based on the height points value. The points’ heights is randomized between the min height and max height values. Ta-dah, simple terrain generation.
Destruction of terrain
Another feature of the terrain is that it’s desctructible. If you fire a weapon at it, it’ll make a hole. You could even destroy the whole map if you destroyed all the ground.
To understand how the desctruction of the terrain works, we have to look at how the terrain itself is stored and rendered.
The terrain is stored as an array of points, which are converted into a canvas path. The path actually consists of the sky, and not the ground – the sky and ground are rendered by first filling the whole image with the ground color, and then using the terrain path to fill in the sky.
When some of the ground is destroyed, the game creates a circular path, which is then added to the terrain path. This way the destroyed areas of the ground get filled with the sky color as well, making it seem like the ground was destroyed, even though the terrain path itself remains unmodified.
Terrain hit testing
This is also used for testing hits on the ground: as long as a projectile (or a tank) is inside the terrain path, or inside one of the circular explosion paths, the projectile keeps moving. The hit check was performed by an Opera-specific method and a custom polygon hit checking function in the old code, as when I wrote it, there was no reliable way to detect whether a point was inside a path or not. In the new version, the hit is detected with the canvas method isPointInPath
, which should work in all browsers with a modern canvas implementation.
The physics engine
The game features a simple physics engine, which simulates falling tanks and moving projectiles such as cannon shells. It also allows for wind.
It functions relatively simply: each “physics object” – tanks and shells – have some vectors which define their position and velocity. The engine has a constant gravity, and an optional wind (which is kind of like horizontal gravity), which affect objects when they move. The physics engine is not active until someone actually fires their cannon – it has no need to run when you take aim, so the engine was designed so it could be given a set of objects to simulate (tanks and the newly fired shell) and then it could run until all the objects are considered “dead”. Being dead simply means that each object has hit the ground, or for shells, hit a tank.
Weapons
Currently the game has eight weapons. Originally there were less, but I had wanted to add more interesting weapons, so the weapon system was designed to accomodate various kinds of weapons, as can be seen in the current selection.
The weapon system is based on the concept of a cannon and a shell (which is what cannons fire). The cannon acts as a kind of a factory for shells, defining how much damage they make, how large is their blast radius etc.
The system is also wired with some events such as an event for the time when a shell hits the ground. This enables the engine to have weapons like the teleport or the arty drop, which both have a special handler for the hit ground event, which moves the player’s tank and fires three more shells at the location from the sky, respectively.
The bomber weapon is a bit special. The weapon’s behavior is actually not intended – it’s a bug. It probably seems crazily overpowered sometimes, and I never meant it to drop an infinite stream of “bombs”, just five of them, but because of a bug it just turned out like that. I liked the unintended behavior and left it in, so it became a feature instead.
Some of the weapons were inspired by Worms 2, like the arty drop and teleport.
Computer opponent
This was one of the most interesting parts of writing the game. As I wanted to give players the possibility to play the game by themselves, I wanted it to have an AI player.
The game’s “player system” is quite flexible. It had to be, so that implementing a “local” player, a “network” player for online games and an “computer” player would be possible. Simply put, the players each implement a specific set of functions that are called in specific points during the game – the game itself doesn’t care much what the type of the player is, except in some special cases, and the player object itself handles things differently.
The computer AI is not very complex. To not give it a completely unfair advantage (and so that my head wouldn’t hurt too much because of the math involved), I didn’t give it a 100% precise aim – which would have been possible by computing where the shell will land, given the gravity, wind and required power and shooting angle.
Instead, the computer follows this graph (click to see a bigger image)
The diagram is not 100% accurate, but that is the general idea of how the AI works. It does some small additional things, such as figuring if wind has changed.
Currently defunct: Online play
The coolest thing in the game no longer works: The online multiplay mode.
The online multiplayer mode was similar to what you can see in many PC games nowadays. It had a lobby where players could talk with each other, and a list of games which were active and joinable. It was also possible to protect your game with a password and choose a maximum amount of players.
There were some additional hidden features in the online lobby, such as the ability to join a custom room, a bit similar to joining channels on IRC. It also implemented some swearword filtering (schoolkids seemed to enojy abusing the chatroom during school hours) and an admin interface for getting rid of unwanted elements (which was implemented on the server and only worked for me)
The gameplay itself was the same, but in addition to the interface elements seen in a local game, the game showed you a chat box and the list of others in the game so you could talk with them during the game. As with local games, you could have up to eight players in a single game, which was a limitation I had chosen based on the map size – the game could have had 10, 20 or more players, but the map would’ve gotten very crowded.
Online game server
The server for the online play was lost in a fatal hard disk failure in 2008 or so. It was implemented in Python, using a library called Twisted to provide HTTP functionality.
The server received communications from clients with XMLHttpRequests, and sent responses using a Server-Sent DOM Events channel, which each client was required to open. Most of the stuff was quite normal client-server type of things – the clients would get an ID, send messages, receive responses and parse them etc.
There was quite much game related things to do on the server as well. While testing the game, I noticed a worrying inconsistency of where the shells landed on one PC compared to a slower PC – sometimes on slow PCs, the shells would land on completely different locations than on the fast PC. I fixed this by making each network player get synced from the server. Basically it meant that whenever a player’s turn was finished, the player’s game told the server where the shots had landed. The server would then broadcast this to all players in the game, and this way each player’s game was kept in perfect sync.
Sadly I no longer have the code as said, and it would be a rather big undertaking to write a new one – certainly possible, but I don’t have the time for it.
Bottom line
TankWar was, and maybe still is, my greatest widget. WidgetCity was a large and complex project, but TankWar had a lot of special things in it and it was 100% written by me – WidgetCity shared a lot of algorithms with the original Sim City source code available online.
Here is the source code for TankWar, if you want to take a look at it.