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

Lesson 9) Adding in bad guys

Who’s bad?

To make the bad guys, we’ll do exactly what we did for the coins. Push objects to an array and draw them on the screen at random places. Only this time, we’ll have them move towards the player with every game cycle.

Now the player will have to dodge some bad guys while collecting coins!

We’ll start by defining variables, a practice you must surely be accustomed to by now:

var theBadGuys = [];
var spawnX, spawnY;

That’s the array we’ll hold our bad guys in, plus two variables we’ll use in a function which will push a new bad guy to the array:

function pushBadGuy() {
  if (Math.random() < 0.5) {
    spawnX = Math.random() < 0.5 ? -11 : 801;
    spawnY = Math.random() * canvas.height;
  } else {
    spawnX = Math.random() * canvas.width;
    spawnY = Math.random() < 0.5 ? -11 : 601

  theBadGuys.push( {
    x: spawnX, 
    y: spawnY, 
    w: 10, 
    h: 10, 
    speed: Math.ceil(Math.random()* 3

Here I assign random numbers to spawnX and spawnY, which will put the baddies off-screen, so that they don’t spawn unfairly close to the player. The first if statement asks if a random number is less than 0.5. If it is, the bit of code directly below will run. If it isn’t, the bit after the “else” will run.

Then there are further if statements nested inside each of these two pathways, but I’ve used a different way of writing them. The format works like this:

spawnX = expression to evaluate ? value if expression is true : value if expression is false;

In English, this could be translated as “create a random number between 0 and 1. If it’s less than 0.5, set spawnX to -11. If not, set it to 801.”

In other words, this…

spawnX = Math.random() < 0.5 ? -11 : 801;

…is the same as this:

if (Math.random() < 0.5) {
  spawnX = -11;
} else {
  spawnX = 801;

If the main if statement is true (i.e., our random number IS less than 0.5):

  • Because the bad guys will be 10 pixels wide, -11 and 801 will put them just outside the left OR right edges of the canvas, at random. We generate a number between 0 and 600 to then place it somewhere at random along the Y axis.

If the main if statement is false (i.e., our random number IS NOT less than 0.5):

  • Because the bad guys will be 10 pixels high, -11 and 601 will put them just outside the top OR bottom edges of the canvas, at random. We generate a number between 0 and 800 to then place it somewhere at random along the X axis.

The final bit of code should be familiar to you – we just push an object to theBadGuys, give it the x and y coordinates we just generated, plus values for width, height and speed. Notice we also randomly generate the bad guy’s speed. Notice also that this can go up to 3 – so some bad guys will be faster than the player!

Getting the baddies to move

This will push our baddies onto the screen, but they’ll just sit there outside the canvas, doing nothing as the player gratefully collects all the coins. We can’t allow this. Let’s give them some orders:

function badGuysMove(){
  theBadGuys.forEach( function(i, j){
    if (i.x > Player1.x) {i.x -= i.speed;}
    if (i.x < Player1.x) {i.x += i.speed;} if (i.y > Player1.y) {i.y -= i.speed;}
    if (i.y < Player1.y) {i.y += i.speed;}

This function loops through all the bad guys in the array, and checks it’s x and y coordinates. If they are greater than the player’s, then we deduct the bad guy’s speed from his current position. If they are less than the player’s x and y coordinates, we add the bad guy’s speed to his position.

This isn’t a brilliant way to move baddies, because they will only move along 4 axes, much like our player. It is quite possible to make them move in a direct line towards the player with a bit of trig. You’ll learn that when we add bullets and shooting to the game. But one thing at a time.

Draw, vermin!

This is great, but at the moment our baddies are invisible! Let’s make a function to draw the baddies.

function badGuysDraw(){
  theBadGuys.forEach( function(i, j) {
    c.rect(i.x, i.y, i.w, i.h);

I won’t go over this because you’ve already seen it – it’s the same function as drawCoins. I’ve just copied it, renamed it, and changed the colours to make the bad guys the inverse of the player (well, they do say opposites attract).

So if you’ve put all this into your program you can run it aaaaaaand…..nothing will happen! Of course, we need to call the functions from mainDraw — remember to put it inside the if statement checking that the game timer is above zero:

if (Math.random() * 100 < 1 ) {

Notice we’ve put the pushBadGuy function in an if statement giving a 1 in a 100 chance of pushing a bad guy each loop. You can change this as you see fit, or add in further conditions that make bad guys more or less likely to spawn (e.g., maybe in the last 10 seconds they spawn twice as fast).

We also need to empty theBadGuys when we restart the game, and I also added a couple of extra lines to this which put the player back in the middle of the screen again:

Player1.x = 395;
Player1.y = 295;

Let’s give this a go:

Well, we’re getting there, but there’s work to be done. First of all, the bad guys just pile up on the player and follow him everywhere. So we need to do some more work around collision.

We also need to make them hurt the player on contact – at the moment they’re little more than a distraction. We need to give the player a health bar, which depletes as these kamikaze squares crash into him.

Leave a Reply

Your email address will not be published. Required fields are marked *