Learn to Code via Tutorials on Repl.it!

← Back to all posts
Javascript platformer - ultimate tutorial
GameDev46 (5)

Getting started

Start off by creating a new html, CSS and JavaScript repl and head over to the index.html file. We will need a canvas on the website page. Start off by adding this code into the index.html file:

<canvas height="800px" width="1800%" id="canvas" ></canvas>

You may have to fiddle around with its width and height as different computers have different sized screens

Starting with JavaScript

Head over to the script.js file. Lets start by creating a few variables:

let velocity = 0;
let velX = 0;
let timer = 0;
let deaths = 0;
let timer2 = 0;

let scrollX = 0;
let scrollY = 0;

Now we need to get the canvas and store it in a variable so we can access its properties:

const ctx = document.getElementById('canvas');
const c = ctx.getContext('2d');

Now create these arrays:

const platforms = [0, 310, 80, 500];
const lava = [0, 700, 2000, 300, 0, 730, 2000, 300, 0, 760, 2000, 300];
const bounce = [70, 304, 20, 7];
var coins = [];
const enemies = [];
var collected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

All of the arrays can be edited (Look at the bottom as how to) apart from the collected!!

Creating the player

Lets create an object to store our player data in...

var player = {
  x: 50,
  y: 300,
  nh: 15,
  nw: 15,
  height: 21,
  width: 21,
  speed: 2,
  jumping: true
},
keys = [];

The keys array is important for recieving player input!!

The Game Loop

We now need to create a simple game loop, there are many ways we can go about this, but here is the one I used...

function draw () {
  document.getElementById('deathcount').innerHTML = deaths;

c.globalAlpha = 1;

timer += 0.0005;
timer2 += 0.0005

drawBacking();
drawBounce();
drawGround();
drawLava();
drawCoin();
drawEnemies();

player.jumping = true;

velocity = velocity + 0.4;
player.y += velocity;

if (player.x >= 1800 - player.width) {
  player.x = 1800 - player.width;
  velX = 0;
}

if (player.x <= -20 + player.width) {
  player.x = -20 + player.width;
  velX = 0;
}

scroll();

detect();

input();

velX = velX * 0.7;
player.x += velX;



requestAnimationFrame(draw);
}

Don't worry about the function errors as we are going to create the needed code for this loop to run properly!

Player input

We now need to move the player when a certain key is pressed, add this underneath the Game Loop:

    function input() {
      // check keys
      if (keys[38] || keys[32]) {
        // up arrow or space
        if (!player.jumping) {
          player.jumping = true;
          velocity = -10;
          jump.play();
        }
      }

      if (keys[39]) { // right arrow
          velX = velX + player.speed; 
      }
      if (keys[37]) {
        // left arrow
        velX = velX - player.speed; 
      }
    }

This code checks to see if certain keys are pressed and moves the player...

The code underneath is very important as it detects all collisions the player has made making the platforms solid:

function detect() {

  let iterations = platforms.length / 4;

  for(var i = 0; i <= iterations; i++) {
    let item = i * 4;
    item -= 1;

    var playerSize = player.width / 2;
    var playerHeight = player.height;

          if (platforms[item - 3] - player.width - 5 + scrollX <= player.x && platforms[item - 3] + platforms[item - 1] + 5 + scrollX >= player.x) {
            if (platforms[item - 2] - playerHeight + scrollY <= player.y && platforms[item - 2] + platforms[item] + scrollY >= player.y) {
              if (player.y - 5 > platforms[item - 2] + scrollY) {
                while (platforms[item - 3] - player.width - 5 + scrollX <= player.x && platforms[item - 3] + platforms[item - 1] + 5 + scrollX >= player.x) {
                  if (velX < 0) {
                    player.x += 1;
                  } else {
                    player.x -= 1;
                  }
                }
                velX *= -1;
              }
            }
      }

  if (platforms[item - 2] - 5 + scrollY <= player.y && platforms[item - 2] + platforms[item] + 9 + scrollY >= player.y) {
    if (platforms[item - 3] - player.width - 5 + scrollX <= player.x && platforms[item - 3] + platforms[item - 1] + 5 + scrollX >= player.x) {

        player.y += velocity * -1;
        velocity = 0;
    }
  }

    if (platforms[item - 2] - playerHeight + scrollY <= player.y && platforms[item - 2] + platforms[item] + scrollY >= player.y) {
      if (platforms[item - 3] - player.width + scrollX <= player.x && platforms[item - 3] + platforms[item - 1] + scrollX >= player.x) {
          player.y = platforms[item - 2] - playerHeight - 1 + scrollY;
          velocity = 0;
          player.jumping = false;
      }
    }
    
  }
}

It is quite a chunky piece of code but I am working on how to make it smaller!!

Drawing the level

You will need this code to draw the level:

function drawGround() {
  c.fillStyle = "white";
  c.fillRect(player.x, player.y, player.nh, player.nw);

    let iterations = platforms.length / 4;

    c.globalAlpha = 1;

  for(var i = 1; i <= iterations; i++) {
    let item = i * 4;
    item -= 1;

    c.fillStyle = "#61a5c2";
    c.fillRect(platforms[item-3] - 6 + scrollX, platforms[item - 2] - 6 + scrollY, platforms[item - 1] + 12, platforms[item] + 12);
  }

      for(var i = 1; i <= iterations; i++) {
    let item = i * 4;
    item -= 1;
      c.fillStyle = "#014f86";
    c.fillRect(platforms[item-3] + scrollX, platforms[item - 2] + scrollY, platforms[item - 1], platforms[item]);
      }
}

This code draws the actual ground the player interacts with, you can easily change it's colour by editing the hex codes!!

The code below is useful as it draws the water beneath the platformer stopping the player from falling infinitely into the void:

function drawLava() {

   let iterations = lava.length / 4;

   c.fillStyle = "#0db39e"

   c.globalAlpha = 0.2;

 for(var i = 1; i <= iterations; i++) {
   let item = i * 4;
   item -= 1;
   
   let wave = 5 * Math.cos((timer + (2 * (i - 1))) * 120);

   c.fillRect(lava[item-3], lava[item - 2] + wave + scrollY, lava[item - 1], lava[item]);

   if(player.y > lava[item - 2] + scrollY) {
     restart();
   }

 }
}

Also this code is useful as it draws the bounce pads...

function drawBounce() {

    let iterations = bounce.length / 4;

    c.globalAlpha = 1;

  for(var i = 1; i <= iterations; i++) {
    let item = i * 4;
    item -= 1;

    c.fillStyle = "#efea5a";
    c.fillRect(bounce[item-3] + scrollX, bounce[item - 2] + scrollY, bounce[item - 1], bounce[item]);

        if(player.y > bounce[item - 2] - 14 + scrollY && player.y < bounce[item - 2] + 14 + scrollY) {
          if (player.x > bounce[item - 3] - player.nw + scrollX && player.x < bounce[item - 3] + 20 + player.nw + scrollX) {
            velocity = -12;
          }
    }
  }
}

The code below is used to render coins...

function drawCoin() {

    c.globalAlpha = 1;

    let iterations = coins.length / 2;
    let got = 0;

    for (var i = 0; i < (collected.length / 2); i++) {
      if ( collected[i + 1] == 1) {
        got++;
      }
    }

    document.getElementById('collected').innerHTML = got;

  for(var i = 1; i <= iterations; i++) {
    let item = i * 2;
    item -= 1;

    let wave = 5 * Math.cos((timer + (2 * (i - 1))) * 120);

    c.fillStyle = "#fee440";
    if (collected[(item + 1) / 2] == 0) {
    c.fillRect(coins[item-1] + scrollX, coins[item] + wave + scrollY, 8, 8);

      if(player.y + player.nh > coins[item] - 4 + scrollY && player.y - player.nh < coins[item] + 4 + scrollY) {
          if (player.x > coins[item - 1] - player.nw + scrollX && player.x < coins[item - 1] + player.nw + scrollX) {
            collected[(item + 1) / 2] = 1;
            coinCollect.pause();
            coinCollect.currentTime = 0;
            coinCollect.play();
          }
      }
    }
  }
}

and this code is used to draw the enemies in the level...

function drawEnemies() {

    c.globalAlpha = 1;

    let iterations = enemies.length / 3;

  for(var i = 1; i <= iterations; i++) {
    let item = i * 3;
    item -= 1;

    c.fillStyle = "#FF6161";
    c.fillRect(enemies[item-1] + scrollX, enemies[item] + scrollY, 15, 15);

      if(player.y + player.nh > enemies[item] - 7.5  + scrollY && player.y - player.nh < enemies[item] + 7.5  + scrollY) {
          if (player.x > enemies[item - 1] - player.nw + scrollX && player.x < enemies[item - 1] + player.nw + scrollX) {
            restart();
          }
      }

      let value = enemies[item - 2]
      let wave = Math.cos((timer2) * value);

        if (wave >= 0) {
          enemies[item - 1]++;
        } else {
          enemies[item - 1]--;
        }
  }
}

and finally this code is used to draw the background effects...

function drawBacking() {
    c.clearRect(0, 0, canvas.width, canvas.height);

    c.globalAlpha = 0.8;

     c.fillStyle = "#ffffff";

        let iterations = stars.length;

      for(var i = 0; i <= iterations; i += 3) {
        var parralax = ( stars[i + 2] * -1 ) + 25;

    c.fillRect(stars[i] + (scrollX / parralax ), stars[i + 1] + (scrollY / parralax), stars[i + 2], stars[i + 2]);
  }

     iterations = backing.length;

    c.globalAlpha = 1;
    c.fillStyle = "#010d23";

  for(var i = 0; i <= iterations; i++) {

    c.fillRect((i * 150) + (scrollX / 10), backing[i + 1] - 200 + (scrollY / 10), 155, 1000);
  }

    c.fillStyle = "white";
  c.fillRect(player.x, player.y, player.nh, player.nw);
}

Making the level scroll smoothly

To make the level scroll it uses a very simple function:

function scroll() {
  let change = (930 - player.x) / 20;

scrollX += change;
if (scrollX > 0) {
  scrollX = 0;
} else {
  player.x += change;
}

change = (400 - player.y) / 50;

scrollY += change;
if (scrollY < 0) {
  scrollY = 0;
} else {
  player.y += change;
}

}

Final lines of code

To finsih it all off you will need this small bit of code to call the drawing function:

window.addEventListener("load",function(){
    draw();
});

and then this restart function that is very important for respawning the player when they die

function restart() {
      player.x = 50;
      player.y = 250;
      velX = 0;
      player.jumping = true;
      deaths++;
      scrollX = 0;
      scrollY = 0;
      deathsound.play();
}

and finally to get the keys the player is pressing add this code right at the bottom...

    document.body.addEventListener("keydown", function(e) {
      keys[e.keyCode] = true;
    });

    document.body.addEventListener("keyup", function(e) {
      keys[e.keyCode] = false;
    });

How to use the arrays

Platforms array

The array called platforms is, you guessed it, the positions and sizes of the platforms! The platforms are equal to 4 numbers in the array, the 1st number is the x position, the 2nd is the y position, the 3rd is the length and the 4th is the height, so when deleting platforms delete all 4 numbers otherwise it will become very weird.

bounce array

This array controls where the bounce pads are and is layed out in the same way as the platforms array. 1st is the x, then the y, then the length and finally the height.

enemies array

The enemies array is done differently than the platforms and bounce array as each enemy is only 3 values! The first is the one you change depending on the platform size that the enemy is on, the higher the number the smaller the amount of platform the enemy covers and the smaller the number the more of the platform the enemy covers!! I suggest fiddling about with this value until it stops just where you want it to! The 2nd value is the x position and the 3rd is the y.

backing array

This array renders the dark blue buildings in the background, this array only needs one value for each building, the distance from the roof it will spawn, the higher the number the closer to the bottom of the game it's roof will be, but the lower the value the closer to the top of the game it's roof will be.

coins array

This draws the coins on the map which you have to collect! It only needs to input values the 1st is the x and the 2nd is the y!! It gets a bit more complicated now as for the win screen to show properly you will need to change a small value in the code!!
So when you are happy with your coin placing go to the timer.js file and head down to line 21 and you should say an if statement

if (coins == 23) {

in the if statement on line 21 replace 23 or whatever number is at the position of 23 with the amount of coins in your game and tada everything is working and the win screen will show when you finish by collecting all the coins

Simple right??

ALL OTHER ARRAYS AND CODE DON'T NEED TO BE EDITED

The end result...

This is my platformer once I had filled up the arrays...
P.S - You will only have the canvas platformer, not any of the death count, coin counter or timer...

Play the result here...
Javascript platformer