Tutorial: How to make a top-down shooter in JavaScript

Lesson 3) Creating a game loop

Let's go for a pseudo-random walk...

As noted previously, to create the illusion of smooth movement in our game we're going to clear and re-draw our canvas quicker than the eye is able to detect. There's a really handy context method for that, called clearRect.
c.clearRect(0, 0, WIDTH, HEIGHT);
clearRect erases from the canvas anything that's within its bounds. So here we've created a clearRect starting at coordinates 0,0 (the upper-left corner of the canvas as you may recall), that is the same width and height as the canvas, using the variables we defined previously.

We're going to create a loop. In every cycle of this loop, our program will clear the canvas, update the positions of all our game elements, and then draw them again in their new positions. This will happen many times every second. There's a few ways of creating such a loop, but we're going to use setInterval, because it lets us choose the speed of the looping:
setInterval(mainDraw, 20);
This command repeats a set of code - in this case a function called mainDraw - at an interval that you specify - in this case every 20 milliseconds, which is 50 frames per second. There's another function, clearInterval() which breaks the program out of the loop. We won't need it here, but it has good potential for games, for example you could set up different loops for the title screen, menu screens, the pause menu, the game itself, and then use setInterval/clearInverval to move between these as needed.

Anyway, for now let's just have one loop. We're calling it mainDraw:
function mainDraw() {

} // mainDraw
Our program will run anything we put in mainDraw, every 20 seconds. The "// mainDraw" bit is just a comment so you know what that closing bracket is actually closing - this is very handy when your functions start getting longer.

So let's draw a little box, which could be a placeholder for our player's sprite, and then clear the canvas every loop:
c.clearRect(0, 0, WIDTH, HEIGHT);
c.fillStyle="red";
c.strokeStyle="blue";
c.beginPath();
c.rect(395,295,10,10);
c.lineWidth=1;
c.stroke();
c.fill();


There's our hero!

Now we just need to update the player's x and y coordinates from within the mainDraw loop, and he'll move around the screen. For now we'll make our four-sided hero move randomly, so that we can get the hang of this looping and clearing business. Later we'll use key-press detectors, and move the character when we detect input from the player.

To code our random movement, we'll replace the x and y coordinates of the box (presently x=395 and y=295) with two variables, player1x and player1y:

c.clearRect(0, 0, WIDTH, HEIGHT);
c.fillStyle="red";
c.strokeStyle="blue";
c.rect(player1x, player1y, 10,10);
c.lineWidth=1;
c.stroke();
c.fill();
As always we need to define these variables with a "var" command before we can use them:
var player1x = 395;
var player1y = 295;
We've got x and y variables for the player, which puts our boxy protagonist in the middle of the canvas. By the way, why 395 and 295, why not 400 and 300? Because the x and y coordinates are for the top-left corner of the rectangle, so to perfectly center him we need to deduct half his width from his x coordinate and half his height from his y coordinate.

Then we'll put the below code in the loop, which adds a random number between -1 and 1 to the player's present x and y coordinates (Math.random returns a number between 0 and 1).
player1x += (Math.random() * 2) - 1;
player1y += (Math.random() * 2) - 1;
We'll also put a little statement to check whether the player has reached the edge of the screen, and if so, regenerate these changer variables and put him back in the middle. Note here that || means "OR," so if ANY of the conditions specified are true, the program will run the code between the brackets. If NONE are true, it will ignore it.
if (player1x < 0 || player1x > WIDTH || player1y < 0 || player1y > HEIGHT) {
player1x = 395;
player1y = 295;
player1xChanger += (Math.random() * 2) - 1;
player1yChanger += (Math.random() * 2) - 1;
}
And here is the result:


Well, it's not going to be a best-seller on Steam, I grant you, but we have a moving character on the canvas, and a functioning game loop, and that's a start.

Choose your path wisely

As an experiment, try removing the clearRect line and see what happens. You'll see a blue smudge appearing. This is because rect basically "stamps" our character onto the canvas at the coordinates we specify. Because the blue border is 1 pixel thick, and because we moved the player less than 1 px per loop, we get a trail of blue where the player used to be. Generally you'll want to clear the canvas to get rid of these trails, but there are some situations you might want to keep them, maybe tyre skid marks in a driving game, or smoke trails behind a rocket.

Put clearRect back, remove the beginPath line, and run the program again. Now you'll see a red smudge with a blue border around it. Why is this? It's all to do with this "path" thing. You can think of a path as a way to combine several shapes/lines into one shape. Therefore we need to beginPath in every cycle of the loop, which closes the previous path. If we don't use beginPath, we're adding to the shape with every loop, building a strange and wonderful pattern with a blue border around it.

If you want to challenge yourself, can you adapt the code so that our quadrilateral character moves in ONE direction until it reaches a border, and then moves back to the center and picks another random direction to go in?

Go to lesson 4 - Getting started with objects