Friday, January 10, 2014

Tank Battle Field, HTML5 Game #6: First enemy unit

Despite that the Flak 88 was a fearsome German anti-tank gun, it is not its historical reputation that made it through to be the first enemy unit in Tank Battle Field game.

As a matter of fact two reasons make it appealing:
- Easiness of animation
- Cheap artificial intelligence (AI) requirement

The animation

Flak88 animation is rather simple. The canon itself is composed of the base and the gun. The base is symmetric and fixed, while the gun can rotate 360°

It is easy to see a similarity with the tank chassis and its turret.
Notice that the gun orientation is horizontal and to the right (towards the increasing x-axis). This is important in case we don't have to worry about the initial angle. Otherwise we should subtract the initial angle each time the gun rotates.
Here's an example:
Suppose the sprite of the gun is upwards like in this picture.

Since the angular system is like the trigonometric one, this gun makes 90° with the x-axis. We are speaking visually wise. However the code does not know that! What it knows is that everything starts at 0°. So if you tell the gun to rotate 90°, the graphic engine will rotate the above sprite by 90° which makes it look like this.
Sadly enough, this is not our intention because visually speaking it is equivalent to 180°. To solve this we subtract 90° from the rotation angle to obtain the desired visual effect.
In order to avoid all this fuss, we draw (from the start) the gun horizontally and oriented to the right.

It is important to differentiate what your eyes see and what data is supplied to the code.

The artificial intelligence (AI)

Probably Flak88 has the easiest AI to implement in any war game!
Even in real life, the Flak88 mission is to track a tank until it has a good shot, then fires.
Same thing happens in the game. Flak88 calculates the angle that separates its gun from the center of the tank. Then starts closing this gap by rotating the gun until the gap reaches 2° or less. At this moment it fires.




There are times when the center of the tank is hidden behind a building. In such cases the Flak88 searches of any of the tank border points to which it has a clear line of fire, if it finds ones it closes in on it and fires.

Time now for some maths calculations:
The angle between the Flak88 and the tank is given by the calculating the arc-tangent of the slope of the line passing by the tank and the Flak88. The arc-tangent returns an angle within the range [-180, 180]. But on the other hand the angle of the gun is expressed in the range [- 360, 360].
So to be able to compute the difference between the two angles, we convert the gun angle to the range  [-180, 180]. This is done like the following:

if gunAngle > 180  then gunAngle2 = gunAngle  - 360 (ex: if gunAngle =   270° => gunAngle2 = -90°)
if gunAngle < -180 then gunAngle2 = gunAngle + 360 (ex: if gunAngle = -270° => gunAngle2 =   90°)

Now we calculate the deltaAngle which is the difference between the angle of the tank and the Flak88 and the Flak88 gun.

if tankFlakAngle and  gunAngle2 have the same signs (both positive or negative) then
deltaAngle = tankFlakAngle - gunAngle2

However if tankFlakAngle and  gunAngle2 have opposing signs (one is positive and the other is negative) then we have to be careful how to compute deltaAngle .
Suppose tankFlakAngle  is 178° and gunAngle2 is -178°

Computing deltaAngle as tankFlakAngle - gunAngle2 will result in an angle of 356° which is not logical since as you can see on the picture above the real difference is 4°.
We can reach this result by doing the following:
if gunAngle2 < 0  then the gun is moving clockwise so deltaAngle must be negative:
deltaAngle = - ( (180 - tankFlakAngle ) + (180 + gunAngle2 ) )

if gunAngle2 > 0 then the gun is moving anti clockwise so the delta must be positive
deltaAngle =  ( (tankFlakAngle + 180 ) + (180 - gunAngle2 ) )

Knowing the deltaAngle, now we can tell the gun to move to close the gap formed by this deltaAngle.

The demo


That's it for today, as always you can check the latest demo by going to http://tankbattlefield.eu.pn

Sunday, January 5, 2014

Tank Battle Field, HTML5 Game #5: Collision detection

Collision detection is an important feature in every game. Because of it you know where you can go, if you hit the enemy, etc... There are plenty of theories regarding this subject (you can check this blog for a good introduction) but they are beyond the scope of this article. What interests us in this post is how the collision detection is applied in the Tank Battle Field game.

In Tank Battle Field game, collision detection mostly applies in two cases:
- The primary tank (player tank) runs into a building, enemy or any other object on the ground
- Shells hitting the primary tank, the enemy units, building or other objects.

Main tank collision detection

As of this version of the game, the only moving vehicle on the field is the primary tank. Since it can move forward and backward, turns left and right, it is essential to be sure that any of these movements do not hit a solid object that denies the tank from changing position.
(it won't be realistic enough, nor interesting, to see the tank driving through buildings for instance).

So to be able to verify whether the tank hit something or not, we keep an eye on eight predefined points of the tank, and we check whether in the new position of the tank any of these points hit a solid object.
These points (called border points) are defined relatively to the center of the tank. For example the upper left corner has the coordinates x = - TankWidth/2 and y = TankLength/2 and so on for the other points.
Each time the tank moves or turns (rotates) the border points are computed in absolute coordinates taking into consideration the new position (including the rotation angle) that the tank it trying to move to.


Lets take an example. Consider that Xc, Yc are the coordinates of the center of the tank and Xul, Yul those of the upper left corner relatively to the tank center. When the tank moves the coordinates of the upper left corner in the absolute system will be:
AbsXul = Xc + (Xul * cosθ - Yul * sinθ)
AbsYul = Yc - (Xul * sinθ + Yul * cosθ)
where θ is the rotation angle of the tank

The above formula is a combination of a translation and a rotation transformations. (You can read more about transformations in the following wikipedia article "transformation matrix" )

The above has determined the border points of the tank. What remains is to check whether any of these points collided into a building. In other words, if any of these points came into intersection with building (or any other solid object) borders then we consider that a collision took place.

This suggests that we know the borders of these solid objects. In case of sprites, we know that they are always contained in a rectangle. However the rectangle might not accurately model the content of the sprite which might be of any shape. So the containing rectangle is not useful for a large number of cases which does not make it appealing. You can easily verify in the image below that the containing rectangle can quickly make the game unplayable.


In the image above, the tank can't move inside the containing rectangle which make the game unrealistic.
To solve this problem, we use polygons to model non-rectangular shapes such as this castle.
However this comes at a price of CPU power in order to try finding if any border point of the tank has crossed the polygon.
You may like to refer to this wikipedia article to know more about detection of a point inside a polygon.



The good news, is that not every thing requires a polygon, but there are cases where several rectangles might do the job. Telling that a point is inside a rectangle is by far faster than telling if it is inside a polygon. So, it is perfectly reasonable to model some objects using a series of rectangle as depicted in the image below.


In the above image, we modeled the house with two rectangles leaving the outdoors free for the tank to drive through.

Shells hit point

What makes tank games appealing is the ability to fire shells on targets. Determining how and where a shell hit a target is also a collision detection problem.
Mathematically this problem can be reduced to finding the intersection point between two lines.
The scenario where the tank fires on the castle,
can be reduced to the following intersection of two lines.




Here we consider that there are two lines defined as
Line A: y = a*x + b
Line B: y = c*x + d
Finding the hit point comes to find the intersection point of Line A with Line B. We define this intersection point by xi, yi. The solution becomes
xi = (b - d) / (c - a)
It follows that yi = a*xi + b

Certainly enough if the slopes represented by a and c are equal, this means the two lines are parallel and won't be intersecting thus the xi won't be determined since the denominator will be zero.

Mathematically this is valid solution. However this is not necessarily true in the game, because in maths the lines are infinite, so in case they are not parallel they will eventually intersect at some point. But in the game the shell has a limited range and the target's borders are also constrained in length. To be valid the intersection point must necessarily be with the valid range of each line.
Back to our previous example, if we say that the Line A stretches between point A1 and point A2 and Line B goes from point B1 to point B2. So in order for the intersection point given by (xi, yi) to be valid, the following relations should be true.
xi >= A1.x && xi <= A2.x assuming that A1.x <= A2.x
and
xi >= B1.x && xi <= B2.x assuming that B1.x <= B2.x

if the above relations are not true we might be in situation like in the image below, where the intersections happens outside the valid ranges.



Special case

We might think that we are done with the hit point. But still this not true yet!
Actually the equations handling the shell trajectory is based on time and velocity.
They have the following form:
x = Vx * t + x0
y = Vy * t + y0
where Vx and Vy are the components of the shell velocity along the x-axis and the y-axis while the x0, y0 is the point of origin (in other terms the tank).

The coordinates of the shell location are updated on each loop turn. This represents a problem since the update occurs every laps of time. So the graphical representation of the shell is discrete and not continuous.
The image below shows in grey the theoretical (continuous) trajectory of the shell, and in red dots the reel  location of the shell over the time.



As you can see there is no way to verify that the shell hit the house by following the red dots alone. Actually the red dot never happen to appear inside the house borders.
To solve this issue, after each update of the shell position we check for intersection between the line formed by the starting point of the shell (position of the tank when it fired) and the latest shell position and the borders of the house.
It appears that for the first two shell positions there is no intersection. But on the third time we notice two intersections represented by the red crosses (X).
Naturally we chose the intersection that is closer to the tank as the hit point or the point of impact then we trigger the explosion sequence. Eventually we ignore the second intersection point and we don't show the third shell position on the screen.

Performance issue

So far the game does not contain too many objects nor requires too many computations that might affect performance. However, to do the things "right", we do take performance topic into consideration.
When detecting collision, we have to check the tank or shell position against every other solid object on the map. So in order to avoid using costly line intersections or polygons checks we first start by simply finding out what containing  rectangles (surrounding objects) intersect with the rectangle formed by the tank position as first corner and the shell position as diagonally opposed corner (see image below). This way using cheap technique we can gather all solid objects that potentially lie in the way of the shell. Then we start testing using the more costly line intersection method on this reduced set of objects.



On the other hand, when seeking to find if the tank is about to collide with other solid objects, we start by checking for candidate objects by verifying if the tank crossed their containing rectangle, then we run a more accurate test using the point-in-polygon technique to find out if the tank really collided with one of them.


The demo

That's it for today, as always you can check the latest demo by going to http://tankbattlefield.eu.pn

D3GGQP7C7BF3