JavaScript

drawing space

The first thing that I want to do is to specify what size the game will be, I will do this by adding a width and height property to the canvas tag:

<canvas width="1024" height="768"></canvas>

In Asteroids space is black. To make the canvas black I need to access the canvas, so I can make it black. I will do this by adding an id to the canvas tag, which I will call gameCanvas:

<canvas id="gameCanvas" width="1024" height="768"></canvas>

Next, I will use a method called getElementById to access that id and modify the document. I will then assign that to a variable, so I can edit it further:

let canv = document.getElementById('gameCanvas');

The canvas can work with 2D and 3D ( known as contexts). This game is 2D, so I will need to specify that. I will use the canv variable to do this and add it all to another variable so I can add further edits to it:

let ctx = canv.getContext("2d");

The canvas has a fillStyle property that can be used to specify a colour, gradient or pattern. I will assign black to it. attached to the ctx variable:

ctx.fillStyle = 'black';

The game now has a canvas with a width of 1024 and a height of 768, filled with black:

drawing space asteroids JavaScript
drawing space

This is good but I would like to improve the user experience. Having a fixed width and height means that not everyone will have the same experience, since people have different screen sizes. An improvement would be to set the game to a size that is dependent on the individual browser window. The way that I will achieve this is by using two properties: innerWidth and innerHeight. These properties give the size of the browser window, in pixels:

canv.width = innerWidth;
canv.height = innerHeight;

ship object

JavaScript has a Math object that allows you to perform mathematical tasks on numbers. This object provides a number of properties, one of which is Math.PI. Math.PI gives pi to a precision of 15 decimal places.

drawing a ship

To begin with, I will represent the ship as a triangle. The canvas API has a number of properties that can be used to draw shapes. I will use strokeStyle, lineWidth, beginPath, moveTo, lineTo, closePath and stroke.

strokeStyle

Specifies a colour, pattern or gradient to use for the stroke.

lineWidth

Sets or returns the current line width.

beginPath

Begins a path or resets the current path.

moveTo

Moves the path to the specified point in the canvas.

lineTo

Adds a new point and creates a line to that point from the last specified point in the canvas

closePath

Adds a straight line from the current point back to the starting point.

stroke

Draws the defined path created by the moveTo and lineTo properties.

trigonometry – sine and cosine

Trigonometry is a branch of mathematics that studies the relationships between the lengths of the sides and the angles of triangles. In trigonometry we label the sides of a triangle as opposite, adjacent and hypotenuse. The hypotenuse is the longest side, the opposite is the side that is opposite to the angle of interest and the remaining side is the adjacent.

In trigonometry we have trigonometric ratios, which are ratios between the edges of right-angled triangles. For this game, there are two ratios that I want to use: sine and cosine.

sine

sine = opposite / hypotenuse

cosine

cosine = adjacent / hypotenuse

Trigonometry is fundamental to game development and if this is an area of interest, I urge you to make sure that you understand it. Games are full of triangles and solving many problems in games involves using trigonometry! If you have a problem in a game that can be expressed as a right-angled triangle, you can solve it using trigonometry!

I can use trigonometry to draw the ship. By starting with the x coordinate of the ship, I can draw each side by using trigonometry.

JavaScript has a Math object, which contains two methods of interest: Math.sin and Math.cos

Using the canvas API properties and trigonometry, I can draw the ship:

ctx.strokeStyle = "white";
ctx.lineWidth = SHIP_SIZE / 20;
ctx.beginPath();

// nose of the ship
ctx.moveTo(
ship.x +  4 / 3 * ship.r * Math.cos(ship.a),
ship.y - 4 / 3 * ship.r * Math.sin(ship.a)
);

// rear left of ship
ctx.lineTo(
ship.x - ship.r * (2 / 3 * Math.cos(ship.a) + Math.sin(ship.a)),
ship.y + ship.r * (2 / 3 * Math.sin(ship.a) - Math.cos(ship.a))
);

//rear right of ship
ctx.lineTo(
ship.x - ship.r * (2 / 3 * Math.cos(ship.a) - Math.sin(ship.a)),
ship.y + ship.r * (2 / 3 * Math.sin(ship.a) + Math.cos(ship.a))
);
ctx.closePath();
ctx.stroke();
drawing a ship space JavaScript
drawing a ship

If any key is pressed down, I want to put it into a function. We will then do the same for when the key is up.

Define all of the attributes and capabilities of the ship. We will do this with a class, which is a blueprint to create the ship. We need to add all of the things that we want our ship to do. Check for visibility. When the game starts we want the ship to be visible. Monitor x and y coordinates of the ship. I want the ship to start in the centre of the screen. Is the ship moving forward? When a game starts the ship will not be moving. Set a speed. Define a velocity for x and y, for how fast the ship moves across the screen. Define a rotation speed. Define the radius of the ship. Define the angle of the ship. Stroke the ship, not fill.

game loop

Games and movies utilise the fact that above 10 – 16 frames per second (FPS) humans do not see the individual frames but instead perceive them as motion. Therefore, to create this illusion of motion, I need to make my game have an FPS of above 16 FPS. A lot of games have a typical frame rate of 30 – 60 FPS, with some games going even higher! I will start with 30 FPS and adjust it if I need to. There will be multiple times that I need to include this in my code, so I will add a constant that I can call:

constant FPS = 30;

The way that I will make the game run is by creating a game loop. A game loop defines what runs and how frequently. To make the game loop, I will use a method named setInterval(). This method accepts multiple parameters, the two that I will use are a function and delay. When setInterval() is invoked, the specified function will run every time the delay has passed.

setInterval(function, delay);

The delay is in milliseconds and I want the game to run at 30 FPS. Therefore I need to set the delay to 1000 / 30:

setInterval(update, 1000 / FPS);

moving the ship

To enable the ship to move, the first thing that I need to do is to detect that the correct keys have been pressed. I will achieve this by using event handlers. There are different events that can be monitored, the two that I will use are keydown and keyup. I need to set up an event handler for each of these:

document.addEventListener("keydown", keyDown);
document.addEventListener("keyup", keyUp);

There are three movements that I want to give the ship: rotate left, thrust and rotate right. There will be a keydown for when each of these triggers an event and a keyup to stop the action.

I want to use the arrow keys to rotate and apply thrust to the ship. Every key has a code (keycode) and using these allows programs to respond to them being pressed:

required keycodes arrows JavaScript
required keycodes

To rotate the ship I will create a constant, TURN_SPEED. I will set this to be 360, since the ship needs to be able to move up to 360°:

TURN_SPEED = 360;

radians, not degrees

However, I don’t want to use degrees. I want to use radians. When working with angles, most people are familiar with degrees, but there are other ways of expressing angles – one of these is radians. Learning about radians is useful and will be required to understand the why of certain things that I will do with the ship. Radians are actually more useful, as degrees are arbitrary and radians account for how much something has moved!

To convert from degrees to radians you use this formula:

radians = angle in degrees / 180 * pi

0 degrees is right, 90 is up

functionkeyDown(ev) {
switch(ev.keyCode) {
case 37:
ship.rot = TURN_SPEED / 180 * Math.PI / FPS;
break;
case 38:
ship.thrusting = TRUE;
break;
case 39:
ship.rot = - TURN_SPEED /180 * Math.PI / FPS;
}
}

How this will work is there will be a method, addEventListener(), that will look for a keyup and keydown. When they are detected, a function will return. The function will look at which key was pressed and respond accordingly. Within the function, I will write a switch statement to check for each key.

friction

In space there is less friction than on Earth. It isn’t zero and therefore I want to account for this. When the spaceship slows down I will apply a small amount of friction. Engineers call this friction coefficient. It does not have units (this is known as a scalar quantity) and most objects are between 0 and 1. The closer it is to 0, the lower the friction. A typical rocket has a friction coefficient of 0.75, which is what I will use. I will create a constant to allow me to account for this in my program:

const FRICTION = 0.75;

object wrapping

In asteroids, all objects wrap around the screen. This means that if an object goes past the top, it will appear at the bottom and vice versa. The same applies to the sides. There are four scenarios that I need to check, each with an appropriate action:

If the x coordinate of the ship is less than 0, then it is leaving the left side of the screen. The ship needs to be moved to the right of the screen.

If the x coordinate of the ship is greater than the width of the screen, then it is leaving the right side of the screen. The ship needs to be moved to the left of the screen.

If the y coordinate of the ship is less than 0. then it is leaving the top of the screen. The ship needs to be moved to the bottom of the screen.

If the y coordinate of the ship is greater than the height of the screen, then it is leaving the bottom of the screen. The ship needs to be moved to the top of the screen.

I will implement all of this by using if statements:

if (ship.x < 0) {
    ship.x = canv.width;
} else if (ship.x > canv.width) {
    ship.x = 0;
}

if (ship.y < 0) {
    ship.y = canv.height;
} else if (ship.y > canv.height) {
    ship.y = 0;
}

However, this is not quite right! These two if statements will move the ship but not quite at the right moments or quite to the correct positions! The reason is that it is triggering on the x and y coordinate position of the ship but what we want to detect is when the rear of the ship has left the screen! I can account for this by factoring in the radius of the ship:

if (ship.x < 0 - ship.r) {
    ship.x = canv.width + ship.r;
} else if (ship.x > canv.width + ship.r) {
    ship.x = 0 - ship.r;
}

if (ship.y < 0 - ship.r) {
    ship.y = canv.height + ship.r;
} else if (ship.y > canv.height + ship.r) {
    ship.y = 0 - ship.r;
}
implementing object wrapping for the spaceship